Object-oriented topics for C and C + + (6)--c++ can also reflect

Source: Internet
Author: User

List of articles in this column

First, what is object-oriented

Second, C language can also achieve object-oriented

Third, the non-elegant features in C + +

Iv. solve the package and avoid the interface

V. Rational use of templates to avoid code redundancy

VI, C + + can also reflect

Vii. single-Case pattern solving the construction order puzzle of static member objects and global objects

Viii. more advanced preprocessor PHP

VI, C + + can also reflect

Today we are to discuss the reflection of C + +, the lack of reflection mechanism has always been the big problem of C + +, a lot of system design, need according to the definition of external resource files, dynamic call internal functions and interfaces, if there is no reflection, it will be difficult to convert the external data into internal methods.

The reflection mechanism of Java and. NET is easy to implement, and because of the nature of its dynamic language, a large amount of metadata is stored at compile time, and in dynamic loading, it is the module that is loaded according to these metadata. Because C + + lacks this information, it often does not have good dynamic loading and linking. In order to implement the dynamic loading function of C and C + +, the dynamic link library is designed, the symbol table is saved in the dynamic library, the code is relocated at runtime, and then the link operation is done. This is implemented by the operating system, and is not very well used in user engineering, so it is necessary for us to build a set of metadata , to preserve the content required for reflection.

The principle of reflection

The core of reflection is to create the corresponding class based on the string name, or call the corresponding class method, for which we use the map in C + +

    std::map<std::string, meta_class*>

Meta_class is a class that holds key metadata in a class and can support reflection constructs, reflection call functions, and so on.

Meta_func is the key information class that holds a method, but because of the variable parameters and return type of the method, we use the template to convert an abstract stored member function pointer to a member function pointer of the type that we determine, and then to invoke, to achieve the purpose of the dynamic invocation:

    T, typename R, typename... Args>    R Call(T* that, Args... args) {        R (T::*mp)();        mp = *((R (T::**)())func_pointer);        return (that->*mp)(args...);    }

The code here is very confusing, if you have not learned the function pointers of C, it is recommended that you go to the definition and use of function pointers first.

Here is the passing of the member function pointer, which will explain in detail how to pass any of the function pointers in a moment.

Reflection Class Object

First of all, we must establish a Meta_class model for the class object, but each meta_class should be able to construct objects of this class, in order to achieve this, we think of the template:

public IMetaClass {public:    virtualvoidCreateObject() {        returnnew T();    }};

In order for each class to have a uniform creation method, we will use the Imetaclass interface for polymorphic invocation

classImetaclass { Public:Virtual void* CreateObject () =0;Template<TypeNameT> t* Create () {return(t*)    CreateObject (); }voidAddfunc (Const STD::stringS, metafunc* c) {func_map[s] = C;} metafunc* Getfunc (Const STD::strings) {if(Func_map.find (s)! = Func_map.end ())returnFunc_map[s];Else returnNULL; }Private:STD:: Map<const std::string, metafunc*>Func_map;};

Here we write methods and member functions in the interface class, I think this is the advantage of C + +, unlike Java, for security, but canceled so simple and easy to use functionality.

Interfaces uniformly implement the same method of building class objects, avoiding the difficulty of repeated writing in the implementation class.

This allows us to register the Metaclass object with our class registrar as soon as we define it for each class.

But the question is, how can you write code when the class is defined? Our C and C + + can only invoke the code in the function, and cannot be executed anywhere in the same way as a script.

Executing code with the constructor of a global object

We have found that C + + has a very interesting feature, and some code can be executed before the main function executes:

class test{public:    test() {        printf("%s\n","Hello Ctor!");    }};test show_message();int main(){    printf("Main function run!\n");    return0;}

Execute code, huh? Seems to be not right, it seems that our object does not start, it is possible that the compiler has been optimized out ... = =!
Display of the console:

sxf@sxf-PC:~/data/workspace/C++/OObyCpp/testCppRunCode$ ./mainMain function run!

Slight change:

#include <cstdio>usingnamespacestd;class test{public:    test(constchar* msg) {        printf("%s\n",msg);    }};test show_message("Hello Ctor!");int main(){    printf("Main function run!\n");    return0;}

OK, we find that the constructor runs before the main function, which is the construction period of our type definition.

sxf@sxf-PC:~/data/workspace/C++/OObyCpp/testCppRunCode$ ./mainHelloCtor!Main function run!

Specifically want to learn more about the operating environment of C + + details, recommended to read an open-source book in English:
"How to make a computer Operating System"
This book explains how to use C + + to develop a small operating system, and in the C + + runtime import process, introduced the C + + global object constructor running process, it can be clearly seen that the main function of C + + in the assembly layer of the call process is this:

Start:    PushEbxStatic_ctors_loop:                  ; Global constructor Initialization   movEBX, Start_ctorsjmp . Test. Body:   Pager[EBX]AddEbx4. Test:CMP ebx, end_ctors JB. Body   PagerKmain; Call the main functionStatic_dtors_loop:                  ; The destructor call of the global object   movEBX, Start_dtorsjmp . Test. Body:   Pager[EBX]AddEbx4. Test:CMP ebx, end_dtors JB. Body

