1. What is ObjectJc

The struct ObjectJc is a basic struct that can be used for all data instances (struct and class) with some common maybe important information with less effort. The information is at least:

  • An identifier for the type, important for type check on downcast.

  • Information about initialized status: init or run, one bit. An instance should be switched to the run mode if all aggregations are complete.

  • Size information.

For these three pieces of information only 4 Byte (an uint32_t) is necessary;

//in src_emC/emC/Base/Object_emC.h:
typedef struct  ObjectJc_T
{
 /**The identSize is helpfull to recognize the instance.
 * The bit31 is used to detect whether it is initialized or not. */
 uint32 identSize;
 #define mInitialized_ObjectJc  0x80000000
 #define mInstanceType_ObjectJc 0x7fff0000
 #define kBitInstanceType_ObjectJc 16
 #define mSize_ObjectJc         0x0000ffff

For usage of ObjectJc in a C++ context with symbolic information about fields in the derived class an additional offset between start of the data class and position of the ObjectJc inside the data are necessary. Additionally a field for handle values is intended here. This both fields are available if the following compiler switch is set:

#ifdef DEF_ObjectJcpp_REFLECTION
  /**Offset from the data-instance start address to the ObjectJc part.
   * It is especially for symbolic field access (reflection) in {cpp}. */
  uint16 offsetToStartAddr;
  /**Some handle bits to use an ObjectJc for lock (mutex). */
  uint16 handleBits;
  #define kNoSyncHandles_ObjectJc 0x0fff;
#endif

In this case, but also independently if DEF_ObjectJc_REFLREF is defined, a reference to ClassJc named reflection is available. This improves the type test capability, especially recognizing base type references (derivation), and it opens the possibility to support full symbolic information about the fields in the data. It is the 'reflection' capability.

#if defined(DEF_ObjectJc_REFLREF) || defined(DEF_ObjectJcpp_REFLECTION)
  #define mInstance_ObjectJc 0x7fff0000
  #define kBitInstance_ObjectJc 16
  /**The reference to the type information. */
  struct ClassJc_t const* reflection;
#endif

If a ClassJc instance can be referenced, the identifier in the first word identSize is now used as instance identifier.

Last not least the own address of the Object can be stored in the ObjectJc. The address of itself can be used if data are imaged to any file (a memory map), and references between the data should be readjusted.

 #ifdef DEF_ObjectJc_OWNADDRESS
   void const* ownAddress;
 #endif
} ObjectJc;

Depending on the memory layout the reflection and the ownAddress has 2 Byte (in 16-bit-Systems), 4 Byte or 8 Byte (for 64-bit-Adressing). In all cases the alignment is correct. Note that 64-bit-addresses should aligned to a memory word boundary.

The idea for ObjectJc came from Java. In Java all instances have a base ('super') class java.lang.Object with adequate information. It is a proven concept.

2. Using ObjectJc for C struct

A C struct for C and C++ compilation should be defined as:

typedef struct MyData_T {
  union { MyBaseData super; ObjectJc obj; } base;
  int32_t anyData;
} MyData_s;
  • The usage of typedef is recommended. Some compilers expect it, it is the clarified form.

  • The MyData_T is the tag name. The tag name should not be the same as the type name, some compilers may have problems elsewhere! It can be used for forward-declaration.

    struct MyData_T;
    ....
    extern struct MyData_T anyData; //data are only declared
    .....
    struct MyData_T* ref = getRef(...)  //only use the reference without access
  • The type name MyData_s is written with suffix _s to offer the possibility for a wrapping C++ class which should be named MyData. This writing rules are regarded by ReflectionGen.en.html.

The ObjectJc is arranged as the last or only one element inside a union. The other parts of the union should be base struct (super struct), whereby the immediate super struct should be arranged first, necessary for INIZ_…​ initialization with { …​ }. This writing rule enables the access to ObjectJc in an unified form independent of super struct nesting (inheritance in C) writing:

