C + + reflection mechanism: variable parameter templates for C + + reflection

Source: Internet
Author: User
Tags thread class variadic

1. Overview

This article describes a method for implementing the C + + reflection mechanism through C + + variadic templates. This method is very practical, and it is widely used in Nebula High Performance network framework, which realizes the dynamic dynamic load creation function. Nebula The warehouse address of the framework in Coding.net.

The new feature of c++11-the variable template parameter (variadic templates) is one of the most powerful features of C++11, which is highly generalized for parameters that represent 0 to any number of parameters of any type. The principle and application of variable parameter templates is not the focus of this article, but the examples in this article also give you a good idea of how variadic templates are applied.

People who are familiar with Java or C # should all know the reflection mechanism, and many well-known frameworks use reflection, and a simple understanding is to create instances of classes based only on the name (string) of the class. C + + does not provide the reflection mechanism directly from the language to us, but the omnipotent C + + can use some trick to realize the reflection. Bwar is also in the development of the nebula framework of the need to use the reflection mechanism, on-line reference to some data combined with their own understanding of the c++11 variable parameter template to achieve C + + reflection.

<br/>

2. The analog reflection mechanism prior to C++11 implementation

Nebula Framework is a high performance event-driven general network framework, the framework itself has no business logic implementation, but for the rapid implementation of the business provides a powerful function, unified interface. The business logic is compiled into so dynamic library from Nebula's actor interface, Nebula loads the business logic dynamic Library to implement various functions server, the developer only needs to focus on business logic code writing, network communication, timer, data serialization deserialization, All of the communication protocols are used to complete the framework.

The business logic classes written by the developer derive from the base class of nebula, but the various business logic derived classes are completely unknown to nebula, and nebula need to load these dynamic libraries and create class instances in the dynamic library to use the reflection mechanism. The first version of Nebula and its predecessor Starship Framework developed with C++03 Standard, unknown class name, unknown parameter number, unknown parameter type, more unknown argument, Bwar didn't think of a valid way to load the dynamic library and create the class instance. To do this, the constructors for all business logic ingress classes are designed as parameterless constructors.

Bwar did not find a better implementation in 2015 years, he thought of a more trickery way to load a dynamic library and create a class instance (this is not a reflection mechanism, but a function that can or need a reflection mechanism to implement). This method is applied and measured in the c++03 version of Nebula, and it is also applied in an IM bottom frame starship with stable operation for more than two years. The code for this implementation is given below:

CMDHELLO.HPP:

#ifdef __cplusplusextern "C" {#endif// @brief 创建函数声明// @note 插件代码编译成so后存放到plugin目录,框架加载动态库后调用create()创建插件类实例。neb::Cmd* create();#ifdef __cplusplus}#endifnamespace im{class CmdHello: public neb::Cmd{public:    CmdHello();    virtual ~CmdHello();    virtual bool AnyMessage();};} /* namespace im */

CmdHello.cpp:

#include "CmdHello.hpp"#ifdef __cplusplusextern "C" {#endifneb::Cmd* create(){    neb::Cmd* pCmd = new im::CmdHello();    return(pCmd);}#ifdef __cplusplus}#endifnamespace im{CmdHello::CmdHello(){}CmdHello::~CmdHello(){}bool CmdHello::AnyMessage(){    std::cout << "CmdHello" << std::endl;    return(true);}}

The key to the implementation is the Create () function, although each dynamic library writes the Create () function, but the address of each create () function is different when the dynamic library is loaded, and the functions that are called by various addresses are not the same, so you can create different instances. The following shows the dynamic library loading and calling the Create () function to create the code snippet for the class instance:

void* pHandle = NULL;    pHandle = dlopen(strSoPath.c_str(), RTLD_NOW);    char* dlsym_error = dlerror();    if (dlsym_error)    {        LOG4_FATAL("cannot load dynamic lib %s!" , dlsym_error);        if (pHandle != NULL)        {            dlclose(pHandle);        }        return(pSo);    }    CreateCmd* pCreateCmd = (CreateCmd*)dlsym(pHandle, strSymbol.c_str());    dlsym_error = dlerror();    if (dlsym_error)    {        LOG4_FATAL("dlsym error %s!" , dlsym_error);        dlclose(pHandle);        return(pSo);    }    Cmd* pCmd = pCreateCmd();

The configuration file that corresponds to this dynamic library load code fragment is as follows:

