- 전체
- Native Apps
- WinJS App
- C# Apps
- XAML
- VB.net
- VisualC.net
- C++
- MFC
- visual studio mobile app dev
- Azure ms cloud service
- Asp.net
- 인공지능 (AI)
- wpf
- UWP
- MAUI
- asp.net
C++ Reflection for C++
2017.07.01 19:08
Reflection for C++
Introduction
Reflection is a mechanism making it possible to investigate yourself. If we are speaking about programming languages, reflection is used to investigate format of objects at runtime, invoke methods and access fields of these objects. Reflection is required if you want to implement some generic code which can work with objects of unknown (at the time of creation of this code) types. There are several tasks in which reflection can be used: remote method invocation, serialization, object dumps, database interfaces. How it works? Given some object we first should ask for its 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 |
|
|
Architecture
As far as creating C++ preprocessor or specialized compiler requires huge amount of work and still can not solve all the problems, I decide to implement first and last approaches (getting type information from debug information and let programmer to provide this information). So CppReflection product consists of the following parts:
- Common core: descriptors for classes, fields, methods which provide all reflection functionality.
- Helpers for describing type information by programmer: set of macros, overloaded methods, and supplementary classes which makes it possible for programmer to specify only names of fields and methods and all other information (type, offset, size, profile) is calculated automatically. Using this helpers make task of describing types more convenient and less error prone.
- Library which is used to extract information from ELF, COFF and some other executable files formats.
Reflection API is described in this document generated by Doxygen. Code for extracting type information from debug data was taken from GNU objdump utility. The rest of this chapter provide more information about describing types by programmer.
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.
Limitations
In both models of using reflection (explicitly described by programmer and extracted from debugging information) there are some limitations. Below we enumerate them grouping in three part - common for proposed reflection API and specific for each approach.
Common limitation
- It is possible to create instance of the object only using default constructor.
- API doesn't support enums, unions and bit fields.
- API provides type only information only about classes and structures used in the application - no information about global or static functions and variables is available.
Limitation of extracting information from descriptors written by programmer
- Only restricted set of field types is supported (scalars, classes, structures, array and pointers of those types, pointer to pointers to those types).
- Methods can not have more than 5 parameters
- It is not possible to describe static methods and fields.
- Each class should have public default constructor or have no constructors at all.
Limitations of extracting types from debug information
- This approach works only under Unix and most probably only with GCC (because format of mangling symbols is specific to each compiler)
- It is possible to invoke method only at platforms where parameters are passed through the stack (for example Intel). Unfortunately there is no portable way of invoking arbitrary method in C++.
- There is no way to distinguish classes defined in application from system classes which are also extracted from debug information.
- Virtual methods can be invoked only statically (i.e. if you have class
Awith virtual methodfooand classBderived from classAwhich overridesfoomethod, get descriptor ofA, find methodfooin it and invoke this method, thenA::foowill be called) - No field qualifiers (such as
const, volatile,...) are currently extracted.
Usage
To build the package under Window with Microsoft Visual C++, just execute 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:
- Create base class with virtual method
char* getTypeand derive all classes for which you want to get runtime type information from this class. - Use templates to parameterize object type. If you have template parameter
T, thenT::RTTIDesciptoris descriptor of this class. - Use C++ RTTI mechanism. Once again you need to have some common base class with virtual function. But it is not necessary to redefine this function in all classes. Instead of it you can use RTTI typeof operator to get runtime type of the object, using pointer to this object.
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).
Distribution terms
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
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
[출처] http://www.garret.ru/cppreflection/docs/reflect.html
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.