ObjectJc* obj = &myDataRef->base.obj;

For C usage it is the same as a simple pointer casting ((ObjectJc*)myDataRef) because the ObjectJc is the first part in memory. But this kind of writing is not recommended because it is an additional (supposed unsafe) cast. Secondly it may be faulty if myDataRef is a C++ class where the ObjectJc is member of. Unnecessary casting is an example of dirty software which runs some years, then somebody extends it, and the assumption for the cast is no longer true. Hence an important rule for C-programming is: "Avoid unchecked casting of pointers!".

3. Using ObjectJc in C++ classes

There are generally three forms:

  • 1) Using as in C, with public access to data.

  • 2) Protected data access only, but a individual operation ,,toObject(),, which returns the ObjectJc const* reference only to the read only ObjectJc data.

  • 3) Using with access via ObjectJcpp which needs at least one virtual operation.

It is a question of C++ using philosophy:

  • If C++ should be used only because of some C++ language features, for example operator definition (float operator+(…​)), but virtual operations are forbidden by style guide for safety than the first or second form is appropriate.

  • For common C++ usage the third form is recommended.

All three forms may use a C struct for data definition. It is necessary if parts of code should only be used in C user environments. It has no disadvantages, but the advantage: Data in struct can be stored and communicate as memory image:

/**Any C use-able data struct based on ObjectJc. */
typedef struct BaseData_Test_ObjectJc_T {
  union { ObjectJc obj; } base;
  //
  int32 d1; //:Any data
  float d2;  //Note: padding any struct to 8-Byte-align if possible,
} BaseData_Test_ObjectJc_s;

The first form, public data, with ObjectJc inside in C++, immediately without virtual operation:

/**The appropriate {cpp} class which wraps the C data in public form: */
class BaseData_Test_ObjectJc : public BaseData_Test_ObjectJc_s
{
  public: BaseData_Test_ObjectJc(...);
  //some routines or operators
  float add(){ return this->d1 + this->d2; }
  float operator*=(float arg) { this->d2 *= arg; return this->d2; }
};

This is an example in which the C++ class does not contain any further data. It defines only non-virtual operations. Virtual operations may be a cause of uncertainty, because the pointer to the vtable is arranged inside the data and a faulty data writing leak can destroy it causing non-predictive behaviour of the program run. Hence virtual operations in C++ are forbidden for some SIL software (SIL=Safety Integry Level).

The access to the ObjectJc data can write very simple as

ObjectJc* obj = &myData->base.obj;

The second form defines the data as protected, but contains a operation to get the ObjectJc reference:

/**The appropriate C++ class which wraps the C data in protected form: */
class BaseData_Test_ObjectJc : protected BaseData_Test_ObjectJc_s
{
  public: BaseData_Test_ObjectJc(...);
  public: ObjectJc const* toObject ( ) { return &this->base.obj; }
  //some routines or operators
  public: setData(...);
  float add(){ return this->d1 + this->d2; }
  float operator*=(float arg) { this->d2 *= arg; return this->d2; }
};

Because all data are protected (or private), the C++ class should contain all access operations, and if necessary getter and setter. The data model is protected as usual in Object Oriented Programming. But the ObjectJc reference is gotten as C data reference, for some existing C-manner operations with ObjectJc. But the reference is const, without malignant pointer casting the data cannot be changed. This is also a protection. All data of ObjectJc are const in fact after initialization, expect the first usage of the element handleBits, but this is set only in a special operation which works with this const* reference. It is a examption.

The third form of ObjectJc usage in C++ encapsulates the data as private or protected too. It offers an interface operation using ObjectJcpp to access the ObjectJc data:

