Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize C++ reflection by variable parameter template of C++ reflection mechanism

2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces "how the variable parameter template of C++ reflection mechanism realizes C++ reflection". In daily operation, I believe many people have doubts about how the variable parameter template of C++ reflection mechanism realizes C++ reflection. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubt of "how to realize C++ reflection by the variable parameter template of C++ reflection mechanism". Next, please follow the editor to study!

1. Summary

   2018 Bwar released the "C++ reflection mechanism: variable parameter template to achieve C++ reflection", the article is very practical, Bwar has seen several read that article in the same way to achieve reflection of the project, also saw a lot of copy from my article even the code style class name function variable name has not changed or just a simple change republished. Being copied shows that it is valuable. If you share it, you don't care about being copied. If you think the article is useful, star Nebula it. Thank you. Most of the projects or articles that use variable parameter templates to achieve reflection use this method to build non-parameter versions of class objects, and the non-parameter version can not fully reflect the real value of variable parameter templates to achieve reflection. About Targ... in the previous article The description of the template parameters is not detailed enough and there is something wrong with the description. This time, write a supplement to this reflection implementation, focusing on the error-prone variable parameters and correcting the errors in the previous article. After all, in the Nebula high-performance network framework, all actor objects must be created in a reflective way, and harnessing this reflection to create objects makes it easier to use Nebula.

two。 Reference folding and type derivation

The variable parameter template of    is mainly realized by the folding of references and its type derivation. With regard to the explanation of citation folding and type derivation, a large amount of information can be found on the Internet, so I will not repeat it here. I recommend a concise and clear article entitled "rules for Type derivation of function template right value reference parameters (Tunable &) Type derivation 11".

3. Review the implementation of C++ reflection mechanism in Nebula network framework

The Actor of    Nebula is the event (message) handler, and all business logic is abstracted into events and event handling. The reflection mechanism is applied to the dynamic creation of Actor. Actor can be divided into four different types: Cmd, Module, Step and Session. The business logic code is implemented by deriving subclasses from these four different types of time processors, focusing on the implementation of the business logic without paying attention to the content outside the business logic. Both Cmd and Module are message processing repositories, and what kind of Cmd and Module the business developer defines is unknown to the framework, so these Cmd and Module are configured in the configuration file, and Nebula completes their instance creation through the names (strings) of Cmd and Module in the configuration file. The key code for dynamically creating an Actor through the reflection mechanism is as follows:

Class declaration for Actor:

Class Actor: public std::enable_shared_from_this

Actor create a factory:

Templateclass ActorFactory {public: static ActorFactory* Instance () {if (nullptr = = m_pActorFactory) {m_pActorFactory = new ActorFactory ();} return (m_pActorFactory);} virtual ~ ActorFactory () {} Register the "instance creation method (DynamicCreator's CreateObject method)" to ActorFactory, and give the method a name "class name" when registering. Later, you can obtain the "instance creation method" of the class through "class name". The instance creation method is essentially a function pointer, and std::function is more readable than function pointers in Clippers 11, so std::function is used. Bool Regist (const std::string& strTypeName, std::function pFunc); / / input "class name" and parameters to create class instance. The method obtains the corresponding "instance creation method (CreateObject method of DynamicCreator)" from m_mapCreateFunction through "class name" to complete the instance creation operation. Actor* Create (const std::string& strTypeName, Targs&&... Args); private: ActorFactory () {}; static ActorFactory* massipActorFactory; std::unordered_map MacromapCreateFunction;}; templateActorFactory* ActorFactory::m_pActorFactory = nullptr;templatebool ActorFactory::Regist (const std::string& strTypeName, std::function pFunc) {if (nullptr = = pFunc) {return (false);} bool bReg = m_mapCreateFunction.insert (std::make_pair (strTypeName, pFunc). Second Return (bReg);} templateActor* ActorFactory::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 (args)...));}}

Dynamically create a class:

