theKernel 10.0 NHI1 - theKernel - theLink - theConfig - theSq3Lite - theCompiler - theBrain - theGuard
c - tcl - cs - py - rb - jv - cc
Loading...
Searching...
No Matches
Managed-Object

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 → MkBufferSMkObjectS)
class-type
The class-type represents the attributes of the class (example: MyClassST → MkSuperTypeSTTMkTypeSTT)

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
// instance-type to represent the data from the class MyClassC …
struct MyClassS {
// [super]
// BEGIN-MyClassS-super - created by 'c_Class.tcl -example' - DO NOT change
union {
struct MkObjectS obj; // instance-base MkObjectS
struct MkBufferS buf; // instance-base MkBufferS
} super;
// END-MyClassS-super - created by 'c_Class.tcl -example' - DO NOT change
// [super]
// BEGIN-MyClassS-native
// END-MyClassS-native
};

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.

// instance-type to represent the data from the class MyClassC …
struct MyClassS {
// [super]
// BEGIN-MyClassS-super - created by 'c_Class.tcl -example' - DO NOT change
union {
struct MkObjectS obj; // instance-base MkObjectS
struct MkBufferS buf; // instance-base MkBufferS
} super;
// END-MyClassS-super - created by 'c_Class.tcl -example' - DO NOT change
// [super]
// BEGIN-MyClassS-native
// END-MyClassS-native
};

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 :


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) :

  1. instance-type -> instance-base(s) -> instance-final (MkObjectS)
  2. class-type -> class-base(s) -> class-final (MkTypeS)
  3. 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)

name definition
MkSuperTypeS super-class-base, the MkTypeS using the super-macro-syntax for all non specific types
MkBufferS instance-type → This is the main-struct to define an instance
MkBufferST instance-type as MkSuperTypeS-class-type
MkBufferSTT instance-type as MkTypeS-class-type (cast from MkBufferST into MkTypeS)
MkBufferC_T class as MkSuperTypeS-class-type, useable in a class-macro as: class##_T
MkBufferC_TT class as MkTypeS-class-type, useable in a class-macro as: class##_TT
MK_BUF class-shortcut for struct MkBufferS *, all shortcut using the XX_YYY syntax (only for public API)
MK_BUFR class-shortcut for const struct MkBufferS *, all const shortcut using the XX_YYYC syntax (only for public API) …
MkBufferCR reference-shortcut for struct MkBufferS, all shortcut using the XX_YYYR syntax (only for public API)
MkBufferCT_X(instance) cast from an instance into the MkSuperTypeS-class-type
MkBufferCTT_X(instance) cast from an instance into the MkTypeS-class-type
MkBufferCT_TT(typ) cast from an MkTypeS-class-type into an MkSuperTypeS-class-type
MkBufferCT class as MkSuperTypeS-class-type for MkBufferC in the Target-Programming-Language (TPL)
MkBufferCTT class as MkTypeS-class-type for MkBufferC in the Target-Programming-Language (TPL)

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:

  1. the path: type->super.typ
  2. the shortcut: MkTYP(type)->XYZ (pointer) or MkTYP_R(type).XYZ (reference)

To get the common-class-type from the instance use:

  1. the path: instance->super.obj.type
  2. the shortcut: MkOBJ(instance)->type (pointer) or MkOBJ_R(instance).type (reference)

To get the class-base from the class-type use:

  1. the path: instance->super.obj.type->base
  2. 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) …

