type.
As a result we are given class descriptor, which provides information about class methods and
fields. We can use these field descriptors to fetch/store object fields and can use
method descriptors to lookup and invoke methods.
Many programming languages provide built-in reflection mechanism. For example, in Java
there is special package java.lang.reflect . But unfortunately C++
doesn't support reflection. Not so long ago first step was made in this direction -
RTTI support was added to the language. But RTTI provides only very restricted subset of reflection:
it allows to get object compile-time and runtime type (it is possible to get object runtime type
only if object class contains virtual functions). You can compare types and you can get type name -
and that is all you can do with RTTI.
There are several ways of extracting type information for C++:
| Approach | Advantages | Disadvantages |
|---|---|---|
| Parse debugging information |
|
|
| Special preprocessor which will parse C++ sources and build class descriptors |
|
|
| Create specialized compiler which will support reflection |
|
| Let programmer to provide this information himself |
|
|
It seems to be better to start with an example:
#include "reflect.h"
#include "typedecl.h"
class A {
public:
int i;
char* pc;
double d;
protected:
long larr[10];
A** ppa;
A* pa;
public:
RTTI_DESCRIBE_STRUCT((RTTI_FIELD(i, RTTI_FLD_PUBLIC),
RTTI_PTR(pc, RTTI_FLD_PUBLIC),
RTTI_FIELD(d, RTTI_FLD_PUBLIC),
RTTI_ARRAY(larr, RTTI_FLD_PROTECTED),
RTTI_PTR_TO_PTR(ppa, RTTI_FLD_PROTECTED),
RTTI_PTR(pa, RTTI_FLD_PROTECTED)));
};
RTTI_REGISTER_STRUCT(A, 0);
So, you see that there two special macros RTTI_DESCRIBE_STRUCT and
RTTI_REGISTER_STRUCT.
First one describes class components and is used inside declaration of the class.
And RTTI_REGISTER_STRUCT macro should be used in implementation module
(.cpp file) to register class descriptor in repository.
Class fields should be described using the following macros:
| Macro | Field type |
|---|---|
RTTI_FIELD | Scalar, structure or class |
RTTI_PTR | Pointer to scalar, structure or class |
RTTI_PTR_TO_PTR | Pointer to pointer to scalar, structure or class |
RTTI_ARRAY | One dimension array of scalars, structures or classes |
First parameter of these macros specifies field and second - bit mask of arbitrary flags (qualifiers) for this field. There are some predefined flags:
enum RTTIFieldFlags {
RTTI_FLD_INSTANCE = 0x0001,
RTTI_FLD_STATIC = 0x0002,
RTTI_FLD_CONST = 0x0004,
RTTI_FLD_PUBLIC = 0x0010,
RTTI_FLD_PROTECTED = 0x0020,
RTTI_FLD_PRIVATE = 0x0040,
RTTI_FLD_VIRTUAL = 0x0100, // used for virtual base classes
RTTI_FLD_VOLATILE = 0x0200,
RTTI_FLD_TRANSIENT = 0x0400
};
So that is all with describing class field. Now lets see how methods can be described:
class B : public A {
int x;
public:
virtual void foo();
char* echo(char* p);
RTTI_DESCRIBE_CLASS(B, (RTTI_BASE_CLASS(A, RTTI_FLD_PUBLIC),
RTTI_FIELD(x)),
(RTTI_PROC(foo, RTTI_FLD_PUBLIC|RTTI_MTH_VIRTUAL),
RTTI_FUNC(echo, RTTI_FLD_PUBLIC)));
};
RTTI_REGISTER_CLASS(B, 0);
Here class B is declared, which is derived from class A and has methods
foo and echo. As you can see base classes are described within list of
fields. And array of field descriptors returned by RTTIClassDescriptor::getFields method
also returns field descriptors for base classes (them has RTTIDerivedType type with
tag RTTI_DERIVED). It was done to simplify iteration through the object fields.
Usually, to store/retrieve values of all object fields, recursive method is implemented
which iterate through all fields in the class and is recursively invoked for each compound field.
There is no difference for such method between inherited in included components.
If base classes are not included in list of fields, then such method has to implement additional loop
through all base classes. And in our case everything can be done within one loop.
When you are describing class with methods, you should use RTTI_DESCRIBE_CLASS
macro instead of RTTI_DESCRIBE_STRUCT. In this macro you should specify
name of the class, list of the field descriptors and list of method descriptors.
For describing method two macros are used: RTTI_PROC - for describing void methods
and RTTI_FUNC for method returning some value.
A with
virtual method foo and class B derived from class A which
overrides foo method, get descriptor of A, find method foo
in it and invoke this method, then A::foo will be called)
const, volatile,...) are currently extracted.
nmake.exe.
It will build reflect.lib library and testrtti.exe example.
To be able to use RTTI in you application, you need to include reflect.h
and typedecl.h header files. Then add type descriptors using macros defined
in typedecl.h header file and decide how you will determine type of the object.
There are several ways of determining object type:
char* getType and derive all classes
for which you want to get runtime type information from this class.
T,
then T::RTTIDesciptor is descriptor of this class.
If you know name of the class, you can locate it in repository using
RTTIRepository::findClass method. You also can request and iterate
through complete list of application classes (see testrtti.cpp example).
Having class descriptor, it is possible to find field or method with specified name or iterate through all components.
At Unix with GCC and GNU make package can be build with make -f makefile.gcc
command. It builds libreflect.a library and testrtti example.
To be able to use debugging information you should change directory to ./bfd and do
make here. As a result library libbfdreflect.a and code>testrtti
example will be build. To extract type descriptors from the image with debugging information
you need both of these libraries (libreflect.a and libbfdreflect.a)
and should use class RTTIBfdRepository instead of RTTIRepository.
The method RTTIBfdRepository::load(char const* filePath) loads debug
information from specified file. Certainly it will be able to it only if image was compiled with
enabled debug information (-g option for GCC).
I will provide e-mail support and help you with development of CppReflection package.
Look for new version at my homepage | E-Mail me about bugs and problems