Templateclass DynamicCreator {public: struct Register {Register () {char* szDemangleName = nullptr; std::string strTypeName;#ifdef _ _ GNUC__ szDemangleName = abi::__cxa_demangle (typeid (T). Name (), nullptr, nullptr, nullptr) # else / / Note: different compilers typeid (T). Name () return different strings. You need to write the corresponding implementation / / in this format?: szDemangleName = typeid (T). Name () for the compiler; szDemangleName = abi::__cxa_demangle (typeid (T). Name (), nullptr, nullptr, nullptr) # endif if (nullptr! = szDemangleName) {strTypeName = szDemangleName; free (szDemangleName);} ActorFactory::Instance ()-> Regist (strTypeName, CreateObject);} inline void do_nothing () const {};}; DynamicCreator () {m_oRegister.do_nothing () / / although the function call here has no actual content, it is the key} virtual ~ DynamicCreator () {}; / / method to dynamically create m_oRegister instances before calling the dynamic creation function. All Actor instances are created by this method. This is a template method, and each derived class of Actor actually corresponds to its own CreateObject method. Static T* CreateObject (Targs&&... Args) {T * pT = nullptr; try {pT = new T (std::forward (args)...);} catch (std::bad_alloc& e) {return (nullptr);} return (pT);} private: static Register Mirrorog;}; templatetypename DynamicCreator::Register DynamicCreator::m_oRegister

ActorFactory and DynamicCreator on    are all implementations of the C++ reflection mechanism. To complete the dynamic creation of the instance also requires that the class definition must meet the requirements (template). Let's take a look at a CmdHello class definition that can dynamically create an instance:

/ / Class definition requires multiple inheritance. / / the first re-inheritance neb::Cmd is the actual base class of CmdHello (neb::Cmd is a derived class of Actor, and what Actor is described in the description at the beginning of this section); / / the second re-inheritance is the need to dynamically create an instance through the class name. Corresponding to the template class DynamicCreator definition, it is easy to understand that the first template parameter (CmdHello) is the class name to be created dynamically, and the other parameters are the constructor parameters of the class. / / if the parameter is a reference to a type, it should be specified to the type as a template parameter. / / if the parameter is a pointer of a type, it needs to be specified as a pointer of the type when used as a template parameter. Class CmdHello: public neb::Cmd, public neb::DynamicCreator {public: CmdHello (int32 iCmd); virtual ~ CmdHello (); virtual bool Init (); virtual bool AnyMessage (std::shared_ptr pChannel, const MsgHead& oMsgHead, const MsgBody& oMsgBody);}

Note: the two CmdHello comments in the first part of "C++ reflection Mechanism: variable Parameter template to achieve C++ reflection" are wrong. The specific principles can be found in item 5 and 6 below. For example, parameter type const std::string& only needs to enter std::string in the template parameters of neb::DynamicCreator. For example, parameter type const std::string needs to enter std::string in the template parameters of neb::DynamicCreator.

  , take a look at how the reflection mechanism above is called:

Template std::shared_ptr WorkerImpl::MakeSharedCmd (Actor* pCreator, const std::string& strCmdName, Targs&&... Args) {LOG4_TRACE ("% s (CmdName\"% s\ "), _ _ FUNCTION__, strCmdName.c_str (); Cmd* pCmd = dynamic_cast (ActorFactory::Instance ()-> Create (strCmdName, std::forward (args)...); if (nullptr = = pCmd) {LOG4_ERROR (" failed to make shared cmd\ "% s\", strCmdName.c_str ()); return (nullptr) }...}

The call to the    MakeSharedCmd () method:

MakeSharedCmd (nullptr, oCmdConf ["cmd"] [I] ("class"), iCmd); 4. Notes on creating objects with MakeSharedActor series functions

The error-prone aspects of   , the C++ reflection mechanism, are:

The class definition class CmdHello: public neb::Cmd, the template parameters in public neb::DynamicCreator must strictly match the parameter types in the constructor (implicit type conversions are supported).

The argument type passed in where the creation method is called must strictly match the parameter type, and there can be no implicit type conversion. For example, the formal parameter type of the class constructor is unsigned int, and the argument passed in when calling ActorFactory::Instance ()-> Create () is int or short or unsigned short or enum will cause ActorFactory to fail to find the corresponding "instance creation method", resulting in the instance cannot be created normally through the class name. For example, const std::string& and std::string& are of different types. If the call related to MakeSharedActor () passes in std::string&, and the template parameter defines const std::string&, the call will fail.

   pay attention to the above two points, basically there will be no problem.

5. Dynamic creation principle

   draws the above two considerations from a series of dynamic creation use cases, and then looks at the nature of dynamic creation from the code.

5.1 Registration object creation function pointer

   is first created dynamically by calling static T* CreateObject (Targs&&...) in the DynamicCreator template class. Args) function. The DynamicCreator template class creates a static instance in public neb::DynamicCreator:

Templatetypename DynamicCreator::Register DynamicCreator::m_oRegister

   actually creates this static instance for ActorFactory::Instance ()-> Regist (strTypeName, CreateObject); register a function pointer to ActorFactory, which is the actual executor of the subsequent dynamic creation of class objects through reflection. In the CreateObject () function, new is called, and the parameters passed are perfectly forwarded to the constructor of the class, while the real participating formal parameters of the constructor call support implicit type conversion, so the template parameter list when inheriting DynamicCreator does not need to be exactly the same as the type of the class constructor.

5.2 the essence of dynamic creation

The    MakeSharedActor series of functions are called, and the called MakeSharedActor () parameter is perfectly forwarded, and there is no type conversion without the difference between the argument type and the formal parameter type. In MakeSharedActor (), by calling ActorFactory::Instance ()-> Create (strCmdName, std::forward (args)...) After the creation, the call is actually made up of two explicit parts and an implied CreateObject:

ActorFactory::Instance () gets an instance of the specialized template class, which is sure to succeed as long as it is not running out of memory. Note that this is not an ActorFactory instance, but an ActorFactory instance. It is easy to think that the call to MakeSharedActor () creates the object dynamically by finding the corresponding creation function through the class name. In fact, the first step is to find the corresponding ActorFactory instance by calling the number and type of parameters.

Create (strActorName, std::forward (args)...) The corresponding creation function pointer is found by the class name, and if found, the parameter is forwarded to CreateObject () to create the object. Most of the reasons for not being created successfully are that the function pointer cannot be found here. The reason why the corresponding creation function pointer cannot be found by the class name is that the class that wants to create the object does not inherit DynamicCreator. There is no inheritance here, there is obvious non-inheritance and implicit non-inheritance, the so-called implicit non-inheritance is because the call does not match the inheritance, in other words, the call and registration are inconsistent: the called ActorFactory instance is not the ActorFactory instance that stores the CreateObject function pointer. After ActorFactory is specialized, it is a definite type, and there is no possibility of implicit conversion of parameters.

The CreateObject () function pointer call to DynamicCreator is called in the second step. As long as the parameter can be implicitly converted to the formal parameter type of the constructor, it can be created successfully, but the object is not created successfully because it is less likely that this step is wrong. For example, the constructor is Construct (int, int&, const std::string&), and the actual CreateObject (bool, int, std::string) can also be created successfully.

6. Dynamic creation of design principles and techniques

The parameter design of    dynamic creation is directly related to the success of subsequent dynamic creation and the efficiency of dynamic creation (the difference between parameter reference transfer and value transfer), so it is very important to establish a design principle.

Starting from the class constructor, the parameter type of the template is designed, and the two are as consistent as possible, and if inconsistent, it should also be an implicit conversion of inefficiency loss.

Properly consider the parameter type of the actual call to adjust the template parameters with inefficient loss.

   for example, the constructor needs to pass an int parameter, and the template parameter type is also designed to int, but the actual int& passed by the caller will be more convenient and easier to understand. In this case, you can change the template parameter type to int& and keep the constructor parameter unchanged (if you change the constructor parameter to int&, it will make people misunderstand that the constructor will change the value of the parameter. Changing it to const int& will change the caller to const int& in order to make the call successful.

  -defined variables are often of a T & type when passed as arguments, which is generally not a problem in object references (such as const std::string&), because constructors and template parameters are usually designed to const std::string&, but the underlying types int, float, etc., are usually passed as values in constructors and template parameters, which involves the int& scenario illustrated above If you do not want to adjust the template parameter type, another trick is to add (int) and (float) in front of the passed argument to make a strong turn, after which the parameter is passed by value and the correct creation function can be called. The pseudo code is as follows:

/ / class Test: public neb::Actor, public neb::DynamicCreatorclass Test: public neb::Actor, public neb::DynamicCreator / / Note the template parameter type std::string&, while the constructor parameter type is const std::string& {public: Test (int iFrom, int iTo, const std::string& strName);...}; int main () {int iFrom = 0; int iTo = 500; std::string strName = "latency" / / if the above template parameter type is changed to const std::string&, it needs to be changed to const std::string strName = "latency"; MakeSharedActor ("Test", iFrom, iTo, strName); / / call failed MakeSharedActor ("Test", (int) iFrom, (int) iTo, strName) / / call successfully} at this point, the study on "how to realize C++ reflection by the variable parameter template of C++ reflection mechanism" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report