Managed-Object-Technology (MOT) is a class system in C that is designed to integrate automatically into other software, which of course also includes other programming languages.
The strategic goal of the MOT is not to "bless" the world with a new class system, but to bring all existing "class systems" together under one "cover".
- Bringing together means that a class created in the MOT can be used identically in every supported language and the code required for this is automatically written by the All-Language-Compiler (ALC).
The class system of the Target-Programming-Language (TPL) is not changed, only used.
Together with the connection of the MOT to the Target-Programming-Language (TPL), a unit is created so that a MOT-class can be used across Target-Programming-Language (TPL).
- For example, it is possible to use one and the same MOT-class in Perl and Java with the identical methods and the identical attributes and thus the identical functionality.
Together with theLink and the Remote-Procedure-Call (RPC) feature, the MOT-class can also be used across processes and networks:
- conclusion
- The MOT is a technology that offers the programmer a programming-language-independent class technology.
MOT-Class
The core of the MOT is the class and the class is a C struct with a link between:
- instance-type
- The instance-type represents the attributes of the instance (example: MyClassS → MkBufferS → MkObjectS)
- class-type
- The class-type represents the attributes of the class (example: MyClassST → MkSuperTypeSTT → MkTypeSTT)
The type is a struct and a struc which follows the MOT-format is called a MOT-class.
The type MyClassS
is always related to the class MyClassC
.
- By definition a type and a class are using the same root-name like
MkBufferS
versa MkBufferC
.
- The term class is used in the Target-Programming-Language (TPL) with class support,
- The term class is also used together with instance to distinguish the class-type from the instance-type.
- The term type is used in the Target-Programming-Language (TPL) without class support (such as C)
- The term type is also used to name the struct of the class-type and the instance-type.
- The term instance is used for the requested and initialized memory of type instance-type.
- The term instance is also referred to as class-instance.
- Example: type template for MyClassS with base type MkBufferS
struct MyClassS {
union {
} super;
};
A MOT-class always starts with a union
called super.
- The first item in the
union
is always the MkObjectS instance-base.
- All items in the super-union share the memory.
- The super-union is used for the cast-operation (cast the MOT-class into the base-class) also.
A MOT-class is not just limited to the C programming language, but the technology allows the SAME class to be used as a MOT-class in almost all programming languages: "write once → use everything" .
- To complete the picture, the All-Language-Compiler (ALC) creates all code required to organizes the interaction of the different components and offers the end user a coherent API.
INSTANCE-TYPE
The central core of the MOT is a struct
called instance-type whose structure is predetermined and whose central component is a union
always called super
that maps the class-hierarchy right from the start.
To illustrate the topic, I create the fictitious type MyClassS
which uses the type MkBufferS
as a instance-base.
struct MyClassS {
union {
} super;
};
The instance-type is a composition of one or more instance-bases and instance-attributes.
- The tool CLASS-TOOL define the instance-bases and the programmer the instance-attributes.
All instance-bases are grouped into a union
called super.
- Only the instance-base MkObjectS is required for object-management, all other instance-bases are optional.
- The short-name of the instance-base like obj or buf is fix and defined in CLASS-TOOL
- The short-name is required by managed-object-macros and managed-object-functions.
The instance is a link between the instance-type and the class-type :
- Example MkBufferC : instance == MkBufferS + MkBufferST
- ... ...
#define MkObjInitFromType(typeV,isLocalV) \
MkObjInit2(MK_RT_PTR, NULL, (*typeV).objsig, typeV, 0, isLocalV)
... #define MkObjInit2(rtmkV,rtExtV,sigV,typeV,refCountV,isLocalV) \
(struct MkObjectS) { \
.signature = sigV, \
.refCount = refCountV, \
.self = NULL, \
.type = typeV, \
.env = NULL, \
.selfCreated = false, \
.selfRefCount = 0, \
.objRt = rtmkV, \
.objRtExt = rtExtV, \
.obj_protect = { \
.isLocal = isLocalV, \
.prev = NULL, \
.next = NULL, \
}, \
}
CLASS-TYPE
MkTypeS - class known as typ or type is used as class-base for a Managed-Object-Technology (MOT) type …
In Managed-Object-Technology (MOT) everything is a struct, there is a class-type-struc and a instance-type-struc:
The class-type is derived from the MkTypeS and provide the type-property (static-property) and the type-method (slot).
- The class-type is a singelton and created once at runtime-startup.
- Every runtime get it's own set of class-type's.
The instance-type is derived from the MkObjectS and provide the instance-property and the instance-method.
- The instance-type is created using the constructor.
- Every instance-type has its own instance-type-object.
The instance is full defined by min 4 structs and optional multiple base-structs/type(s) :
- instance-type -> instance-base(s) -> instance-final (MkObjectS)
- class-type -> class-base(s) -> class-final (MkTypeS)
- example: ~~~~~ // instance-type | class-type
// ------------------------------— | ------------------------—
// |
struct MyClassS { | struct MyClassTypeS { // default: MkSuperTypeS
union { | union {
// required 'instance-final' | // always MkObjectS, a 'type' is also an 'object' !! struct MkObjectS { | struct MkObjectS obj; ... (pointer) | // required 'class-final'
MK_TYP type; -> | struct MkTypeS typ; ... | ... } obj; | ... // optional 'instance-base' | // optional 'class-base'
struct MyBaseClassS base; | struct MyBaseTypeS base;
... | ...
} super; | } super;
... | ...
} | }
~~~~~
The following naming-convention exist for the C-API (example: MkBufferC)
-
The struct(s) are defined as:
The
class-type is able to create the
instance.
- The class-final is a type not able to create an instance but used as base-class for the class-type and the class-base
- The class-base is a type and is used to initialize the type-properties and type-methods (called slots).
- slot is a method predefined in the type like constructor.
- A list of all slots are defined in MkTypeS.
- Example MkBufferC : class == MkTypeS -> MkSuperTypeS -> MkObjectST -> MkBufferST
The
instance-type is able to configure the
instance and provide access to the
methods and
slots.
- The instance is a link between the instance-type and the class-type
- Example MkBufferC : instance == MkBufferST + MkBufferS
- The instance-base is a type and is used to provide the base-instance-properties
- Example: The base MyBaseStruct add MyBaseStruct-properties ~~~~~ struct MyBaseStruct { union { struct MkObjectS obj } super; // MyBaseStruct-properties } struct MyInstanceStruct { union { struct MkObjectS obj struct MyBaseStruct bse } super; // MyInstanceStruct-properties } ~~~~~
To use the specific-class-type as argument to a function or as property in a struct the common-class-type of type MkTypeS is used. To cast a specific-class-type into a common-class-type use:
- the path:
type->super.typ
- the shortcut:
MkTYP(type)->XYZ
(pointer) or MkTYP_R(type).XYZ
(reference)
To get the common-class-type from the instance use:
- the path:
instance->super.obj.type
- the shortcut:
MkOBJ(instance)->type
(pointer) or MkOBJ_R(instance).type
(reference)
To get the class-base from the class-type use:
- the path:
instance->super.obj.type->base
- the shortcut:
MkOBJ(instance)->type->base
(pointer) or MkOBJ_R(instance).type->base
(reference)
The predefined class-type is an instance of the default-class-type (MkSuperTypeS) …
The properties and slots of the class-type are predefined by the class-base and may be overwritten …
The following relationship between the three different struct exists:
- The class-type and the class-base have the super-class MkTypeS and the instance not.
- The class-final is a super-class of an class-type.
- The class-type has the 3 attributes MkTypeS::objsig, MkTypeS::objmask and MkTypeS::objsize defined, the class-final not.
- The instance has access to the class-type using the cast (example
MkBufferCT_X(instance)
)
- The class-type has access to the class-base using MkTypeS::base and to the type using MkObjectS::type.
MOT-Wrapper
The MOT-Wrapper is used to connect the MOT-Class (lang=C) to a Target-Programming-Language (TPL) (lang=C++,C#,VB.NET,Java,Python,Ruby,Perl,PHP,Tcl or GO).
Two connection methods are conceivable:
- [level-0] Connection as reference. TPL-instance and MOT-instance are one object
struct Wrapper {
struct MyClassC hdl;
}
level-0 is experimental and only possible with a language like C++.
- The biggest problem here is that when the TPL-instance is HARD deleted (e.g. C++
delete ptr
) from the outside, the MOT instance is also deleted, which is difficult to control in a complex scenario (e.g. in the event processing of an application server) and ultimately creates "disorder" in the Programming-Language-Micro-Kernel (PLMK).
- level-0 is not used.
- [level-1] Connection as pointer. TPL-instance and MOT-instance are two objects
struct Wrapper {
struct MyClassC *hdl;
}
level-1 was implemented and confirmed as a solution through extensive testing.
MOT-Null
The MOT-Null is part of the Managed-Object-Technology (MOT) technology:
- A class-instance differs from a value in that a class-instance is passed via a reference (pointer).
- A class-instance supports methods and attributes that are accessed via the reference.
- A class-instance is created from MOT-Wrapper and has a TPL-instance and a MOT-instance.
- NULL defined via MOT
- In general, the Managed-Object-Technology (MOT) is connected to the Target-Programming-Language (TPL) via the MOT-Wrapper and the MOT-Wrapper connects two instances,
- the TPL-instance and the MOT-instance.
Both instances can become NULL
separately from each other so that the TPL and MOT are separated.
- There is therefore not just one
NULL
but two.
To map a MOT-NULL you need an intact TPL-instance whose MOT-Wrapper-hdl is NULL
.
- NULL required as argument
- Although there is a separation between TPL-instance and MOT-instance, it should still be possible to call a MOT-method with the explicit value
NULL
.
This means that the TPL-instance itself cannot be a NULL
because then it would NO longer be possible to pass a NULL
to the MOT-method.
Example: a C function explicitly allows a NULL
value as an argument:
bool isNull(object *obj) {return obj == NULL;}}
This simple function CANNOT be represented in object notation
NULL->isNull() … // invalid
Now they are two options:
- define a class-method that MAY explicitly receive a NULL-Value as static.
val = (NULL-Value); MyClassC::isNull(val);
The static-method-definition is possible but has the disadvantage that in an explicitly dynamic environment (e.g. the connection to a scripting-language or a call via Remote-Procedure-Call (RPC)) it is difficult to check whether a NULL-Value is actually being passed and thus you must always expect that a NULL-Value will also be given as argument.
- define a MOT-Null-singelton-instance, which is returned if whenever the MOT returns a NULL-instance.
val = (MyClassC::MK_NULL); val->isNull();
The test for NULL-Value passing should be placed INSIDE the method because INSIDE the method the functional logic of the method is known and therefore that is the best place to decide whether a NULL
value is allowed or not.
- Parable assert in C: A common practice when writing C code is to place a
assert
statement right at the beginning of a function to check the plausibility of an argument.
However, if the NULL
check is to be performed INSIDE the method, there is a problem with the definition of the NULL value itself because:
- The expression
NULL->myInstanceMethod(...)
is invalid.
- The invalid expression prevents the NULL check from being called inside the function.
To address this problem, the MOT creates one corresponding MOT-Null singelton instance for each MOT-Wrapper at startup and this MOT-Null is always returned where the underlying C function returns a NULL-Value.
- Example: return the MOT-Null and use this value in a test
MyClass myVal = otherClassInstance->ReturnMyClassInstanceOrNull(...);
if (myVal->isNull()) ...
The MOT-Null provide the following features:
- The MOT-Null is real instance of MOT-Wrapper that can be used to call an instance-method of a class.
- The internal handle of the MOT-Wrapper used to reference the MOT-instance is
NULL
.
TCL
Example: MOT-Null from tcl/MkBufferC_tcl.c
...
#define ClassInit \
\
if (MkBufferCTT == NULL) MkBufferCTT = MkBufferSTT; \
...
\
MK(MkBufferC_MK_NULL) = Tcl_GetObjectName(interp, \
MK(AtomCreate) (
MK_RT_CALL interp, NULL , OT_CLASS,
"MK_NULL" , \
"::tclmkkernel::MkBufferC" , false ) \
); \
Tcl_IncrRefCount(MK(MkBufferC_MK_NULL));
Example: MOT-Null from tcl/LibMkKernel_tcl.h
#define OT_MkBufferC MK(MkBufferC)
...
}
static MkBufferC * MkBufferC_ObjNew(MK_RT_ARGS MK_BUF hdl)
#define MkBufferC_X2obj(x)
PYTHON
Example: MOT-Null from py/MkBufferC_py.c
#undef MkBufferCTT
#define MkBufferCTT MK(MkKernelThreadState).MkBufferCTT
...
#define ClassInit \
\
if (MkBufferCTT == NULL) MkBufferCTT = MkBufferSTT; \
...
\
MkBufferC_MK_NULL = Py_NewRef(MK(AtomCreate) (
MK_RT_CALL OT_CLASS, NULL ,
false));
Example: MOT-Null from py/LibMkKernel_py.h
#define MkBufferC_MK_NULL MK(MkKernelThreadState).MkBufferC_MK_NULL
...
C++
Example: MOT-Null from cc/MkBufferC_inline_cc.hh
inline MK_PTR MkBufferC::MkBufferC_selfCreateTLS(
MK_OBJ obj) {
}
return MkOBJ_R(buf).obj_protect.isLocal ? MkBufferC_selfCreateTLS(obj) : new
MkBufferC(obj);
}
Example: MOT-Null from cc/MkBufferC_cc.hh
...
...
MOT-TOOLS
The Managed-Object-Technology (MOT) uses the following tools to define the classes:
- The CLASS-TOOL to generate the class-code from a configuration-file and include the code in the project.
- The LABEL-TOOL to add a file-specific-header to an existing file which also contains code.
CLASS-TOOL
The class-definition itself is done in the class-tool from the All-Language-Compiler (ALC) :
# "MkBufferS" → the base-class of MyClassS
# "Bux" → short name for the class used in "super" and "cast"
# "public" → the class is "public" and not "hidden"
define class MkBufferS MyClassS {
desc "MyClassS is an exmple used for documentation"
Short Bux
public 1
header MyClassC
header_def MyClassC_def
header_inline class
}
The class-tool write the class-definition direct into to source-code using the BEGIN/END
marker :
- The example from this page was created with:
c_Class.tcl -example
- The classes of theKernel were created with:
c_Class.tcl -mk
BLOCK: ShortDef
BEGIN/END-ShortDef
→ to define the class-handle block
The shortdef is an abbreviation of the class and also defines the meta-type under which the All-Language-Compiler (ALC) processes the class.
__parser__(type=ME_CCC_MyClassC:
"MyClassS is an exmple used for documentation":primary)
typedef struct MyClassS * MY_BUX;
__parser__(type=ME_CCN_MyClassC:
"const - MyClassS is an exmple used for documentation":primary)
typedef const struct MyClassS * MY_BUXN;
typedef struct MyClassS MY_BUXR;
BLOCK: super
BEGIN/END-MyClassS-super
→ to define the class-dependency block
The super-block creates the class hierarchy.
- The base-class have to be a valid MOT-class.
- The base-class MkObjectC is always present and define the object-feature.
- Other base-classes are optional.
- All base-classes in the super-block share the memory so that a common memory block is created which in turn interlock like matryoshka dolls.
Example: The Block: super
of the fictitious class MyClassC with the base class MkBufferC.
The base-class in the super-block is identified by the short name:
- The identifiers obj and buf were defined in the CLASS-TOOL item
Short
.
BLOCK: Definition
BEGIN/END-MyClassS-Definition
→ to define the class block
The definition of a class creates all the functions needed to work with a class, including "signature", "cast" and access to the class-type.
#define MyClassC_SIGNATURE (MkBufferC_SIGNATURE ^ (5u<<6))
#define MyClassC_MASK (((1u<<26)-1)<<6)
#define MyClassC_X2bux(x) (x)
#define MyClassC_X2buf(x) MkBUF(x)
#define MyClassC_X2obj(x) MkOBJ(x)
__parser__(ignore)
typedef struct MyClassS MyClassCR;
__parser__(ignore)
typedef const struct MyClassS MyClassCNR;
#define MyClassC_T ( (struct MkSuperTypeS *) (MyClassC_TT) )
#define MyClassST MyClassC_T
#define MyClassSTT (MkTYP(MyClassST))
#define MyClassC_type MY_BUX
#define MyClassCT_X(instance) ( (struct MkSuperTypeS *) (MkOBJ_R(instance).type) )
#define MyClassCTT_X(instance) (MkOBJ_R(instance).type)
#define MyClassCT_TT(typ) ( (struct MkSuperTypeS *) (typ) )
#define MyClassC_NS MY
#define MyClassCTT MyClassCTT
#define MyClassCT ( (struct MkSuperTypeS *) MyClassCTT )
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"
}
}
#pragma GCC diagnostic pop
#define MyClassC_Check(mng) MyBuxCheck(mng)
META_ATTRIBUTE_SANITIZE
return (MyBuxCheck(mng) ? (MY_BUX)mng : NULL);
}
META_ATTRIBUTE_SANITIZE
return (MyBuxCheck(mng) ? (MY_BUXN)mng : NULL);
}
#define MyBuxRaise(_bux) if (!_MkCheckX(MyClassC,_bux)) { \
MkErrorSetC_1E("'MyClassC' hdl is NULL"); \
goto error ; \
}
#define MyBUX_R(x) (*(x)).super.bux
#define MyBUX(x) (&MyBUX_R(x))
BLOCK: Export
BEGIN/END-MyClassS-Export
→ to define the import/export block
The export is a functionality to convert the pointer to a class into a handle that can then be safely stored in an external software
__parser__push__(doc-group=_ignore_,doc-index=Class,doc-name=Export,
class=MyClassC);
MY_BUX const bux
) {
) {
} MK_ATTR_RT_RUNTIME
#define MyClassHandleResolve_e(netHdl) ({ \
MK_HDL tmpHdl=netHdl; \
MY_BUX tmp; \
if (tmpHdl==0) { \
tmp=NULL; \
} else { \
tmp=MyClassHandleResolve(tmpHdl); \
if (tmp==NULL) { \
MkErrorSetC_1_NULL("ERROR: 'MyClassC' handle is 'NULL'"); \
goto error; \
}; \
}; \
tmp; \
})
__parser__(flags=
new,doc-group=_ignore_,doc-index=Class,doc-name=Misc,
class=MyClassC,null-
return-allow)
}
BLOCK: Instrospection
BEGIN/END-MyClassS-Instrospection
→ to define the introspection block
The Introspection is a functionality to access all instances of a class on a very abstract level.
__parser__push__(doc-name=Introspection,doc-index=Class,
class=MyClassC,no-rpc,null-
return-allow,flags=
new);
MyRtSetup_NULL_RT;
return (MY_BUX)MyClassC_TT->instances;
}
mk_inline MY_BUX MyClassNext(MY_BUX
const bux) {
return (MY_BUX)MyClassC_X2obj(bux)->obj_protect.next;
}
mk_inline MY_BUX MyClassPrev(MY_BUX
const bux) {
return (MY_BUX)MyClassC_X2obj(bux)->obj_protect.prev;
}
LABEL-TOOL
The MOT-label-tool (Nhi1Label) is the second part of the MOT-class definition.
LABEL-TOOL-USAGE
The
Nhi1Label is the oldest tool and a kind of Swiss Army knife in
Programming-Language-Micro-Kernel (PLMK) programming.
- This tool was developed to provide a large number of files with a uniform file header, which is difficult to do by hand in large projects such as PLMK with currently 4811 files.
Over time, the
Nhi1Label has been expanded to include source code that appears in the prefix of a file and is similar for a variety of files.
In principle, the
Nhi1label creates the appropriate header based on the file extension.
- An unknown extension can be mapped to a known extension using mapping within the tool.
The
Nhi1Label is customized using three files (per directory) :
file name | description |
.labelignore | ignores the file with pattern matching, the pattern matching works similarly to the .gitignore file pattern matching. |
.label.EXT | enable the extended header feature for all files with *.EXT . |
.label_inc.tcl | configure the extended header feature. |
In the
source file, the
Nhi1Label tool looks for
text markers to determine where the replacements should take place.
text marker | description |
doxygen header | this is the default text marker and will be applied to all files not mentioned in the .labelignore file. |
LABEL_NO | this text marker disables the extended header feature on a per file basis. |
LABEL_INIT | this text marker starts a pre init section not changed by the extended header feature. |
LABEL_START | this text marker starts the extended header feature. |
LABEL_END | this text marker ends the extended header feature. |
The
default text marker is of course the
file header itself, which is defined depending on the programming language.
- The format of the file header follows the format of the doxygen documentation tool.
- Example: a file from the tclmkkernel package
* @file NHI1/theKernel/tcl/MkCall_tcl.c
* @brief MkCall_tcl.c - 21 Jun 2024 - aotto1968
* @copyright (C) NHI - #1 - Project - Group
* This software has NO permissions to copy,
* please contact AUTHOR for additional information
* @version 7a59dddb6cd1ae3575106e253d5f5d8150e9c628
* @date Fri Jun 21 14:31:41 2024 +0200
* @author aotto1968 <aotto1968@t-online.de>
The
extended header function defines some placeholders that are applied to the data from the
.label.EXT
file :
- Example: placeholders for class MkBufferC
# §BASENAME§ = basename
# §PART#§ = basename split "_"
# §ClassC§ = MkBufferC
# §Root§ = Buffer
# §SHORT§ = BUF
# §Short§ = Buf
# §Prefix§ = MkBuffer
# §Ns§ = Mk
# §NS§ = MK
# §ns§ = ns
# §NsPkg§ = MkKernel
# §nspkg§ = mkkernel
LABEL-TOOL-EXAMPLE
The MOT-class header is create with Nhi1Label.tcl -w FileName.EXT
or just Nhi1Label.tcl -w
for the whole directory.
- Example: The class MkBufferC from the tclmkkernel package.
- In TCL, the MOT-class is connected via the C api. The connection basically creates one file per class, with additional code being used to provide the cross-class connection.
#define META_FILE_NAME "MkBufferC_tcl.c"
#include "LibMkKernel_private_tcl.h"
#define OT_CLASS NS(MkBufferC)
#define OT_CLASS_NAME "MkBufferC"
#define OBJECT2BUF(O) MkAssertCastM(MkBufferC,(Tcl_ObjectGetMetadata(O, &MK(AtomMeta))))
Tcl_Interp *interp = env;
return MK(AtomCreate) (
MK_RT_CALL interp,obj,OT_CLASS,NULL,NULL,
true);
}
Tcl_Object selfO = selfP;
Tcl_Interp *interp = env;
OT_OBJECT_DELETE_HARD(selfO);
}
#define OT_SETUP_hdl_static_constr OT_SETUP_hdl_static
#define OT_SETUP_hdl_static \
int __skip=Tcl_ObjectContextSkippedArgs(objCtx); \
AllRtSetup_NULL; \
__attribute__((unused)) Tcl_Object selfO = Tcl_ObjectContextObject(objCtx); \
__attribute__((unused)) MK_TYP hdl = MkBufferC##_TT;
#define OT_SETUP_hdl SetupHdlFromMetaData_2(BUF,MK_BUF);
#define OT_SETUP_hdl__null_allow SetupHdlFromMetaData__null_allow_2(BUF,MK_BUF);
#define OT_SETUP_hdl_destr SetupHdlFromMetaData__null_allow_2(BUF,MK_BUF);
#define OT_SETUP_hdl_constr \
int __skip=Tcl_ObjectContextSkippedArgs(objCtx); \
AllRtSetup_NULL; \
MK_RT_UNUSED Tcl_Object selfO = Tcl_ObjectContextObject(objCtx); \
MK_BUF hdl = (MK_BUF) &MkERROR;
#if !defined(SetupRtFromHdl_XN)
#define SetupRtFromHdl_XN(hdl) AllRtSetup_XN(hdl)
#define SetupRtFromHdl_X(hdl) AllRtSetup_X(hdl)
#endif
#define ClassInit \
\
if (MkBufferCTT == NULL) MkBufferCTT = MkBufferSTT; \
\
\
if (MkBufferCTT->selfCreate == NS(MkBufferC_selfCreate)) return MK_OK; \
\
\
MkBufferCTT->selfCreate = NS(MkBufferC_selfCreate); \
MkBufferCTT->selfDelete = NS(MkBufferC_selfDelete); \
\
\
Tcl_Object classO = MK(ClassDef)(interp,ns,MkBufferCTT); \
check_NULL(classO) goto error; \
OT_CLASS = Tcl_GetObjectAsClass(classO); \
\
\
static MkThreadLocal Mk_UnknownS NS(sClassUnknown) = {OT_UNKNOWN_CLASS}; \
check_LNG(MK(UnknownSetup) (interp,classO,NS(sOtClassDef),&NS(sClassUnknown))) goto error; \
\
\
static MkThreadLocal Mk_UnknownS NS(sInstanceUnknown) = {OT_UNKNOWN_INSTANCE}; \
check_LNG(MK(UnknownSetup) (interp,classO,NS(sInstanceDef),&NS(sInstanceUnknown))) goto error; \
\
\
MK(MkBufferC_MK_NULL) = Tcl_GetObjectName(interp, \
MK(AtomCreate) (MK_RT_CALL interp, NULL , OT_CLASS, "MK_NULL" , \
"::tclmkkernel::MkBufferC" , false ) \
); \
Tcl_IncrRefCount(MK(MkBufferC_MK_NULL));
#define VER TCL_OO_METHOD_VERSION_CURRENT
MOT-DETAILS
CAST
There are two possible "cast" operations in the MOT :
- A "cast" from a pointer of unknown origin (example: MK_MNG).
- A "cast" from an already known managed object pointer (example: MK_BUS).
In (1) the "cast" is checked using the "signature" and in (2) the "cast" is simply resolved within the "base-class".
Example: "cast" a pointer into a MkBufferS
- "cast" from an unknown pointer:
MK_BUF ret = MkBuf(ptr);
- This "cast" is called an up-cast because the pointer is upgraded (getting more information).
- This "cast" checks the MkObjectS::signature to ensure that ptr is a valid object.
- This "cast" is checked at runtime.
- "cast" from a managed pointer:
MK_BUF ret = MkBUF(ptr);
- This "cast" is called a down-cast because the pointer is downgraded (lose some information).
- This "cast" uses the super-union to just return a pointer that is already available.
- This "cast" is checked at compile time.
- To put it simply:
MkBuf ≠ MkBUF
SIGNATURE
The MOT-class is identified by a signature and a mask.
The signature and the mask are an 32bit unsigned integer value of type MK_SIG.
- The signature and the mask define the MOT-class and the class-hierarchie.
- The signature and the mask are created by the CLASS-TOOL and written into the class-def-source-code.
#define MkBufferC_SIGNATURE (MkObjectC_SIGNATURE ^ (1u<<10))
#define MkBufferC_MASK (((1u<<22)-1)<<10)
The class-type is the storage for the signature and the mask
- The signature is stored in the (MkTypeS::objsig) entry of the MkTypeS class-type.
- The mask is stored in the (MkTypeS::objmask) entry of the MkTypeS class-type.
- The class-type is initialized at runtime-startup. ...
#define MkInstanceTypeInit(cls,bse) MkInstanceTypeInit_4(cls,((RT_REF)._##cls##_T),bse,#cls)
... #define MkInstanceTypeInit_4(_cls,_typ,_bse,_nme) do { \
MkTypeInit_4(_cls,_typ,_bse,_nme); \
(*_cls##_TT).objsig = _cls##_SIGNATURE ; \
(*_cls##_TT).objmask = _cls##_MASK ; \
(*_cls##_TT).objsize = sizeof (_cls##R) ; \
} while (0)
The instance-type is initialized by the class-type
- The instance-signature stored at
instance.super.obj.signature
.
- The
instance.super.obj.signature
is the MkObjectS::signature attribute of the MkObjectS instance-type.
- Every pointer to the instance is also a pointer to the instance.super.obj.signature.
- The instance-signature is initialized from MkTypeS::objsig.
#define MkObjInitFromType(typeV,isLocalV) \
MkObjInit2(MK_RT_PTR, NULL, (*typeV).objsig, typeV, 0, isLocalV)
... #define MkObjInit2(rtmkV,rtExtV,sigV,typeV,refCountV,isLocalV) \
(struct MkObjectS) { \
.signature = sigV, \
.refCount = refCountV, \
.self = NULL, \
.type = typeV, \
.env = NULL, \
.selfCreated = false, \
.selfRefCount = 0, \
.objRt = rtmkV, \
.objRtExt = rtExtV, \
.obj_protect = { \
.isLocal = isLocalV, \
.prev = NULL, \
.next = NULL, \
}, \
}
The signature check is done at runtime with MyClassC_Check(instancePtr)
(example: MkBufferC_Check).
- The test is
true
if the instance belongs to the class MyClassC
or has MyClassC
as base-class.
To check an instance on the instance-type without instance-base use:
switch (obj->signature) {
case LcConfigC_SIGNATURE: ...; break;
case LcSettingC_SIGNATURE: ...; break;
default: ...; break;
}
To check an instance on the instance-type and instance-base use:
if (LcConfigC_Check(obj) {
...
} else if (LcSettingC_Check(obj)) {
...
} else {
...
}