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