Generics Reference Manual

Metaclass Definition

    Metaclasses are classes descriptions that allow for dynamic analyse of instances types at runtime.


CMetaClass type

    There are 3 defined types of metaclasses :

typedef enum
        {
            METACLASS_GENERIC=0,
            METACLASS_DYNAMIC,
            METACLASS_CAPSULE
        } TMetaClass;

generic the metaclass describes an abstract class i.e. a class that declares pure virtual functions
dynamic the metaclass describes a non abstract class and is able to instanciate it by calling its default constructor
capsule the metaclass describes a non abstract class but is not able to instanciate it because either the described class does not define a default constructor either the programmer's choice was not to give the access to this functionality

 A metaclass handles the followings :
 - the base definition the described class is derived from (NULL for the absolute one),
 - the metaclass type (dynamic, generic or capsule),
 - the described class name (its type as a CString value),
 - the described class tag identification (should not equal 0L),
 - the default described class size (sizeof class),
 - "a default constructor pointer" of the described class (only if it is a dynamic one).

 A metaclass handles static attributes too. Their definitions are as follow :
 - all declared metaclasses list access for the binary and its potential modules,
 - a function that searches for a metaclass from a given class name,
 - a function that searches for a metaclass from a given class tag identification,
 - a function that checks a derivation status between two metaclasses,
 - a function that searches for derived metaclass described classes definition,
 - a function that returns the declared metaclasses list hierarchy as a tree.

typedef NServices::TBuffer <const CMetaClass *> CMetaClasses;
typedef NServices::TNode   <CMetaClass            > CMetaClassNode;

class CMetaClass
{
    // metaclass attributes
    public :

        const CMetaClass *       BaseMetaClass;
        const TMetaClass          MetaClassType;
        const UInt32                  ClassTag;
        const CString                 ClassName;
        const UInt16                  ClassSize;
        CClass *                     (*ClassInstanciate)    ();

    // static attributes
    public :

        static CMetaClasses             MetaClasses;
        static const CMetaClass *    GetMetaClass           (const CString &);
        static const CMetaClass *    GetMetaClass           (const UInt32);
        static bool                            MetaClassIs              (const CMetaClass *inBase, const CMetaClass *inCandidate);
        static bool                            IsFinal                       (const CMetaClass *);
        static CMetaClassNode       GetMetaClassTree    ();
   
    // instanciation section
    public :

        CMetaClass  (const CMetaClass * =NULL, const TMetaClass =METACLASS_GENERIC, const UInt32 =0L,  
                               const CString & =CString(), const UInt16 =0, CClass * (*) () =NULL);
        virtual ~CMetaClass        ();
};


 The CMetaClass constructor and destructor handle the insertion and deletion of the metaclasses instances into the static list of the binary defined metaclasses. There is no initialization process requirement in order to use the metaclasses facility. All of them are declared as static constants and are also allocated before the binary execution entry point is called as described in the following "macros services" section.



CClass base class metaclass association

    The CClass definition is the absolute abstract metaclassed class definition. Every metaclassed class defines at least the following functions :
 
Bool ClassIs (const CMetaClass *) const;
    A function that dynamically tests the instance definition from a given metaclass.
   
virtual const CMetaClass * GetMetaClass () const;
    A virtual function that returns the correct instance associated metaclass. 
    
static CClass * Instanciate ();
    A static function that returns a default instance of the metaclassed class only if it is a dynamic one. The dynamic metaclassed class should have a default constructor and the associated metaclass should be a dynamic one to be able to access it. You should not use directly this function. It is only defined for the CMetaClass "CClass * CMetaClass::ClassInstanciate ()" attribute.

 Those two last functionnalities are given the cmetaclass.h defined macros SECTION_GENERIC_METACLASS, SECTION_CAPSULE_METACLASS and SECTION_DYNAMIC_METACLASS as described in the following "macros services" section.

class CClass
{
    // instanciation section
    public :

        CClass                     ();
        virtual ~CClass        () =0;

    // dynamic definition test
    public :