{"cmd":10001, "so_path":"plugins/CmdHello.so", "entrance_symbol":"create", "load":false, "version":1}

The code implementation achieves the purpose of loading the dynamic library and creating an instance of the framework's unknown class. However, there is no reflection mechanism so flexible, it is also a bit cumbersome to use, developers have to write a business logic class also need to implement a corresponding global create () function.

3. Reflection mechanism Realization of C + +

Bwar used a C + + template to encapsulate a generic thread class based on pthread. The following is a representative function implementation of the thread template class, which has some inspirations for designing the C + + reflection mechanism:

' C + +
template <typename t>
void cthread<t>::startroutine (void para)
{
T
PT;
PT = (t*) para;
Pt->run ();
}

&emsp;&emsp; Similarly, creating an unknown class instance can be implemented by means of the new T (), and if it is a constructor with parameters, it can be implemented by new T (T1, T2). Then, creating the class instance through the class name, establishing the correspondence between "ClassName" and t, or establishing the corresponding relationship between "ClassName" and the function containing the new T () statement can realize the reflection mechanism of the non-parametric structure class. Considering that the number of parameters and parameter types of new T (T1, T2) are unknown, the variable parameter template of c++11 is used to solve the parameter problem, that is, to complete the reflection mechanism of the class with parameter construction. # # 4. Realization &emsp;&emsp; Research of C + + reflection mechanism in Nebula network framework the most important application of the C + + reflection mechanism is that the [Nebula] (Https://github.com/Bwar/Nebula) network framework has extremely important applications, While the [Nebula] (Https://github.com/Bwar/Nebula) framework implemented and applied the reflection Mechanism, the code volume was reduced by about 10%, while ease of use was greatly improved, taking into account the application of the reflection mechanism to the [Nebula] (https ://github.com/bwar/nebula The benefits of business logic development, it can be said that the reflection mechanism is the [Nebula] (Https://github.com/Bwar/Nebula) framework that is second only to the significant elevation of the C++14 standard rewrite. &emsp;&emsp; The actor of Nebula is the event (message) processor, all business logic is abstracted into event and event processing, and the reflection mechanism is applied to the dynamic creation of actor. Actors are divided into four different types of cmd, Module, Step, session. Business logic code is implemented by deriving subclasses from these four different types of time handlers, focusing on business logic implementations without having to focus on content outside of the business logic. Both CMD and module are message-handling, and business developers define what kind of cmd and module are unknown to the framework, so the CMD and module are configured in the config file, Nebula completes their instance creation by using the CMD and module names (strings) in the configuration file. The key code for dynamically creating the actor through the reflection mechanism is as follows: [Actor class] (HTTPS://GITHUB.COM/BWAR/NEBULA/BLOB/MASTER/SRC/ACTOR/ACTOR.HPP): "' C++class Actor:public Std::enable_shaRed_from_this<actor> 

Actor Creation Factory (note the code comment):

"' C + +
Template<typename ... Targs>
Class Actorfactory
{
Public
Static actorfactory* Instance ()
{
if (nullptr = = m_pactorfactory)
{
M_pactorfactory = new Actorfactory ();
}
return (m_pactorfactory);
}

virtual ~ActorFactory(){};// 将“实例创建方法(DynamicCreator的CreateObject方法)”注册到ActorFactory,注册的同时赋予这个方法一个名字“类名”,后续可以通过“类名”获得该类的“实例创建方法”。这个实例创建方法实质上是个函数指针,在C++11里std::function的可读性比函数指针更好,所以用了std::function。bool Regist(const std::string& strTypeName, std::function<Actor*(Targs&&... args)> pFunc);// 传入“类名”和参数创建类实例,方法内部通过“类名”从m_mapCreateFunction获得了对应的“实例创建方法(DynamicCreator的CreateObject方法)”完成实例创建操作。Actor* Create(const std::string& strTypeName, Targs&&... args);

Private
Actorfactory () {};
static actorfactory<targs...> m_pactorfactory;
Std::unordered_map<std::string, Std::function<actor
(targs&& ...) > > m_mapcreatefunction;
};

Template<typename ... Targs>
actorfactory<targs...>* actorfactory<targs...>::m_pactorfactory = nullptr;

Template<typename ... Targs>
BOOL Actorfactory<targs...>::regist (const std::string& strtypename, std::function<actor* (Targs& &, .... args) > PFunc)
{
if (nullptr = = PFunc)
{
return (false);
}
BOOL Breg = M_mapcreatefunction.insert (
Std::make_pair (Strtypename, PFunc)). Second;
return (Breg);
}