class BaseData_Test_ObjectJcpp : public ObjectJcpp
 , private BaseData_Test_ObjectJc_s               //the C data are private.
{
 /**Returns the ObjectJc base data.
 * This operation should be implemented in this form anytime. */
 public: ObjectJc const* toObject() { return &this->base.obj;  }
 //
 public: BaseData_Test_ObjectJcpp(int size, ClassJc const* refl, int idObj);
 //
 public: int32 get_d1(){ return this->d1; } //encapsulated C data.
 public: float get_d2(){ return this->d2; } //encapsulated C data.
};

The difference to the second form is only: There is a universal access to ObjectJc outside the special class operation definition of toObject(). Any instance which is based on ObjectJcpp can accessed in the same way. It is more universal, but it needs a virtual operation in the base class ObjectJcpp. That class requires implementation of the operation toObject() to get the C-like access to the ObjectJc-data.

The difference between 2) and 3) it small, it needs the same implementation. The real difference is: ObjectJcpp offers a universal access to classes which uses ObjectJc. The type-unspecified (abstract) access to the C++ data use an ObjectJcpp* reference (pointer) instead ObjectJc*. But the gotten ObjectJc const* reference is the same and can be used adequate.

See test sources, it contains some casting situations too: emC_Base/src/test/cpp/emC_Test_ObjectJc/test_ObjectJcpp.cpp.

4. Initializing of data with ObjectJc

4.1. static and const initializing with initializer list in C

To get const data in a const memory section (Flash Rom) only a const initializing can be done with an so named initializer list. Thas is the same situation in C as in C++ (!).

Type const myData = { ..... };  //hint: write const right side.
const Type myData = { ..... };  //it is the same

In C it is not possible to initialize const data in any operations in runtime, other than in C++. But if const data are initialized in runtime, with operations, possible in C++, but that data are never stored in a const memory section! This topic is irrelevant for C++ programming on a PC platform, but essential relevant for Embedded.

For non const data the same initializing with an initializer list is possible for all non-allocated data (not from heap). If static data are used an initializing may be seen as recommended.

Type myData;  //The initial data are undefined - prone of error
Type myData = {0}; //at least forced 0-initialization.

But the initializer list is complex to write, it is a challenge for the programmer. For the variants of ObjectJc there is a macro which builds a proper initializer list:

ObjectJc anObject = INIZ_ObjectJc(anObject, refl_ObjectJc, 234);

This macro is expanded for the several variants of ObjectJc, see chapter What is ObjectJc. It means the code can written independent of the Chamaeleon-manner definition of ObejctJc.

4.1.1. INIZ for the simplest Form of ObjectJc, ID_refl_…​ necessary

For a simple ObjectJc (compiler switch DEF_ObjectJc_SIMPLE is set) this is expanded to