~~~~~ _struct MkRuntimeS { ... struct MkSuperTypeS _MkBufferST; ... } ~~~~~

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:

  1. The class-type and the class-base have the super-class MkTypeS and the instance not.
  2. The class-final is a super-class of an class-type.
  3. The class-type has the 3 attributes MkTypeS::objsig, MkTypeS::objmask and MkTypeS::objsize defined, the class-final not.
  4. The instance has access to the class-type using the cast (example MkBufferCT_X(instance))
  5. 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 {
    // init...
    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 {
    // init...
    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:

  1. define a class-method that MAY explicitly receive a NULL-Value as static.
    val = (NULL-Value); MyClassC::isNull(val); // class-method (static)
    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.
  2. define a MOT-Null-singelton-instance, which is returned if whenever the MOT returns a NULL-instance.
    val = (MyClassC::MK_NULL); val->isNull(); // instance-method
    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(...);
// "myVal" is always of type "MyClassC", if NULL the value is "MyClass::MK_NULL"
if (myVal->isNull()) ...

The MOT-Null provide the following features:

  1. The MOT-Null is real instance of MOT-Wrapper that can be used to call an instance-method of a class.
  2. The internal handle of the MOT-Wrapper used to reference the MOT-instance is NULL.

TCL

Example: MOT-Null from tcl/MkBufferC_tcl.c

// MO class
MkThreadLocal OT_LNG_T MK(MkBufferC_MK_NULL) = NULL;
#define MkBufferCTT
#define MkThreadLocal

...

// initialize the TCL and MO class specific object
#define ClassInit \
/* if not already done, initialize NEW MQ type */ \
if (MkBufferCTT == NULL) MkBufferCTT = MkBufferSTT; \

...

/* define the "NULL" object */ \
MK(MkBufferC_MK_NULL) = Tcl_GetObjectName(interp, \
MK(AtomCreate) (MK_RT_CALL interp, NULL /*obj*/, OT_CLASS, "MK_NULL" /*name*/, \
"::tclmkkernel::MkBufferC" /*ns*/, false /*useSelf*/) \
); \
Tcl_IncrRefCount(MK(MkBufferC_MK_NULL));
#define MK_RT_CALL

Example: MOT-Null from tcl/LibMkKernel_tcl.h

// class: MkBufferC
#define OT_MkBufferC MK(MkBufferC)
MK_TCL_EXTERN_DATA MkThreadLocal OT_LNG_CLASS_T MK(MkBufferC);
MK_TCL_EXTERN_DATA MkThreadLocal OT_LNG_T MK(MkBufferC_MK_NULL);
MkBufferC(MK_BUF hdl)

...

mk_inline OT_LNG_T MK(MkBufferC_ObjNew) (MK_RT_ARGS OT_LNG_ENV_T interp, MkBufferC_type hdl) {
return ( hdl ? MK(AtomObjNew) (MK_RT_CALL interp, MkBufferC_X2obj(hdl)) : MK(MkBufferC_MK_NULL) );
}
static MkBufferC * MkBufferC_ObjNew(MK_RT_ARGS MK_BUF hdl)
#define MkBufferC_type
#define MkBufferC_X2obj(x)
#define mk_inline
#define MK_RT_ARGS

PYTHON

Example: MOT-Null from py/MkBufferC_py.c

// MO class
#undef MkBufferCTT
#define MkBufferCTT MK(MkKernelThreadState).MkBufferCTT
/* MkBufferC_MK_NULL defined in LibMkKernel_py.h */

...

// initialize the PY and MO class specific object
#define ClassInit \
/* if not already done, initialize NEW MQ type */ \
if (MkBufferCTT == NULL) MkBufferCTT = MkBufferSTT; \

...

/* define the "NULL" object */ \
MkBufferC_MK_NULL = Py_NewRef(MK(AtomCreate) (MK_RT_CALL OT_CLASS, NULL /*obj*/, false));

Example: MOT-Null from py/LibMkKernel_py.h

// MK_NULL singelton
#define MkBufferC_MK_NULL MK(MkKernelThreadState).MkBufferC_MK_NULL

...

mk_inline OT_LNG_T MK(MkBufferC_ObjNew) ( MK_RT_ARGS OT_LNG_CLASS_T type, MK_BUF hdl ) {
return hdl ? MK(AtomObjNew)(MK_RT_CALL type,MkBufferC_X2obj(hdl),true) : Py_NewRef(MkBufferC_MK_NULL);
}

C++

Example: MOT-Null from cc/MkBufferC_inline_cc.hh

namespace ccmkkernel {
inline MK_PTR MkBufferC::MkBufferC_selfCreateTLS(MK_OBJ obj) {
return new MkBufferC(obj);
}
MK_PTRB * MK_PTR
inline MK_PTR MkBufferC::MkBufferC_selfCreate (MK_RT_ARGS MK_OBJ obj, MK_PTR const env) {
MK_BUF buf = reinterpret_cast<MK_BUF>(obj);
return MkOBJ_R(buf).obj_protect.isLocal ? MkBufferC_selfCreateTLS(obj) : new MkBufferC(obj);
}
#define MkOBJ_R(x)

Example: MOT-Null from cc/MkBufferC_cc.hh

namespace ccmkkernel {

...

class MkBufferC : public MkObjectC {
MkObjectC()

...

public:
static thread_local MkBufferC MK_NULL_REF;
private:
static MkBufferC* MkBufferC_GetSelf (MK_BUF hdl) {
return (hdl != NULL ? static_cast<MkBufferC*>((*MkBufferC_X2obj(hdl)).self) : &MK_NULL_REF);
}

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.

// BEGIN-ShortDef - created by 'c_Class.tcl -example' - DO NOT change
__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;
__parser__(ignore)
typedef struct MyClassS MY_BUXR;
// MyClassC_Class_C_API
// END-ShortDef - created by 'c_Class.tcl -example' - DO NOT change

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.

  • // BEGIN-MyClassS-super - created by 'c_Class.tcl -example' - DO NOT change
    union {
    struct MkObjectS obj; // instance-base MkObjectS
    struct MkBufferS buf; // instance-base MkBufferS
    } super;
    // END-MyClassS-super - created by 'c_Class.tcl -example' - DO NOT change

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.

// BEGIN-MyClassS-Definition - created by 'c_Class.tcl -example' - DO NOT change
__parser__push__(prefix=Class, doc-group=Define, doc-index=Class);
// Signature --------------------------------------------------------------
#define MyClassC_SIGNATURE (MkBufferC_SIGNATURE ^ (5u<<6))
#define MyClassC_MASK (((1u<<26)-1)<<6)
// CompileTimeCast --------------------------------------------------------------
#define MyClassC_X2bux(x) (x)
#define MyClassC_X2buf(x) MkBUF(x)
#define MyClassC_X2obj(x) MkOBJ(x)
// TypeDef --------------------------------------------------------------
__parser__(ignore) typedef struct MyClassS MyClassCR;
__parser__(ignore) typedef const struct MyClassS MyClassCNR;
__parser__(ignore) MY_EXTERN_DATA MkThreadLocal MK_TYP MyClassC_TT;
#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 )
// TypeCheck --------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"
__parser__(class=MyClassC,static,hide)
mk_inline bool MyBuxCheck (MK_MNGN mng) {
return MkSanitizeCheck(MyClassC,mng);
}
__parser__(class=MyClassC,static,hide)
mk_inline bool MyBuxCheckO (MK_OBJN obj) {
return MkSanitizeCheckO(MyClassC,obj);
}
#pragma GCC diagnostic pop
#define MyClassC_Check(mng) MyBuxCheck(mng)
// RunTimeCast --------------------------------------------------------------
__parser__(class=MyClassC,hide,static)
META_ATTRIBUTE_SANITIZE
mk_inline MY_BUX MyBux(MK_MNG mng) {
return (MyBuxCheck(mng) ? (MY_BUX)mng : NULL);
}
__parser__(class=MyClassC,hide,static)
META_ATTRIBUTE_SANITIZE
mk_inline MY_BUXN MyBuxN(MK_MNGN mng) {
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))
// MyClassC_Class_Define_C_API
// END-MyClassS-Definition - created by 'c_Class.tcl -example' - DO NOT change

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

// BEGIN-MyClassS-Export - created by 'c_Class.tcl -example' - DO NOT change
__parser__push__(doc-group=_ignore_,doc-index=Class,doc-name=Export,class=MyClassC);
mk_inline MK_HDL MK_DECL MyClassHandleGet_RT (
MY_BUX const bux
) {
return MkObjectHandleGet(MkOBJ(bux));
__parser__(flags=new)
mk_inline MY_BUX MyClassHandleResolve_RT (
MK_HDL const netHdl
) {
return MyBux(MkObjectHandleResolve(netHdl));
} 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; \
})
// Class export & import
__parser__(flags=new,doc-group=_ignore_,doc-index=Class,doc-name=Misc,class=MyClassC,null-return-allow)
mk_inline MY_BUX MyClassGetNull ( void ) {
return (MY_BUX)MK_NULL;
}
// MyClassC - Misc - function
// MyClassC_Class_C_API
// END-MyClassS-Export - created by 'c_Class.tcl -example' - DO NOT change

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.

// BEGIN-Class-Introspection - created by 'c_Class.tcl -example' - DO NOT change
__parser__push__(doc-name=Introspection,doc-index=Class,class=MyClassC,no-rpc,null-return-allow,flags=new);
mk_inline MY_BUX MyClassInstances_RT( MK_PARSER_RT_ONLY ) {
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;
}
// MyClassC_Class_C_API
// END-Class-Introspection - created by 'c_Class.tcl -example' - DO NOT change

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.
/* LABEL-START */
#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 class
MkThreadLocal OT_LNG_CLASS_T OT_CLASS = NULL;
// MO class
MkThreadLocal OT_LNG_T MK(MkBufferC_MK_NULL) = NULL;
// MQ: ObjNew feature: called to return a new or an already existing TCL-SELF-Object
// -> moved to Lib?Pkg?_tcl.h
// MQ: ObjNew feature: selfCreate will be called from "ObjNew->MkSelfNew" if MkObj need a SELF pointer
static MK_PTR NS(MkBufferC_selfCreate) (MK_RT_ARGS MK_OBJ const obj, MK_PTR const env) {
Tcl_Interp *interp = env;
return MK(AtomCreate) (MK_RT_CALL interp,obj,OT_CLASS,NULL,NULL,true);
}
// MQ: called if MkObj must be destroyed and if the SELF is still alive → goal: destroy the SELF
static void NS(MkBufferC_selfDelete) (MK_RT_ARGS MK_PTR selfP, MK_PTR const env) {
Tcl_Object selfO = selfP;
Tcl_Interp *interp = env;
OT_OBJECT_DELETE_HARD(selfO);
}
// ATTENTION: TCL has no "Unlink" because the "Tcl_ObjectSetMetadata(selfO,&MK(AtomMeta),NULL);" call
// also the destructor
#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
// initialize the TCL and MO class specific object
#define ClassInit \
/* if not already done, initialize NEW MQ type */ \
if (MkBufferCTT == NULL) MkBufferCTT = MkBufferSTT; \
\
/* protect against double call */ \
if (MkBufferCTT->selfCreate == NS(MkBufferC_selfCreate)) return MK_OK; \
\
/* add "selfCreate" and "selfDelete" feature to the MQ-Class */ \
MkBufferCTT->selfCreate = NS(MkBufferC_selfCreate); \
MkBufferCTT->selfDelete = NS(MkBufferC_selfDelete); \
\
/* create the TCL-class */ \
Tcl_Object classO = MK(ClassDef)(interp,ns,MkBufferCTT); \
check_NULL(classO) goto error; \
OT_CLASS = Tcl_GetObjectAsClass(classO); \
\
/* create the TCL static Methods */ \
static MkThreadLocal Mk_UnknownS NS(sClassUnknown) = {OT_UNKNOWN_CLASS}; \
check_LNG(MK(UnknownSetup) (interp,classO,NS(sOtClassDef),&NS(sClassUnknown))) goto error; \
\
/* create the TCL instance Methods */ \
static MkThreadLocal Mk_UnknownS NS(sInstanceUnknown) = {OT_UNKNOWN_INSTANCE}; \
check_LNG(MK(UnknownSetup) (interp,classO,NS(sInstanceDef),&NS(sInstanceUnknown))) goto error; \
\
/* define the "NULL" object */ \
MK(MkBufferC_MK_NULL) = Tcl_GetObjectName(interp, \
MK(AtomCreate) (MK_RT_CALL interp, NULL /*obj*/, OT_CLASS, "MK_NULL" /*name*/, \
"::tclmkkernel::MkBufferC" /*ns*/, false /*useSelf*/) \
); \
Tcl_IncrRefCount(MK(MkBufferC_MK_NULL));
#define VER TCL_OO_METHOD_VERSION_CURRENT
/* LABEL-END */

MOT-DETAILS


CAST

There are two possible "cast" operations in the MOT :

  1. A "cast" from a pointer of unknown origin (example: MK_MNG).
  2. 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 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 {
    ...
    }