        // instance definition dynamic test
        Bool            ClassIs        (const CMetaClass *) const;

        // generic metaclass association
        SECTION_GENERIC_METACLASS;
};

// class tag and metaclass declaration
DECLARE_GENERIC_METACLASS ('clss', CClass, NULL);

    The classes metaclasses tags of the libgenerics are declared using constant characters then converted to unsigned long. This notation may generate some warnings when building your code. To avoid those recursive "dummy" unneeded messages, you can use the GNU gcc -Wno-multichar option.



Macros services

    Some macro are defined to associate the metaclasses to their classes in order to facilitate the process and to respect the declaration syntaxes. They are logically separated in 4 sections.

    The first macro familly is dedicated to class associated metaclass code sections i.e. part class prototypes declarations. There are 3 possible sections types, according to the metaclasses types that should be associated to the defined class (see "CMetaClass types" section above) :
SECTION_GENERIC_METACLASS;
SECTION_CAPSULE_METACLASS;
SECTION_DYNAMIC_METACLASS;

    The second macro familly is about static constants metaclasses objects and class tag / signature declarations. Those macros should be used in concordance with the metaclass class section declared above.
DECLARE_GENERIC_METACLASS   (classtag, class, baseclass);
DECLARE_CAPSULE_METACLASS  (classtag, class, baseclass);
DECLARE_DYNAMIC_METACLASS (classtag, class, baseclass);

    The third macro familly should be used to resolve code of the metaclasses part class prototypes declarations. There are still 3 available macros.
RESOLVE_GENERIC_METACLASS  (class);
RESOLVE_CAPSULE_METACLASS  (class);
RESOLVE_DYNAMIC_METACLASS (class);

    The last macro familly is defined to facilitate and organize the metaclasses access :
__classtag (class)
        returns the class tag / signature of a given class type.
__metaclass (class)
        returns the metaclass address of a given class type (const CMetaClass *).
metaclass_cast (instance)
        returns the metaclass address of a given class instance (virtual const CMetaClass * instance -> GetMetaClass ()).
classtag_cast (instance)
        returns the instance associated class tag / signature.
METACLASSESLENGTH
METACLASSES(index)
        returns the declared metaclasses list length and the metaclass of specified index in the global list respectively.



Deriving a metaclassed class and testing a class type at runtime

    a) As example, consider you want to define your CMyClass class that would be metaclassed described :

"myclass.h"
  #include "cclass.h"

     class CMyClass : public CClass
     {
         public :
            CMyClass ();
            SECTION_xxx_METACLASS;
     };
     DECLARE_xxx_METACLASS ('mycs', CMyClass, CClass);

  "myclass.c"
   #include "myclass.h"
 
     RESOLVE_xxx_METACLASS (CMyClass);
     CMyClass::CMyClass () : CClass () { }

  "main.c"
    #include "myclass.h"
    ...
     CClass *MyClass = new CMyClass ();
     printf ("%s\n", metaclass_cast(MyClass) -> ClassName.Get());                // "CMyClass"
     printf ("%s\n", __metaclass(CMyClass) -> ClassName.Get());                 // "CMyClass"
     printf ("%d\n", classtag_cast(MyClass));                                                   // "unsigned long('mycs')'"
     printf ("%d\n", __classtag(CMyClass));                                                    // "unsigned long('mycs')"
     for (size_t i=0; i<METACLASSESLENGTH; i++)
        printf ("%s\n", METACLASSES(i) -> ClassName.Get());                    // "CClass \n CMyClass"



    b) Or, let's consider you have 3 classes definitions with the following implementation and a function that should interpret your classes types to do some particular job (all those samples classes should be described with metaclasses) :
  
    CClass
    |--CA
    |--CB
        |--CC

    void MyFunc (CClass &inClass)
    {
        // analyse class type
        if (inClass.ClassIs (__metaclass(CA)))
            printf ("got a CA instance\n");
        else if (inClass.ClassIs (__metaclass(CB)))
            printf ("got a CB or CC instance\n");
    }