ObjectJc anObject = { mIdOnlySimple_ObjectJc
                  | ((((uint32)(ID_##refl_ObjectJc))<<kBitInstanceType_ObjectJc)
                                          & mIdentSmall_objectIdentSize_ObjectJc)
                  | (sizeof(anObject) & mSizeSmall_objectIdentSize_ObjectJc) } ;

This is a simple 32-bit-integer constant which’s value is calculate on compile time. Calculating on compile time is essential because elsewhere it cannot be a const value in Flash memory. To support this approach an

#define ID_refl_ObjectJc 0x0FFE

is defined inside src_emC/Base/Object_emC.h. Hence the constant calculated on compile time for this INIZ-macro of the chapter above (for anObject) is 0x4FFE0004.

For all user struct and class based on ObjectJc which are existing for an application which should be compiled with DEF_ObjectJc_SIMPLE for all refl_MyType argument an adequate

#define ID_refl_MyType 1234

should be existing. The range is from 1 to about 4000 because of the last values till 4095 (0x0fff) are used from emC-specific classes. This effort is done by automatic Reflection-Offset-Generation for access symbolic data with the Inspector Target Proxy, see ClassJc.en, chapter "The reflection generator". This effort is done for all emC nativ struct of course, it is not necessary for applications, which are not compiled with DEF_ObjectJc_SIMPLE.

Hint: Getting a const value from a given another const instance inside an initializer list is not possible in C and not possible for C++ const-memory-segment-initialization. The access to refl→idType fails though it is a instance defined before. It is too complex for the compiler’s initializer value calculation.

Adequate it is not possible to use the address of the instance shifted and masked for the correct bit position. An address value inside a constant initializer list is only possible by linker replacement, the address value can only be set as const reference as a whole from the linker. Some numeric calculations afterwards cannot be done with it because they would need to be done by the compiler.

4.1.2. INIZ for ObjectJc with a reflection reference

If DEF_ObjectJc_REFLREF is set as compiler switch (applstdef_emC.h, see first chapter) then the line

ObjectJc anObject = INIZ_ObjectJc(anObject, refl_ObjectJc, 234);

is expanded to

ObjectJc anObject = { ( (((uint32)(234))<<kBitInstanceType_ObjectJc)
                        & mIdentSmall_objectIdentSize_ObjectJc)
                      | (sizeof(anObject) & mSizeSmall_objectIdentSize_ObjectJc)
                    , &refl_ObjectJc
                    } ;

For this case an ID_refl_…​ is not necessary. The higher bits in ObjectJc::identSize are set from the given instance number, in this case 234. This instance number should be helpfully for debugging and for data evaluation, and maybe for tests ("…​ is it the expected instance?"). The type identification is given with the reference to the reflection data which should be located statically in a const memory section.

4.1.3. INIZ for the full ObjectJc capability

The ObjectJc with more capability contains the handleBits and the offsetToStartAddr. The last one is always 0 for const C data. It is only used for C++. The handleBits are always initialized with 0xfff. It cannot be changed for const instances, hence the capabilities of synchronized_ObjectJc(…​) cannot be used on const objects, which is quite clearly. It means all other elements of a full qualified ObjectJc are pure const values independent of the arguments of the INIZ_-macro.

4.1.4. Nested INIZ_…​ for user types

For a struct using ObjectJc a specific initializer macro can / should be defined:

#define INIZ_MyData(OBJ, ID, DATA) \
 { { INIZ_ObjectJc(OBJ, refl_MyData, ID) }, DATA }

The expanded form may be complex and depends on the variants of ObjectJc, but the macro definition is well arranged. The additional { } surround INIZ_ObjectJc are necessary because the writing rule union { ObjectJc obj; } base; is used.

At least a non-const instance should be initialized with { 0 } but for the ObjectJc-part the correct initializing data should be given including the &refl_MyType. Then especially the size of the instance has already been set initially.

4.2. CTOR_ObjectJc(…​) and iniz_…​() for as initializing functions

To initialize the ObjectJc data especially in a C++ class environment or on run time / startup time (not as initializer List) use

CTOR_ObjectJc(OTHIZ, ADDR, SIZE, REFL, ID);

This is a macro.

  • OTHIZ, the first argument is the pointer to the ObjectJc part.

  • ADDR: The second argument is expected of type void* and should be the address of the instance itself. It has the same value for C-compilation as OTHIZ if ObjectJc is the first element in a struct, but for C++ there may be small differences between the address of the instance and the ObjectJc data part. This is if inheritance and virtual tables are used. The difference between both address values are stored in the ObjectJc::offsetToInstanceAddr, which requires setting DEF_ObjectJcpp_REFLECTION. If it is not set but DEF_REFLECTION_FULL is set, and C++ compiling is used, then an compiler error message is forced (#error …​). The offsetToInstanceAddr is necessary to access data via reflection (FieldJc). Hence in C++ this form of initializing should be used. The initializer list is not suitable for use.

  • SIZE: The size argument should be the size of the whole instance. It is stored inside ObjectJc for possible checks.

  • The reflection argument (See chapter "Reflection and Types") can only be given as name of the refl_…​ ClassJc data. For the two different implementations of DEF_ObjectJc_SIMPLE or not, either the pointer to this reflection reference is used as argument, then

    iniz_ObjectJc(OTHIZ, ADDR, SIZE, &(REFL), ID)
  • …​ is called. Or

    inizReflid_ObjectJc(OTHIZ, ADDR, SIZE, ID_##REFL, ID)
  • …​ is the result of the macro expansion, which builds the ID_refl_…​ as argument to store only the type ident for DEF_ObjectJc_SIMPLE. It is the reason for the macro definition.

  • ID: The last argument is an instance identifier. If 0 is given, it is formed by an incremented static variable, so that all instances get a consecutive number.

5. Initializing of data based on ObjectJc

For example we have:

typedef struct MyComplexDataType_T {
  union { ObjectJc obj; } base;
  float re, im;
  //
  MyDataType embdata;
  //
} MyDataType_s;

5.1. static and const struct data, with initializer list

For static initialization there may be a complex INIZ…​ macro:

#define INIZ_MyComplexDataType (  OBJ, REFL, ID, ANGLE) \
 { { INIZ_ObjectJc(OBJ, REFL, ID) }  \
 , 0, 0         \
 , INIZ_ObjectJc( &(OBJ)->embdata.base.obj, sizeof((OBJ)->embdata) \
                , &reflection_MyDataType, ID, 0) \
 }

If this INIZ-macro is maintained together with the struct-definition (both are in the same header), it is not too demanding.

An other variant: offer only the

void iniz_MyComplexDataType (  MyComplexDataType_s* thiz, void* ptr
          , int size, struct ClassJc_t const* refl, int idObj
          , float angle
          ) {
  memset(thiz, 0, sizeof(*thiz)); //clean all
  iniz_ObjectJc(&thiz->base.obj, ptr, size, refl, idObj);
  iniz_ObjectJc( &embdata.base.obj, &embdata, sizeof(thiz->embdata)
                 , &reflection_MyDataType, 0);
}

In both cases the nested INIZ_…​ or iniz_…​ is invoked. The reflection_MyDataType is given, because it is defined in the struct with this type. But the refl argument is given from outer because it is possible that this struct is a base structure or a base of a class, the instance has a derived reflection. The reflection which should be given is type of the real instance anyway.

5.2. ctor for C-data based on ObjectJc

A ctor_MyType(…​) routine is the constructor for C-data. For example we have

typedef struct MyDataType_T {
  union { ObjectJc obj; } base;
  float re, im;
} MyDataType_s;
MyDataType_s* ctor_MyDataType(ObjectJc* othiz, float angle) {
  STACKTRC_ENTRY("ctor_MyDataType");
  MyDataType_s* thiz = null;
  if( checkStrict_ObjectJc(othiz, sizeof(MyDataType_s)
    , &reflection_MyDataType, 0, _thCxt
    ) {
    MyDataType_s* thiz = C_CAST(MyDataType_s*, othiz); //cast after check!
    thiz->re = cosf(angle);
    thiz->im = sinf(angle);
  }
  STACKTRC_RETURN thiz; //returns null on not thrown exception
}

The ctor expects a pointer to the data area in form of an ObjectJc reference. The data can be all set to 0, except the ObjectJc-data. The calling environment before calling this ctor should initialize the ObjectJc-data. That can be done:

  • either by using alloc_ObjectJc(…​)

  • or by an initializer list using INIZ_ObjectJc(…​) see chapter INIZ

  • or by calling iniz_ObjectJc(…​), especially in a C++ constructor or for embedded data basing on ObjectJc too, see chapter above.

The checkStrict_ObjectJc(…​) checks

  • the size, it should be greater than or equal the expected size. The size is greater if the instance is derived and contains more data.

  • the type via reflection. Doing so also a derived reflection type in ObjectJc is recognized. Then the requested type is recognized as base type. The reflection check is done only for full capability of ObjectJc, not for DEF_ObjectJc_SIMPLE. The reflection should be generated with full capability, not only with a simple usage of INIZ_ClassJc(…​) for derived reflection. The check of reflection can be disregarded using null as reflection argument.

  • the instance id if given (here 0 is given).

Only if the check is passed, the data can be set in ctor. If the check fails, the routine checkStrict_ObjectJc(…​) throws an exception. If the exception handling is not available (for simple applications), the ctor returns null which should be tested outside. It is a fatal error situation, the instance should match.

5.3. Initializing for C++

In C++ either the data are created with

MyData* data = new MyData(...);

or they are created statically with

MyData data(...);

In both cases the constructor is part of data creation. That is consequent and prevents errors because of non-initialized data.

The constructor in C++ should call all ctor of base classes, at least the ctor for the C-data, see chapter above. The C++-ctor for this example should be written as:

MyData::MyData(int size, ClassJc const* refl, int idObj) {
  iniz_ObjectJc( &this->base.obj, this, size, refl, idObj);
  //Now initialize the base struct of this class:
  ctor_BaseData_Test_ObjectJcpp(&this->base.obj);
  ..... further initialization of {cpp} data
}

This means that the ctor needs size and reflection information about the C++ class:

MyData* data = new MyData(sizeof(MyData), &reflection_MyData, 0);

If the idObj argument is given with 0, a self-counting identification number is assigned, which can be use for debug. The idObj should be managed in the user`s responsibility.

6. ALLOC_ObjectJc()

The macro-wrapped function call of

ALLOC_ObjectJc(SIZE, REFL, ID)

is for C-usage or for creation of non-class-data based on ObjectJc in C++. Depending on DEF_ObjectJc_SIMPLE it expands either / or to

allocReflid_ObjectJc(SIZE, ID_##REFL, ID, _thCxt)
allocRefl_ObjectJc(SIZE, &(REFL), ID, _thCxt)

adequate to CTOR_ObjectJc in the chapter above. Additionally it requires the pointer to a Thread context see Stacktrace, ThreadContext and Exception handling because generally the allocation can fail, then an Exception handling is recommended.

The core prototype with reflection reference is

extern_C ObjectJc* alloc_ObjectJc ( const int size, const int32 typeInstanceIdent
                struct ClassJc_T const* refl  , struct ThreadContext_emC_t* _thCxt);

This routine allocates and initializes the core data of the ObjectJc.

7. Type check for casting and safety

Often a pointer is stored and/or transferred as void*-pointer if the precise type is not known in the transfer or storing environment. Before usage a casting to the required type is done. But such casting turns off the compiler error checking capability. An unchecked cast is a leak for source safety. A void* pointer should only be used for very general things. For example for memcpy. In C++ some casting variants are present. The static_cast<Type*> checks whether the cast is admissible in an inheritance of classes, and adjusts the correct address value toward the start address of a base class. It forces a compiler error if the type is faulty. The dynamic_cast<Type*> does the same for 'downcast', by correcting the address value for the derived class. The dynamic cast checks the possibility of type derivation and causes a compiler error if the types are incompatible. It is not safe, a fault instance type can be assumed. To work safely it needs a type information of the referenced instance. This is possible for C++ by switching on RTTI (Real time type information) for the compilation. But that is not supported for C. The reinterpret_cast<Type*> delivers faulty results if it is used for inheritance class Types. It should only be used if C-data are present.

In C only the known (Type*)(ref) is available, this is the same as reinterpret_cast<Type*> for C++. For compatibility C and C++ a macro CAST_C(Type, dataI is defined in emC/Base/os_types_def.h which is adapted for C++ to a reinterpret_cast<Type*>. On the one hand the mnemonic C_CAST may be very clear, on the other hand in C++ a immediate (Type*)(ref) is often reported as either warning or error.

Independently of the question C or C++ or with or without RTTI the ObjectJc base class delivers the type information. It works for C++ too either using the ObjectJcpp-Base class or with immediate access to the C data which contains ObjectJc. The type check can be done with

extern_C ClassJc const refl_MyType;
.....
bool bTypeOk = instanceof_ObjectJc((&myDataObj->base.obj, &refl_MyType);

This routine recognizes and returns true for a base type too. Note that for class inheritance in C++ with multiple inheritance or with virtual operations a static_cast<TYPE>(OBJ) has to be used for cast because addresses should be tuned. For C inheritance using a base type struct as first element of the inherited struct of course a C_CAST(TYPE, OBJ) is only possible and necessary. But the capability to recognize base types depends on setting and using of DEF_REFLECTION_FULL in conclusion without definition of DEF_ObjectJc_SIMPLE or inside poor applications (DEF_ObjectJc_SIMPLE is set) with using of DEF_ObjectJc_REFLREF. See Variants of emC-usage in Applications - chapter ClassJc and Reflection.

The cast seems to be safe and might not be necessarily be tested if the type is known in the user programming environment, because the same software module stores the instance pointer, and gets it back. But there may be programming errors, if the algorithm is enhanced etc.etc. Hence it is recommended to check the type too, but with an assertion, which can be switched off for fast runtime request. With a side glance to Java the type is always checked on runtime for castings. In Java a casting error is never possible. For that the reflection info in java.lang.Object is used. Because castings are not the operations most commonly used in ordinary programs, a little bit of calculation time is admissible for that.

The type check as assertion should be written as:

if(ASSERTs_emC(instanceof_ObjectJc((&myData->base.obj, &reflection_MyType))
              , "faulty instance", 0, 0) {
  MyType* myData = C_CAST(MyType*, myData);
  ...

The assertion ASSERT_emC(…​) can be return always with true if assertions are not activated, for fast realtime. Then the if(true) is optimized by the compiler. The C_CAST is an reinterpret_cast for C++ usage and a normal ((MyType*) myData) for C usage.

The reflection_MyType is the type information, see next chapter.

8. Reflection and Types

In the full capability of ObjectJc reflections contains symbolic information for all data elements. A reflection instance of type ClassJc contains the type information, all base type information and the fields and maybe operations (methods) too. With the information about base types (super types) the instanceof_ObjectJc(…​) can check whether a given instance is proper for a basic type too. The construction of full reflection is described in ReflectionJc.

For simple capability of ObjectJc use-able in embedded platforms maybe without String processing with fast realtime or less hardware resources there are four variant forms of reflections:

  • a) In the simplest form, only an idType is stored which is contained in the ObjectJc instance too to compare it. In this case the ClassJc is defined as:

    typedef struct ClassJc_t {
     int idType;   // sizeReflOffs;
    } ClassJc;
  • b) Reflection access with Inspector target proxy. In this case reflection data are generated in form of positions of data in a struct and a number (index) of any struct type. In this case the ClassJc is defined as:

    typedef struct ClassJc_t {
     int idType;   // sizeReflOffs;
     //
     int const* reflOffs;
    } ClassJc;
  • c) The reference reflOffs refers to the generated reflection data. As the reflection data are defined in succession in a "const" memory area, the low 16-bit of this pointer address can be used as a type identifier.

  • d) No Reflection access, DEF_REFLECTION_NO is set: The reflections are only defined to have information about the type:

    typedef struct ClassJc_t {
     int idType;   // sizeReflOffs;
     //
     char const* nameType;
    } ClassJc;

The nameType is optional depending on DEF_NO_StringJcCapabilities. See org/vishia/emC/sourceApplSpecific/SimpleNumCNoExc/ObjectJc_simple.h

The kind to build the idType depends on some possibilities on initialization of the reflection_…​Type instance and can be defined by the users programming. For example additional information, which can be used for debugging, are given outside a fast realtime and low resource CPU, the idType is a simple index. It is important that the idType of all reflection instances are unique. The instanceof_ObjectJc(…​) compares only the idType given with the reflection…​ argument with the type information in ObjectJc. It is the low 16 bit of idInstanceType for the simple ObjectJc.

For the reflection with full capability see Reflection.en.html.