So we can write a class that is designed to register a class:

class reflector_class {public:    reflector_class(constchar* name, IMetaClass* meta_class) {        ClassRegister::Add(name, meta_class);        printf("define class: %s\n", name);    }};

When the object of this class is constructed, it calls a static method in the Classregister class, adding the class name and the class metadata to it

Using macros to define the registration of processing classes

We want each class object to be able to easily find its own meta_class, the simplest way is to add it as its own member, why not use the inheritance mechanism? First, the inheritance is more complex, and the parent class may also have meta_class, we hope each type can easily find Meta_class, then you can build a reflectible macro, let everyone write in class

#ifndef Reflectible#define Reflectible \public:    static IMetaClass* meta_class;private:#endif

To avoid placing on top, the default definition of private for the following member is affected, so it is written as such.

We are writing a macro that lets the user add to the CPP file of the class and actually defines the Meta_class object:

#ifndef ReflectClass#define ReflectClass(class_name) \    new MetaClass< class_name >();     reflector_class class_name##reflector_class( #class_name , class_name::meta_class)#endif

Here we have used two macro tricks:
# # means to connect two symbols together, due to lexical analysis, macros are separated according to the order of the word, if the direct connection, often cause the symbol analysis is unclear.
#something means that the content is expanded into a string of "something data", so we can easily use this macro to convert a macro symbol into a string into a function.

Reflection member functions

First write a template class that can invoke the member function, and, based on our reflection principle, convert a function pointer to a pointer to a member function:

class Metafunc{public:Metafunc(void* p){func_pointer = p;} void Setfuncpointer(void* p){func_pointer = p;} Template <typenameT, TypeNameR, TypeName ...Args>R Pager(T* that, Args... args){R (T::*MP)(); MP = *((R (T::* *)()) func_pointer); Return(that->*MP)(args...); }private:void* Func_pointer;};

I used the new c++11 feature, a variable parameter template, which makes it easier to accept the target parameters.
If we take the address directly to the member function, we return a member function pointer such as Return_type (ClassName::*) (args).
Note that member function pointers cannot be passed directly, because member function pointers contain a lot of other data information and cannot be coerced into void*, an obvious example is that member function pointers tend to be larger and the largest pointers can even reach 20byte.

To be able to pass a function pointer, we can assign a member function pointer to an object of that member function pointer type, and then take the address of the pointer object

    auto p = &test::print;    &p  //这个地址可以被轻松传递

The address is a pointer to the return_type (ClassName::**)(args) pointer
So there's a way to force type conversions in our previous code.

Define functions using variable-parameter functions of the C language

We're going to pass the address now, but we don't know how many functions are in each class, so we're going to use a C-language macro to handle the mutable arguments.

The following will be modified Reflector_class to support multiple parameters

classReflector_class { Public: Reflector_class (Const Char* Name, imetaclass* meta_class, ...) {Classregister::add (name, Meta_class);printf("Define class:%s\n", name);        Va_list ap; Va_start (AP, Meta_class); for(intarg = Va_arg (AP,int); Arg! =-1; arg = Va_arg (AP,int) )         {STD::stringName (Va_arg (AP,Const Char*));void* p = va_arg (AP,void*);if(arg = =0) {printf("\tdefine func:%s\n", Name.c_str ()); metafunc* f =NewMetafunc (P);            Meta_class->addfunc (name, f); }Else{printf("\tdefine prop:%s\n", Name.c_str ());      }} va_end (AP); }};
va_list ap; 可变参数列表va_start(ap, meta_class);  这里的第二个参数,是当前函数的最后一个固定参数位置void* p = va_arg(ap, void*); 可以用来获得一个固定类型的参数

Release Resources after use:

va_end(ap);  

To support both function and attribute declarations, we define the following macros:

#ifndef Defreflectfunc#define DEFREFLECTFUNC (class_name, func_name) \Auto func_name# #_function_pointer = &class_name::func_name#endif#ifndef Reflectclass#define REFLECTCLASS (class_name) \imetaclass* class_name:: Meta_class =Newmetaclass< class_name > (); Reflector_class class_name# #reflector_class (#class_name, Class_name::meta_class,#endif#ifndef Reflectfunc#define REFLECTFUNC (func_name) \    0, #func_name, _f (func_name# #_function_pointer),#endif#ifndef Reflectprop#define REFLECTPROP (prop_names) \    1, #prop_names, _f (Prop_names),#endif#ifndef _f#define _F (x) reinterpret_cast<void*> (&x)#endif#ifndef End#define END-1)#endif

This allows us to use these macros in CPP only if:

DefReflectFunc(test2,print2);ReflectClass(test2)    ReflectFunc(print2)End;
Global registration Challenges for classes

OK, the key parts are already clear, but at the moment we still lack a very important class, that is, the global registrar of the class.

class ClassRegister{public:    staticstd::map<const std::string, IMetaClass*> class_map;    staticvoid Add(conststd::string s, IMetaClass* k) {        class_map[s] = k;    }}

But this class has a serious flaw that causes the program to crash, and in the following chapters, we'll cover the cause of this embarrassing problem.

Object-oriented topics for C and C + + (6)--c++ can also reflect

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.