<legend id='je039'><style id='je039'><dir id='je039'><q id='je039'></q></dir></style></legend>
  • <small id='je039'></small><noframes id='je039'>

    1. <tfoot id='je039'></tfoot>
        <bdo id='je039'></bdo><ul id='je039'></ul>

      1. <i id='je039'><tr id='je039'><dt id='je039'><q id='je039'><span id='je039'><b id='je039'><form id='je039'><ins id='je039'></ins><ul id='je039'></ul><sub id='je039'></sub></form><legend id='je039'></legend><bdo id='je039'><pre id='je039'><center id='je039'></center></pre></bdo></b><th id='je039'></th></span></q></dt></tr></i><div id='je039'><tfoot id='je039'></tfoot><dl id='je039'><fieldset id='je039'></fieldset></dl></div>
      2. 在编译时使用 C++ 模板在 AbstractFactory 中动态注册

        时间:2023-05-24

          <bdo id='i2XTq'></bdo><ul id='i2XTq'></ul>

            <tfoot id='i2XTq'></tfoot>
              <tbody id='i2XTq'></tbody>

            <legend id='i2XTq'><style id='i2XTq'><dir id='i2XTq'><q id='i2XTq'></q></dir></style></legend>

            <small id='i2XTq'></small><noframes id='i2XTq'>

            <i id='i2XTq'><tr id='i2XTq'><dt id='i2XTq'><q id='i2XTq'><span id='i2XTq'><b id='i2XTq'><form id='i2XTq'><ins id='i2XTq'></ins><ul id='i2XTq'></ul><sub id='i2XTq'></sub></form><legend id='i2XTq'></legend><bdo id='i2XTq'><pre id='i2XTq'><center id='i2XTq'></center></pre></bdo></b><th id='i2XTq'></th></span></q></dt></tr></i><div id='i2XTq'><tfoot id='i2XTq'></tfoot><dl id='i2XTq'><fieldset id='i2XTq'></fieldset></dl></div>
                  本文介绍了在编译时使用 C++ 模板在 AbstractFactory 中动态注册构造函数方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

                  问题描述

                  限时送ChatGPT账号..

                  在实现 MessageFactory 类来实例化 Message 对象时,我使用了类似的东西:

                  When implementing a MessageFactory class to instatiate Message objects I used something like:

                  class MessageFactory 
                  {
                    public:
                      static Message *create(int type)
                      {
                         switch(type) {
                           case PING_MSG:
                              return new PingMessage();
                           case PONG_MSG:
                              return new PongMessage();
                           ....
                      }
                  }
                  

                  这没问题,但每次添加新消息时,我都必须添加新的 XXX_MSG 并修改 switch 语句.

                  This works ok but every time I add a new message I have to add a new XXX_MSG and modify the switch statement.

                  经过一番研究,我找到了一种在编译时动态更新 MessageFactory 的方法,这样我就可以添加任意数量的消息,而无需修改 MessageFactory 本身.这使得代码更清晰、更易于维护,因为我不需要修改三个不同的地方来添加/删除消息类:

                  After some research I found a way to dynamically update the MessageFactory at compile time so I can add as many messages as I want without need to modify the MessageFactory itself. This allows for cleaner and easier to maintain code as I do not need to modify three different places to add/remove message classes:

                  #include <stdio.h>                                                                                                                                                                           
                  #include <stdlib.h>                                                                                                                                                                          
                  #include <string.h>                                                                                                                                                                          
                  #include <inttypes.h>                                                                                                                                                                        
                  
                  class Message                                                                                                                                                                                
                  {                                                                                                                                                                                            
                     protected:                                                                                                                                                                                
                        inline Message() {};                                                                                                                                                                   
                  
                     public:                                                                                                                                                                                   
                        inline virtual ~Message() { }                                                                                                                                                          
                        inline int getMessageType() const { return m_type; }                                                                                                                                   
                        virtual void say() = 0;                                                                                                                                                                
                  
                     protected:                                                                                                                                                                                
                        uint16_t m_type;                                                                                                                                                                       
                  };                                                                                                                                                                                           
                  
                  template<int TYPE, typename IMPL>                                                                                                                                                            
                  class MessageTmpl: public Message                                                                                                                                                            
                  {                                                                                                                                                                                            
                     enum { _MESSAGE_ID = TYPE };                                                                                                                                                              
                     public:                                                                                                                                                                                   
                        static Message* Create() { return new IMPL(); }                                                                                                                                        
                        static const uint16_t MESSAGE_ID; // for registration                                                                                                                                  
                  
                     protected:                                                                                                                                                                                
                        MessageTmpl() { m_type = MESSAGE_ID; } //use parameter to instanciate template                                                                                                         
                  };                                                                                                                                                                                           
                  
                  typedef Message* (*t_pfFactory)();                                                                                                                                                           
                  class MessageFactory⋅                                                                                                                                                                        
                  {                                                                                                                                                                                            
                     public:                                                                                                                                                                                   
                       static uint16_t Register(uint16_t msgid, t_pfFactory factoryMethod)                                                                                                                     
                       {                                                                                                                                                                                       
                         printf("Registering constructor for msg id %d
                  ", msgid);                                                                                                                             
                         m_List[msgid] = factoryMethod;                                                                                                                                                        
                         return msgid;                                                                                                                                                                         
                       }                                                                                                                                                                                       
                  
                       static Message *Create(uint16_t msgid)                                                                                                                                                  
                       {                                                                                                                                                                                       
                         return m_List[msgid]();                                                                                                                                                               
                       }                                                                                                                                                                                       
                       static t_pfFactory m_List[65536];                                                                                                                                                       
                  };  
                  
                  template <int TYPE, typename IMPL>                                                                                                                                                           
                  const uint16_t MessageTmpl<TYPE, IMPL >::MESSAGE_ID = MessageFactory::Register(                                                                                                              
                       MessageTmpl<TYPE, IMPL >::_MESSAGE_ID, &MessageTmpl<TYPE, IMPL >::Create);                                                                                                              
                  
                  class PingMessage: public MessageTmpl < 10, PingMessage >                                                                                                                                    
                  {⋅                                                                                                                                                                                           
                    public:                                                                                                                                                                                    
                    PingMessage() {}                                                                                                                                                                           
                    virtual void say() { printf("Ping
                  "); }                                                                                                                                                   
                  };                                                                                                                                                                                           
                  
                  class PongMessage: public MessageTmpl < 11, PongMessage >                                                                                                                                    
                  {⋅                                                                                                                                                                                           
                    public:                                                                                                                                                                                    
                    PongMessage() {}                                                                                                                                                                           
                    virtual void say() { printf("Pong
                  "); }                                                                                                                                                   
                  };                                                                                                                                                                                           
                  
                  t_pfFactory MessageFactory::m_List[65536];                                                                                                                                                   
                  
                  int main(int argc, char **argv)                                                                                                                                                              
                  {                                                                                                                                                                                            
                    Message *msg1;                                                                                                                                                                             
                    Message *msg2;                                                                                                                                                                             
                  
                    msg1 = MessageFactory::Create(10);                                                                                                                                                         
                    msg1->say();                                                                                                                                                                               
                  
                    msg2 = MessageFactory::Create(11);                                                                                                                                                         
                    msg2->say();                                                                                                                                                                               
                  
                    delete msg1;                                                                                                                                                                               
                    delete msg2;                                                                                                                                                                               
                  
                    return 0;                                                                                                                                                                                  
                  } 
                  

                  这里的模板通过注册到 MessageFactory 类来实现魔法,所有新的 Message 类(例如 PingMessage 和 PongMessage)都是 MessageTmpl 的子类.

                  The template here does the magic by registering into the MessageFactory class, all new Message classes (e.g. PingMessage and PongMessage) that subclass from MessageTmpl.

                  这很好用并简化了代码维护,但我仍然对这项技术有一些疑问:

                  This works great and simplifies code maintenance but I still have some questions about this technique:

                  1. 这是一种已知的技术/模式吗?是什么名字?我想搜索更多信息关于它.

                  1. Is this a known technique/pattern? what is the name? I want to search more info about it.

                  我想创建用于存储新构造函数的数组 MessageFactory::m_List[65536]一个 std::map 但这样做会导致程序在到达 main() 之前发生段错误.创建一个由 65536 个元素组成的数组是矫枉过正的,但我还没有找到一种方法使其成为动态容器.

                  I want to make the array for storing new constructors MessageFactory::m_List[65536] a std::map but doing so causes the program to segfault even before reaching main(). Creating an array of 65536 elements is overkill but I have not found a way to make this a dynamic container.

                  对于作为 MessageTmpl 子类的所有消息类,我必须实现构造函数.如果不是,则不会在 MessageFactory 中注册.

                  For all message classes that are subclasses of MessageTmpl I have to implement the constructor. If not it won't register in the MessageFactory.

                  例如注释 PongMessage 的构造函数:

                  For example commenting the constructor of the PongMessage:

                   class PongMessage: public MessageTmpl < 11, PongMessage >       
                   {                                                                                                                                                                                           
                     public:                                                                                                                                                                                    
                      //PongMessage() {} /* HERE */                                                                                                                                                                          
                      virtual void say() { printf("Pong
                  "); }                   
                   };
                  

                  会导致 PongMessage 类没有被 MessageFactory 注册,并且程序会在 MessageFactory::Create(11) 行中出现段错误.问题是
                  为什么班级不会注册?必须添加 100+ 的空实现我需要的消息感觉效率低下和不必要.

                  would result in the PongMessage class not being registered by the MessageFactory and the program would segfault in the MessageFactory::Create(11) line. The question is
                  why the class won't register? Having to add the empty implementation of the 100+ messages I need feels inefficient and unnecessary.

                  推荐答案

                  答案一

                  派生这样的类的一般技术是Curiously Recurring Template Pattern (CRTP):

                  The general technique of deriving a class like this is the Curiously Recurring Template Pattern (CRTP):

                  class PingMessage: public MessageTmpl < 10, PingMessage > 
                  

                  您使用模板类的静态成员初始化来注册该类的子类的具体技术 (IMO) 简直太棒了,我以前从未见过.一种更常见的方法,由单元测试框架使用,例如 UnitTest++ 和 Google Test 是提供宏来声明类和初始化该类的单独静态变量.

                  Your specific technique of using a template class's static member initialization to register subclasses of that class is (IMO) simply brilliant, and I've never seen that before. A more common approach, used by unit test frameworks like UnitTest++ and Google Test, is to provide macros that declare both a class and a separate static variable initializing that class.

                  回答二

                  静态变量按列出的顺序初始化.如果在 MessageFactory::Register 调用之前移动 m_List 声明,则应该是安全的.还请记住,如果您开始在多个文件中声明 Message 子类,则必须将 m_List 包装为单例并在每次使用前检查它是否已初始化,因为 C++ 静态初始化顺序失败.

                  Static variables are initialized in the order listed. If you move your m_List declaration before your MessageFactory::Register calls, you should be safe. Also keep in mind that if you start declaring Message subclasses in more than one file, you'll have to wrap m_List as a singleton and check that it's initialized before each use, due to the C++ static initialization order fiasco.

                  答案三

                  C++ 编译器只会实例化实际使用的模板成员.模板类的静态成员不是我经常使用的 C++ 领域,所以我在这里可能是错的,但看起来提供构造函数足以让编译器认为使用 MESSAGE_ID(从而确保 MessageFactory::寄存器被调用).

                  C++ compilers will only instantiate template members that are actually used. Static members of template classes is not an area of C++ that I've used much, so I could be wrong here, but it looks like providing the constructor is enough to make the compiler think that MESSAGE_ID is used (thus ensuring that MessageFactory::Register is called).

                  这对我来说似乎很不直观,所以它可能是一个编译器错误.(我在 g++ 4.3.2 中对此进行了测试;例如,我很想知道 Comeau C++ 是如何处理它的.)

                  This seems very unintuitive to me, so it may be a compiler bug. (I was testing this in g++ 4.3.2; I'm curious to know how Comeau C++, for example, handles it.)

                  显式实例化 MESSAGE_ID 也足够了,至少在 g++ 4.3.2 中:

                  Explicitly instantiating MESSAGE_ID also suffices, at least in g++ 4.3.2:

                  template const uint16_t PingMessage::MESSAGE_ID;
                  

                  但这比提供一个空的默认构造函数更没有必要.

                  But that's even more unnecessary work than providing an empty default constructor.

                  使用您当前的方法,我想不出一个好的解决方案;我个人很想切换到较少依赖高级 C++ 的技术(例如宏或使用脚本生成部分源文件).(脚本具有简化 MESSAGE_ID 维护的额外优势.)

                  I can't think of a good solution using your current approach; I'd personally be tempted to switch to a technique (such as macros or using a script to generate part of your source files) that relied less on advanced C++. (A script would have the added advantage of easing maintenance of MESSAGE_IDs.)

                  回应您的评论:

                  单例通常应该避免,因为它们经常被过度使用为伪装不当的全局变量.然而,有时您确实需要一个全局变量,可用 Message 子类的全局注册表就是其中之一.

                  Singletons are generally to be avoided because they're often overused as poorly disguised global variables. There are a few times, however, when you really do need a global variable, and a global registry of available Message subclasses is one of those times.

                  是的,您提供的代码正在初始化 MESSAGE_ID,但我说的是 显式实例化每个子类的 MESSAGE_ID 实例.显式实例化是指指示编译器实例化模板,即使它认为该模板实例不会被使用.

                  Yes, the code that you provided is initializing MESSAGE_ID, but I was talking about explicitly instantiating each subclass's instance of MESSAGE_ID. Explicit instantiation refers to instructing the compiler to instantiate a template even if it thinks that that template instance won't otherwise be used.

                  我怀疑带有 volatile 赋值的静态函数是为了欺骗或强制编译器生成 MESSAGE_ID 赋值(以解决 dash-tom-bang 和我在编译器或链接器是否删除时指出的问题)实例化赋值).

                  I suspect that the static function with the volatile assignment is there to trick or force the compiler into generating the MESSAGE_ID assignment (to get around the problems that dash-tom-bang and I pointed out with the compiler or linker dropping or not instantiating the assignment).

                  这篇关于在编译时使用 C++ 模板在 AbstractFactory 中动态注册构造函数方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持html5模板网!

                  上一篇:模板类型定义? 下一篇:使用 SFINAE 进行模板类专业化

                  相关文章

                  最新文章

                    <bdo id='V4K8u'></bdo><ul id='V4K8u'></ul>

                  <i id='V4K8u'><tr id='V4K8u'><dt id='V4K8u'><q id='V4K8u'><span id='V4K8u'><b id='V4K8u'><form id='V4K8u'><ins id='V4K8u'></ins><ul id='V4K8u'></ul><sub id='V4K8u'></sub></form><legend id='V4K8u'></legend><bdo id='V4K8u'><pre id='V4K8u'><center id='V4K8u'></center></pre></bdo></b><th id='V4K8u'></th></span></q></dt></tr></i><div id='V4K8u'><tfoot id='V4K8u'></tfoot><dl id='V4K8u'><fieldset id='V4K8u'></fieldset></dl></div>

                  <small id='V4K8u'></small><noframes id='V4K8u'>

                • <legend id='V4K8u'><style id='V4K8u'><dir id='V4K8u'><q id='V4K8u'></q></dir></style></legend>

                      <tfoot id='V4K8u'></tfoot>