C++ reflection mechanism details and example code

  • 2020-05-12 02:59:09
  • OfStack

C++ reflection mechanism

1. Introduction:

Java has a very prominent dynamic correlation mechanism: Reflection, which in the case of Java means classes, which we can load at runtime, detect, and use completely unknown at compile time. In other words, an Java program can load an class whose name is known only to the runtime, learn its full construct (but not its methods definition), and generate its object entities, or set its fields value, or invoke its methods. However, C++ does not support reflection, although C++ has RTTI(runtime type recognition). But to realize C + + object serialization, serialization is stored in the disk, the objects into the hexadecimal code to the fixed format, and then to use will be stored in the disk 2 hexadecimal code into an in-memory object, the process is always need to have a instructions to tell the compiler to generate a what kind of object, the simplest way is the name of the class, of course, for example: 1 ClassXXX object stored in the disk, read from disk again let the compiler new1 according to the name of "ClassXXX" object.


ClassT* obj = FactoryCreate("ClassT"); 

Similar to the above syntax, C++ has no built-in syntax to implement, but we can implement it by other methods. (due to my limited ability, this blog is only about how to implement this reflection mechanism simply, and there is no discussion about whether it is necessary to have this reflection mechanism in C++. Of course, if there is anything wrong with the blog, please feel free to comment below. Thank you.)

2. Implementation:

1. It is easy to think of a simple factory pattern to achieve this effect: for example


class Object 
{ 
public: 
  virtual string ToString() = 0; 
}; 

This is the base class that all the classes that need to implement reflection need to inherit from, and then the derived classes just need to implement ToString again. Such as:


class MyClass :public Object 
{ 
public: 
  virtual string ToString(){ return "MyClass"; } 
}; 

Then there is the factory used to generate the object.


Object* FactoryCreat(const string& className) 
{ 
  if (className == "ClassA") 
    return new ClassA; 
  else if (className == "ClassB") 
    return new ClassB; 
  else if(className == "ClassC") 
    return new ClassC; 
  else if(className == "ClassD") 
    return new ClassD; 
  else if(className == "ClassE") 
    return new ClassE; 
  ... 
} 

We can use it like this:


int main() 
{ 
  Object* obj = FactoryCreat("MyClass"); 
  cout << obj->ToString(); 
  delete obj; 
  return 0; 
} 

We use the simple factory pattern to feel like we have solved the problem, we can use the string to new1 corresponding objects, but if we want to create a new class or modify a class, then this FactoryCreat will have to be modified. 10 is bad for maintenance. So we need to do it in a different way.

2. Factory mode combined with callback mechanism.

First of all, we should sort out the basic context of this method:

1. There needs to be a mapping inside the factory, that is, one string corresponds to one class new method.
2. The factory gives an interface, we pass in a string, then return the object pointer from the method new corresponding to this string.
3. Our new class, if we need to support reflection, will automatically register its new method and name in the factory map.

OK, if we can fulfill the above requirements, then we will have 10 points less to change when the class is extended. We basically don't change the code for the factory. This basically realizes the basic function of our C++ reflection mechanism.

Let's take a step by step to parse the code:

First of all, we still need an Object as the base class that needs to support the reflection mechanism class


//Reflex.h 
class Object 
{ 
public: 
  Object(){} 
  virtual ~Object(){} 
  static bool Register(ClassInfo* ci);     // Register the incoming 1 a classInfo( The class information ), Register the class information in the map  
  static Object* CreateObject(string name);   // The factory produces the interface of the object  
}; 

And then the implementation:


//Reflex.cpp 
static std::map< string, ClassInfo*> *classInfoMap = NULL; 
bool Object::Register(ClassInfo* ci) 
{ 
  if (!classInfoMap)  { 
    classInfoMap = new std::map< string, ClassInfo*>();   // Here we go through map To store this mapping.  
  } 
  if (ci) { 
    if (classInfoMap->find(ci->m_className) == classInfoMap->end()){ 
      classInfoMap->insert(std::map< string, ClassInfo*>::value_type(ci->m_className, ci)); //  The name of the class  <-> classInfo 
    } 
  } 
  return true; 
} 
Object* Object::CreateObject(std::string name) 
{ 
  std::map< string, ClassInfo*>::const_iterator iter = classInfoMap->find(name); 
  if (classInfoMap->end() != iter) { 
    return iter->second->CreateObject();     // When you pass in a string name after , through name find info, And then call the corresponding CreatObject() Can be  
  } 
  return NULL; 
} 

The rest we need is 1 classinfo class and we're done:


//Reflex.h 
 
typedef Object* (*ObjectConstructorFn)(void); 
class ClassInfo 
{ 
public: 
  ClassInfo(const std::string className, ObjectConstructorFn ctor) 
    :m_className(className), m_objectConstructor(ctor) 
  { 
    Object::Register(this);       //classInfo The constructor is passed in the class name corresponding to the class new The function is then automatically registered in map In the.  
  } 
  virtual ~ClassInfo(){} 
  Object* CreateObject()const { return m_objectConstructor ? (*m_objectConstructor)() : 0; } 
  bool IsDynamic()const { return NULL != m_objectConstructor; } 
  const std::string GetClassName()const { return m_className; } 
  ObjectConstructorFn GetConstructor()const{ return m_objectConstructor; } 
public: 
  string m_className; 
  ObjectConstructorFn m_objectConstructor; 
}; 

With these classes in place, we can simply make the classes that need to support reflection meet the following requirements:

1. Inherit the Object class.
2. Overload 1 CreatObject() function, inside which return new itself class.
3. Have a member of classInfo and initialize it with the class name and CreatObject.

Classes that meet all three of the above requirements will be able to create objects using the reflection mechanism. We can look at the following example:


class B : public Object 
{ 
public: 
  B(){ cout << hex << (long)this << " B constructor!" << endl; } 
  ~B(){ cout << hex << (long)this << " B destructor!" << endl; } 
  virtual ClassInfo* GetClassInfo() const{ return &ms_classinfo; } 
  static Object* CreateObject() { return new B; } 
protected: 
  static ClassInfo ms_classinfo; 
}; 
ClassInfo B::ms_classinfo("B", B::CreateObject); 

To use it, we simply call Object::CreatObject(string) and pass in the class name.


int main() 
{ 
  Object* obj = Object::CreateObject("B"); 
  delete obj; 
  return 0; 
} 

Basically, the reflection mechanism works, and it is easy to maintain later extensions using callback registration.

3. Use macros to simplify code:

In fact, you see, because we're going to make our class reflective we're going to have to satisfy the three requirements above, but each class is going to have to write something similar. Look carefully 1, including function application da's, function definition, function registration, the code of every class except the class name is 1 mode 1, is there a simple method?
That's using macros.


class Object 
{ 
public: 
  virtual string ToString() = 0; 
}; 
0

With macro substitution, we define a new class.

Simply add IMPLEMENT_CLASS(classname) to the DECLARE_CLASS(classname) implementation in the class definition to make the class reflect.

For example, our class B above can be written like this:


class Object 
{ 
public: 
  virtual string ToString() = 0; 
}; 
1

In this way, no matter what functions need to be added or modified later, you only need to modify the macro instead of adding and modifying methods for each class.

At this point, ok is basically finished with the implementation of c++ reflection! .

Thank you for reading, I hope to help you, thank you for your support of this site!


Related articles: