The C + + reflection mechanism that I understand

Source: Internet
Author: User
Tags class definition

1. Preface

In the actual project, hear brother said C + + used in the reflection, out of curiosity, to consult the relevant information, found that the strong C + + itself does not support reflection, but Java support reflection mechanism. When I learned this fact, I have been the only C + + took direction me, heart secretly tears, lament unceasingly. But, C + + fans do not feel uncomfortable, strong C + + itself does not support, but can let us manually realize, really is a winding place, chanfang flowers and trees deep. C + + is a hot pillow and chase that will not disappoint us to its death.

However, when it comes to the reflection mechanism of Java or the use of C + + for reflection, if not really used in the project, we will feel strange and puzzled. We ask what reflection is, what is the role of reflection, and what is the application scenario of reflection? When you look at a lot of information, there may be doubts, or is the official terminology of the confusion, do not know the cardinal, or is the author of the poor writing and unfamiliar application scene confused, unintelligible. Let me take a simple application scenario as a starting point for explaining the practical usefulness of the C + + reflection mechanism. When we encounter problems, we can explore the solution of the problem and solve the problem, we learn new knowledge. This is what we often use, accustomed to learning patterns, in line with ordinary people learning habits. If, we do not know what the reflection can solve the problem, or our work in the practice of the problems encountered without reflection to solve, then we painstakingly to learn this infrequently used things, what is the meaning of it?

So, here's a question: How to generate a Class object by its name string. For example, if you have a class ClassA, how do you generate the object of a class by using the class name string "ClassA"?

Reflection is often used in Java programming, but I think a lot of people who use C + + have not thought about it yet. C + + is not supported by the class name string "Classxx" to generate the object, that is, we can use ClassXX* object =new ClassXX; to build the object, but not through ClassXX* object=new "ClassXX"; to build the object.

So how do we solve this problem? We can solve this problem by reflection. Find the problem, find the reflection can solve the actual problem, is not feel full of motivation and expectations to learn how C + + how to achieve reflection to solve this problem?

Well, we know that reflection can solve this problem, but what is reflection? We have a Baidu encyclopedia more official definition: reflection is the ability of a program to access, detect, and modify its own state or behavior. It's a little abstract, and I understand it. In the process of running the program, you can create an object from the class name and get the member variables and methods declared in the class.

So, how do we solve the problems raised above? Here we will slowly explain the implementation of the reflection in C + + to solve the above problem.

2. Specific design and implementation 2.1 design ideas

My design idea is roughly like this.
(1) Define a callback function that creates the class object for the class that needs reflection;

(2) design a factory class with a std::map that holds the class name and the callback function that created the instance. Dynamic creation of class objects through class factories;

(3) When the program starts running, the callback function is stored in the Std::map (hash table), and the class name is the key value of the map;

The implementation process is as follows:

2.2 Concrete implementations

Next, I will explain the implementation of the concrete step-by-step method.

The first step: define a function pointer type that points to the callback function that creates the class instance.

typedefvoid* (*PTRCreateObject)(void);  

Step Two: define and implement a factory class to hold the callback function that holds the class name and creates the class instance. The function of the factory class is simply to save the class name and the callback function that creates the class instance, so there is no need for multiple instances of the factory class for the entire proof period of the program, so a singleton pattern is used to refer to the factory class.

//Definition of factory classclassclassfactory{Private: Map<string, ptrcreateobject>M_classmap; ClassFactory () {};//Constructor Privatization Public:void* Getclassbyname (stringClassName);voidRegistclass (stringName, Ptrcreateobject method);Staticclassfactory& getinstance (); };//Implementation of factory class//@brief: Gets a single instance object for the factory classclassfactory& classfactory::getinstance () {StaticClassFactory slo_factory;returnSlo_factory; }//@brief: Gets an instance of the class through the class name stringvoid* Classfactory::getclassbyname (stringClassName) { Map<string, ptrcreateobject>:: Const_iterator iter; iter = M_classmap.find (className);if(iter = M_classmap.end ())returnNULL;Else          returnIter->second (); }//@brief: Saves the given class name string and the corresponding function that created the class object to the mapvoidClassfactory::registclass (stringName, Ptrcreateobject method) {M_classmap.insert (pair<string, ptrcreateobject> (name, method)); }

The third step: This step is more important, but also the most worthy of a step forward, but also easy to make a confused place, look carefully. Registers the defined class with the factory class. This means that the class name string and the callback function that creates the class instance are saved to the map of the factory class. Here we need to do two more work, the first one is to define a callback function that creates an instance of the class, and the second is to save the class name string and our defined callback function to the map of the factory class. Suppose we define a testclassa.

//test class Aclass TestClassA{public:    voidm_print(){        cout<<"hello TestClassA"<<endl;    };};//@brief:创建类实例的回调函数TestClassA* createObjTestClassA{        returnnew TestClassA;}

All right, we're done. The first job defines a callback function that creates an instance of the class. Let's consider how to save this callback function and the corresponding class name string to the factory class map. One of the things I do here is to create a global variable that, when created, saves the callback function and the corresponding class name string to the factory class map within the constructor of the call. Here, the type of the global variable we define as Registeraction.

//注册动作类class RegisterAction{public:    RegisterAction(string className,PTRCreateObject ptrCreateFn){        ClassFactory::getInstance().registClass(className,ptrCreateFn);    }};

With this registered action class, after each class definition, we create a global object that registers the action class, registering the name and callback function of the class that we define in the map of the factory class by registering the action class's constructor. You can create an object that registers an action class in any of the source files of the program, but here we put it behind the callback function. You'll know why you're doing this later. Create an object that registers an action class as follows:

g_creatorRegisterTestClassA("TestClassA",(PTRCreateObject)createObjTestClassA);   

Here we will complete the registration of the class name and the callback function that created the class instance to the map of the factory class. Let's take another class TESTCLASSB as an example and revisit the above steps:

//test class Bclass TestClassB{public:    voidm_print(){        cout<<"hello TestClassB"<<endl;    };};//@brief:创建类实例的回调函数TestClassB* createObjTestClassB{        returnnew TestClassB;}//注册动作类的全局实例RegisterAction g_creatorRegisterTestClassB("TestClassB",(PTRCreateObject)createObjTestClassB);

Smart you, there is no discovery, if we define a Class C, D ...., we repeat writing a lot of code with very high similarity. So how do we slack off, let the code become concise, improve our coding efficiency. Sometimes we should be lazy, not to say that the world is lazy people to create, of course, these lazy people are very smart. So how do we lazy, if you think of the macro, congratulations, correct. In fact, a closer look, including the definition of the callback function and the definition of the variables of the class that registered the action, the code of each class is identical except for the class name, so we can replace the duplicate code with the following macro.

#define REGISTER(className)                                             \    className* objectCreator##className(){                              \        returnnew className;                                               }                                                                       RegisterAction g_creatorRegister##className(                        \        #className,(PTRCreateObject)objectCreator##className)

With the above macro, we can write a simple after each class to REGISTER(ClassName) complete the function of registration, is not very convenient and fast!!!

2.3 Testing

At this point, we have completed the C + + reflection part of the function, why is part of the function, followed by another explanation. Impatient, let us test first, whether or not to solve the problem we mentioned above: How to generate the class object by the name string of the class . The test code is as follows:

 #include <map>  Span class= "Hljs-preprocessor" > #include <iostream>   #include < String>  using  namespace  STD ; //test class  class  Testclass{public : void  m_print () {cout  << "Hello TestClass"  <<endl; };}; REGISTER (TestClass); int  Main (int  argc, char  * argv[]) {testclass* ptrobj= (testclass*) classfactory::getinstance ().    Getclassbyname ( "TestClass" ); Ptrobj->m_print ();}  

Program compile run output:

2.4 Questions that may exist

Look at the above test code, we may be very sad, we are in the class name string to create the class instance, we still need to use class name for coercion type conversion, with the class name, why should we also deliberate to implement the function of reflection, directly with the class name to create an instance of the line?

In fact, the reflection implemented above only solves the problem originally raised in this article. Then in the actual project, there is another application scenario is that we have defined the base class, to customer inheritance, but we do not know the customer inherits the base class after the type name. We can use the configuration file to describe the specific type name that the customer implements, so that we can create an instance of the customer custom class through the class name string.

3. Are there any other methods of registration?

This is specifically explained by implementing a C + + reflection to achieve an instance of a class created by a class name string. When registering a class that needs reflection, we use a global variable that registers the action class to help us achieve the function of registration. In addition to this method, there is no other way? You can think about it. If you have any ideas, please leave a message.

Carefully, we register the function of the created instance of the class with the constructor of the global object into the factory class, in fact, we are taking advantage of the initialization of the global object to execute the constructor is executed before the program enters the main function, this problem can be abstracted to C + + in the main () function before executing a statement?

There are several main ways to do this:
(1) A constructor for a global variable.
That is, the constructor of the global object described above to implement the desired operation before the main function. But the obvious side effect is to define a global variable that is not used, from birth, to complete the mission, to be ruthlessly abandoned by us.

(2) The assignment function of the global variable.
Similar to the above method, but also has the above side effect. Refer to the following code:

 #include < Iostream>  using  namespace  STD ; int  foo (void ); int  I=foo (); int  foo (void ) {    cout  << "before main"  <<endl; return  0 ;} int  Main (void ) { cout  << "I ' m main"  <<endl;} Zenzen Link: http://www.zhihu.com/question/26031933/answer/40612536  Source: Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source. 

(3) If you use GCC, you can declare constructor and destructor by the attribute keyword to specify that the function executes before and after the main function, respectively.

 #include <stdio.h> __attribute (constructor )) void  before_main   ()  {printf ("%s/n", _  _FUNCTION__); }  __attribute   (( destructor ) ) void  after_main   ()  {printf ("%s/n", __function__); }  int  main   (int      ARGC, char * * argv)  {printf ("%s/n", __function__);  return 0; }  

(4) Specify the entry point, call the original entry point in the entry point
when compiling a C program using GCC, we can use linker to specify the portal, using the compile option-E to indicate the program entry function.

//test.c   #include <stdio.h>  int  Main ( int  argc, char  **argv) {printf  (); return  0 ;} int  Xiao (int  argc, char  **argv) {printf  (     "xiao\n" ); return  Main (argc, argv);} The compile statement can be: gcc-e xiao test.c Kanki mo Link: http://www.zhihu.com/question/26031933/answer/31872780  Source: Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source. 

The above is the method that the user proposed, but when I am in the test, run into the main function, there will always be a segment error. C + + programs, use g++ to do the same, compile can pass, but also execute to the main function is thrown segmentation fault (core dumped). Interested readers can try it, when compiling, remember to add the extern "C" description to the new entry function, in case the g++ compiler changes the function signature. If it is resolved, please leave a message to inform.

(5) You can call main in main to implement a code before main, as follows:

#include <stdio.h>#include <stdbool.h>intMainintargcChar**ARGV) {Static _boolFirsttime =true;if(Firsttime) {Firsttime =false;printf("Before main\n");returnMain (argc, argv); }printf("main\n");return 0;} Kanki Link: http://www.zhihu.com/question/26031933/answer/31872780Source: Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
4. Summary

Here is an explanation of the question raised in section 2.3 above, why we just completed the C + + reflection of the partial function, because we do not have a complete implementation of the C + + reflection mechanism, can only implement a reflection mechanism of a small function module, that is, the class name string to create an instance of the class. In addition, as far as I know, the reflection mechanism of the programming language can realize the function of getting the properties and methods in the class through the class name string, modifying the access rights of properties and methods, and so on.

Why do we need a reflection mechanism? Due to the successful application of Java and. NET, the reflective technology has been developed from theory and technology to practicality by its clear separation of information from the structure, behavior and information of the system, and the establishment of a dynamically manipulated causal association to dynamically adjust the good characteristics of the system behavior. So that the dynamic acquisition and adjustment of system behavior has a solid foundation. When you need to write more extensible code and deal with objects that are not identified in the program design, the reflection mechanism will show its power, such as:
(1) serialization (serialization) and databinding (data binding).
(2) Remote method invocation (invocation RMI).
(3) object/relational data mapping (E/R Mapping).

Many of the current popular frameworks and tools, such as Castor (Java-based data binding tool), Hibernate (Java-based object/relational mapping framework), and so on, use the reflection mechanism to dynamically obtain type information. Therefore, the ability to dynamically acquire and manipulate type information has become one of the hallmarks of modern software.

The reflection mechanism is so complex that C + + is not yet supported, and it is a fragment of my sanjiaojiuliu and a few snippets of code that I can sketch.

The following is attached to the complete code used in this article, are written in a source file, you can according to the actual application, say different functions of the code written in different files. On the basis of this, the function can be expanded and improved.

#include <map>#include <iostream>#include <string>using namespace STD;typedef void* (*ptrcreateobject) (void);classclassfactory{Private: Map<string, ptrcreateobject>M_classmap; ClassFactory () {};//Constructor Privatization Public:void* Getclassbyname (stringClassName);voidRegistclass (stringName, Ptrcreateobject method);Staticclassfactory& getinstance (); };void* Classfactory::getclassbyname (stringClassName) { Map<string, ptrcreateobject>:: Const_iterator iter; iter = M_classmap.find (className);if(iter = M_classmap.end ())returnNULL;Else          returnIter->second (); }voidClassfactory::registclass (stringName, Ptrcreateobject method) {M_classmap.insert (pair<string, ptrcreateobject> (name, method)); } classfactory& classfactory::getinstance () {StaticClassFactory slo_factory;returnSlo_factory; }classregisteraction{ Public: Registeraction (stringClassname,ptrcreateobject ptrcreatefn) {classfactory::getinstance (). Registclass (CLASSNAME,PTRCREATEFN); }};#define REGISTER (className) \classname* Objectcreator# #className () {\        return NewClassName; } registeraction G_creatorregister# #className (\        #className, (ptrcreateobject) objectcreator# #className)//test classclasstestclass{ Public:voidM_print () {cout<<"Hello TestClass"<<endl; };}; REGISTER (TestClass);intMainintargcChar* argv[]) {testclass* ptrobj= (testclass*) classfactory::getinstance (). Getclassbyname ("TestClass"); Ptrobj->m_print ();}
Reference documents

[1] Implementation of C + + reflection mechanism
[2] A simple implementation of the C + + reflection mechanism. [J]. Baoliang, Chen Ping. Computer Engineering, 2006, 32 (16): 95-96
[3]http://www.zhihu.com/question/26031933

The C + + reflection mechanism that I understand

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.