Template<typename ... Targs>
actor* actorfactory<targs...>::create (const std::string& strtypename, Targs&& ....) args)
{
Auto iter = M_mapcreatefunction.find (strtypename);
if (iter = = M_mapcreatefunction.end ())
{
return (nullptr);
}
Else
{
return (Iter->second (std::forward<targs> (args)));
}
}

[Dynamic creation of classes] (HTTPS://GITHUB.COM/BWAR/NEBULA/BLOB/MASTER/SRC/ACTOR/DYNAMICCREATOR.HPP) (note the code comment): "C++template<typename T , TypeName ...  Targs>class dynamiccreator{public:struct Register {Register () {char* szdemanglename =            Nullptr std::string strtypename; #ifdef __gunc__ szdemanglename = Abi::__cxa_demangle (typeID (T). Name (), nullptr, nullptr , nullptr); #else//NOTE: Here are different compiler typeid (T). Name () The string returned is not the same, you need to write the corresponding implementation for the compiler//in this format?: Szdeman            Glename = typeID (T). Name (); Szdemanglename = Abi::__cxa_demangle (typeID (T). Name (), nullptr, nullptr, nullptr); #endif if (nullptr! = Szdeman                Glename) {strtypename = Szdemanglename;            Free (szdemanglename);        } actorfactory<targs...>::instance ()->regist (Strtypename, CreateObject);    } inline void do_nothing () const {};    }; Dynamiccreator () {m_oregister.do_nothing();    The function call here has no actual content, but is the key to the creation of the M_oregister instance before invoking the dynamic creation function. Virtual ~dynamiccreator () {}; A method of dynamically creating an instance that all actor instances are created by this method.    This is a template method, in fact each actor's derived class corresponds to its own CreateObject method.        Static t* CreateObject (Targs&& ... args) {t* PT = nullptr;        try {PT = new T (std::forward<targs> (args) ...);        } catch (std::bad_alloc& e) {return (nullptr);    } return (PT); }private:static Register M_oregister;}; Template<typename T, TypeName ... Targs>typename dynamiccreator<t, Targs...>::register dynamiccreator<t, Targs...>::m_oRegister;

The above actorfactory and Dynamiccreator are all implementations of the C + + reflection mechanism. To complete the dynamic creation of an instance, you also need the class definition to meet the (template) requirements. Here's a cmdhello<a name= "Cmdhello" ></a> class definition that you can create instances dynamically (note the code comment):

"' C + +
A class definition requires the use of multiple inheritance.
The first inheritance neb::cmd is the actual base class of Cmdhello (Neb::cmd is the actor's derived class, what actor is described in the description at the beginning of this section);
The second inheritance is the need to dynamically create instances by class name, with Template<typename T, TypeName ... Targs> class Dynamiccreator definition It is easy to understand that the first template parameter (Cmdhello) is the class name to be dynamically created, and the other parameters are the constructor parameters of the class.
If the parameter is a pointer or reference of a type, it should be specified to the type as a template parameter. For example: The parameter type const std::string& only needs to fill std::string in the template parameter of the NEB::D ynamiccreator.
Class Cmdhello:public Neb::cmd, public neb::D Ynamiccreator<cmdhello, int32>
{
Public
Cmdhello (Int32 iCmd);
Virtual ~cmdhello ();

virtual bool Init();virtual bool AnyMessage(                std::shared_ptr<neb::SocketChannel> pChannel,                const MsgHead& oMsgHead,                const MsgBody& oMsgBody);

};

&emsp;&emsp;再看看上面的反射机制是[怎么调用](https://github.com/Bwar/Nebula/blob/master/src/labor/WorkerImpl.inl)的:``` C++template <typename ...Targs>std::shared_ptr<Cmd> WorkerImpl::MakeSharedCmd(Actor* pCreator, const std::string& strCmdName, Targs... args){    LOG4_TRACE("%s(CmdName \"%s\")", __FUNCTION__, strCmdName.c_str());    Cmd* pCmd = dynamic_cast<Cmd*>(ActorFactory<Targs...>::Instance()->Create(strCmdName, std::forward<Targs>(args)...));    if (nullptr == pCmd)    {        LOG4_ERROR("failed to make shared cmd \"%s\"", strCmdName.c_str());        return(nullptr);    }    ...}

The invocation of the Makesharedcmd () method:

"' C + +
Makesharedcmd (nullptr, ocmdconf["cmd"]i, ICMD);

&emsp;&emsp; at this point through C + + variable parameter template implementation of C + + reflection mechanism has all finished, I believe read here has a certain understanding, this is [Nebula] (Https://github.com/Bwar/Nebula) One of the core functions of the framework, many of which are based on [Nebula application Practices] (Https://github.com/Bwar/NebulaBootstrap), is a C + + reflection implementation that can be used for production. &emsp;&emsp; the error-prone application of this C + + reflection mechanism is * class definition class Cmdhello:public Neb::cmd, public neb::D Ynamiccreator<cmdhello , the template parameters in the int32> must match the parameter types in the constructor strictly (do not understand, read again [Cmdhello class definition] (#CmdHello)). * The type of argument passed in where the method was created must match the formal parameter type exactly. There cannot be an implicit type conversion, such as a class constructor with a formal parameter type of unsigned int, calling actorfactory<targs...>::instance ()->create () An argument that is passed in as int or short or unsigned short or enum causes actorfactory not to find the corresponding instance creation method, which causes the instance to not be created properly through the class name. &emsp;&emsp; note the above two points, the basic will not have any problem. # # 5. An executable example &emsp;&emsp; the code above to illustrate the C + + reflection mechanism is all derived from the [Nebula] (https://github.com/Bwar/Nebula) framework. Finally, provide an executable example to deepen understanding. DynamicCreate.cpp: ' C + + #include <string> #include <iostream> #include <typeinfo> #include < memory> #include <unordered_map> #include <cxxabi.h>namespace neb{class actor{public:actor () {std:: cout << "Actor construCT "<< Std::endl;}    Virtual ~actor () {};    virtual void Say () {std::cout << "Actor" << Std::endl; }};template<typename ...    Targs>class actorfactory{public://typedef actor* (*actorcreatefunction) ();    std::function< actor* (Targs...args) > pp;        Static actorfactory* Instance () {std::cout << "static actorfactory* Instance ()" << Std::endl;        if (nullptr = = m_pactorfactory) {m_pactorfactory = new actorfactory ();    } return (m_pactorfactory);    } virtual ~actorfactory () {}; Lambda:static std::string readtypename (const char * name)//bool regist (const std::string& Strtypename, ActorCre Atefunction pFunc)//bool regist (const std::string& strtypename, std::function<actor* () > PFunc) bool Regis T (const std::string& Strtypename, std::function<actor* (targs&&. args) > PFunc) {std::cout &L t;< "BOOL Actorfactory::regist (const std::string& Strtypename, std::function<actor* (Targs ... args) > PFunc) "<< Std::endl;        if (nullptr = = PFunc) {return (false);        } std::string strrealtypename = Strtypename; [&strtypename, &strrealtypename]{int iPos = Strtypename.rfind ('); strrealtypename = Std::move (        Strtypename.substr (Ipos+1, Strtypename.length ()-(IPos + 1));};        BOOL Breg = M_mapcreatefunction.insert (Std::make_pair (Strrealtypename, PFunc)). Second;        Std::cout << "m_mapcreatefunction.size () =" << m_mapcreatefunction.size () << Std::endl;    return (Breg); } actor* Create (const std::string& strtypename, Targs&&. args) {std::cout << actor* Act        Orfactory::create (const std::string& strtypename, Targs ... args) "<< Std::endl;        Auto iter = M_mapcreatefunction.find (strtypename);        if (iter = = M_mapcreatefunction.end ()) {return (nullptr); } ELSE {//return (Iter->second ());        Return (Iter->second (std::forward<targs> (args)));    }}private:actorfactory () {std::cout << "actorfactory construct" << Std::endl;};       Static actorfactory<targs...>* m_pactorfactory; Std::unordered_map<std::string, std::function<actor* (targs&& ...) > > m_mapcreatefunction;}; Template<typename ... targs>actorfactory<targs...>* actorfactory<targs...>::m_pactorfactory = nullptr;template< TypeName T, TypeName ... Targs>class dynamiccreator{public:struct Register {Register () {std::cout << "Dy            Namiccreator.register construct "<< Std::endl;            char* szdemanglename = nullptr; std::string strtypename; #ifdef __gunc__ szdemanglename = Abi::__cxa_demangle (typeID (T). Name (), nullptr, nullptr    , nullptr); #else//in this format?: Szdemanglename = typeID (T). Name ();        Szdemanglename = Abi::__cxa_demangle (typeID (T). Name (), nullptr, nullptr, nullptr); #endif if (nullptr! =                Szdemanglename) {strtypename = Szdemanglename;            Free (szdemanglename);        } actorfactory<targs...>::instance ()->regist (Strtypename, CreateObject);    } inline void do_nothing () const {};    };        Dynamiccreator () {std::cout << "Dynamiccreator construct" << Std::endl;    M_oregister.do_nothing ();    } virtual ~dynamiccreator () {m_oregister.do_nothing ();}; Static t* CreateObject (Targs&& args) {std::cout << "static actor* Dynamiccreator::createobject        (Targs. Args) "<< Std::endl;    return new T (std::forward<targs> (args) ...);    } virtual void Say () {std::cout << "dynamiccreator Say" << Std::endl; } static Register m_oregister;}; Template<typename T, TypeName ... Targs>typename DyNamiccreator<t, Targs...>::register dynamiccreator<t, Targs...>::m_oregister;class cmd:public Actor,    Public Dynamiccreator<cmd>{public:cmd () {std::cout << "Create Cmd" << Std::endl;}    virtual void Say () {std::cout << "I am CMD" << Std::endl; }};class step:public Actor, Dynamiccreator<step, std::string, int>{public:step (const std::string& StrType,    int iSeq) {std::cout << "Create Step" << strtype << "with seq" << iSeq << Std::endl;}    virtual void Say () {std::cout << "I am Step" << Std::endl; }};class worker{public:template<typename ... targs> actor* createactor (const std::string& strtypename, Targs&& .... args) {actor* p = Actorf        Actory<targs...>::instance ()->create (Strtypename, std::forward<targs> (args) ...);    return (p); }};} int main () {//actor* P1 = actorfactory<std::string, Int>::instanCE ()->create (std::string ("CMD"), std::string ("Neb::cmd"), 1001);    actor* p3 = actorfactory<>::instance ()->create (std::string ("CMD"));    Neb::worker W;    neb::actor* P1 = W.createactor (std::string ("Neb::cmd"));    P1->say ();    Std::cout << Abi::__cxa_demangle (typeID (Worker). Name (), nullptr, nullptr, nullptr) << Std::endl;    Std::cout << "----------------------------------------------------------------------" << Std::endl;    neb::actor* P2 = w.createactor (std::string ("Neb::step"), std::string ("Neb::step"), 1002);    P2->say (); return (0);}

The nebula framework is written in the C++14 standard, with precompiled options in Makefile, which can be compiled with C++11 standards, but compilers that do not fully support c++11 all standards may fail to compile successfully. The measured g++ 4.8.5 does not support variable parameter templates, it is recommended to use the GCC 5.0 compiler, preferably with GCC 6,nebula is gcc6.4.

The example given here DynamicCreate.cpp can be compiled like this:

 g++ -std=c++11 DynamicCreate.cpp -o DynamicCreate

The results of the program execution are as follows:

Dynamiccreator.register constructstatic actorfactory* Instance () actorfactory Constructbool ActorFactory::Regist ( Const std::string& Strtypename, std::function<actor* (Targs ... args) > PFunc) m_mapcreatefunction.size () = 1dynamiccreator.register constructstatic actorfactory* Instance () actorfactory Constructbool ActorFactory::Regist ( Const std::string& Strtypename, std::function<actor* (Targs ... args) > PFunc) m_mapcreatefunction.size () = 1static actorfactory* Instance () actor* actorfactory::create (const std::string& strtypename, Targs ... args) static actor* dynamiccreator::createobject (Targs ... args) Actor constructdynamiccreator constructcreate CmdI am CMD----------------------------------------------------------------------static actorfactory* Instance () actor* Actorfactory::create (const std::string& strtypename, Targs ... args) static actor* Dynamiccreator::createobject ( Targs. args) Actor constructdynamiccreator constructcreate step neb::step with SEQ 1002I am step

Finished, the weekend spent 6 hours to finish writing, found a suitable time to publish. If you feel that this article is useful to you, if you feel nebula can also, trouble to GitHub to give a star, thank you. Nebula is not only a framework, but also a series of applications based on this framework, the goal is to create a high-performance distributed service cluster solution.

<br/>

Resources:

    • C + + Implementation reflection (dynamically creating objects based on class name)
    • The beauty of generalization--c++11 the magic of variable template parameters
    • typeID of C + + standard library

C + + reflection mechanism: variable parameter templates for C + + reflection

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.