c ++派生类型的自动工厂注册

Ale*_*lex 16 c++ templates static-methods factory-pattern

像我之前的许多人一样,我正在努力让我的派生类型自动注册到我的工厂.我读了很多问题,并试着把注意力集中在我没有找到的地方.

除了自动注册外,我的一切运行都很顺利.

我的目标:

  1. 自动注册我的基类Base的任何派生类
    1. 只有我标记为可注册的
    2. 不仅是Base的直接子类
      • 例如:基地 - >设备 - >摄像头 - > 网络摄像头
      • 这将使得使用像这个问题中描述的CRTP一样困难
  2. 我想要注册的类的最小变化 - 假人证明
  3. 宁愿使用registrator类而不是宏

是)我有的:

template <class T>
class abstract_factory
{
    public:
        template < typename Tsub > static void register_class();
        static T* create( const std::string& name );
    private:
        // allocator<T> is a helper class to create a pointer of correct type
        static std::map<std::string, boost::shared_ptr<allocator<T> > > s_map;
};
Run Code Online (Sandbox Code Playgroud)
  • 模板化抽象工厂,使用std :: string作为键类型
  • abstract factory具有静态的所有成员和方法
  • 使用typeid自动恢复类名(注册时不需要文本名)
  • 通过电话注册: abstract_factory<Base>::register_class<MyDerived>();

我尝试过(或想要但不知道如何正确):

  • registrator<Derived> class:模板化的类,静态实例化Derived.cpp,应该调用abstract_factory::register_class<Derived>()它的构造函数
    • 永远不会被调用或实例化
    • 如果我Derivedmain()这个作品中做出一个实例- >有点打败了目的
  • 在每个中声明一个简单的静态变量,Derived.hpp并在Derived.cpp- >中再次使用静态注册方法设置它,永远不会被调用.
  • 制作abstract_factory一个真正的单身人而不是让一切都变得静止?

可以使用任何建议,无论大小,thanx.

Yli*_*sar 9

我使用单身人士和会员注册,基本上:

template< typename KeyType, typename ProductCreatorType >
class Factory
{
    typedef boost::unordered_map< KeyType, ProductCreatorType > CreatorMap;
    ...
};
Run Code Online (Sandbox Code Playgroud)

使用Loki然后我有这样的东西:

 typedef Loki::SingletonHolder< Factory< StringHash, boost::function< boost::shared_ptr< SomeBase >( const SomeSource& ) > >, Loki::CreateStatic > SomeFactory;
Run Code Online (Sandbox Code Playgroud)

注册通常使用宏来完成,例如:

#define REGISTER_SOME_FACTORY( type ) static bool BOOST_PP_CAT( type, __regged ) = SomeFactory::Instance().RegisterCreator( BOOST_PP_STRINGIZE( type ), boost::bind( &boost::make_shared< type >, _1 ) );
Run Code Online (Sandbox Code Playgroud)

此设置具有许多优点:

  • 适用于例如boost :: shared_ptr <>.
  • 不需要为所有注册需求维护一个巨大的文件.
  • 对于创作者来说非常灵活,任何事情都可以.
  • 该宏涵盖了最常见的用例,同时保留了可供选择的大门.

然后,在.cpp文件中调用宏就足以获得在静态初始化期间启动时注册的类型.当类型注册是静态库的一部分时,这可以使用dandy,在这种情况下,它不会包含在二进制文件中.将注册编译为我所见过的库的一部分的唯一解决方案是有一个巨大的文件,它明确地将注册作为某种初始化例程的一部分.相反,我现在所做的是拥有一个带有我的lib的客户端文件夹,用户将其作为二进制构建的一部分.

从您的要求列表中我相信这可以满足除使用registrator类之外的所有内容.

  • 请注意,如果您在编译为静态库的翻译单元中注册您的工厂,这将不起作用。链接器可以去掉全局变量,因为它们不在任何地方使用 ODR。 (2认同)

eno*_*ram 5

--- 注册.h ---

#include <iostream>
#include <typeinfo>
#include <set>
#include <string>
using namespace std;

template<class T>
struct registrar {
        struct proxy { inline proxy();};
        static proxy p;
};

template<class T> typename registrar<T>::proxy registrar<T>::p;



struct factory {
        template <typename T> static T* create() {
               registrar<T>::p;
               return new T();
        }
};

set<string> & types();

template<typename T>
registrar<T>::proxy::proxy() { types().insert(typeid(T).name());}
Run Code Online (Sandbox Code Playgroud)

--- 注册.cpp ---

#include "registration.h"
#include <boost/foreach.hpp>

set<string> & types() {static set<string> types; return types;} 

int main() {
    BOOST_FOREACH(const string & s, types()) { cout<<s<<"\n";}
        factory::create<int>();
    factory::create<double>();
    factory::create<bool>();
        return 0;
}
Run Code Online (Sandbox Code Playgroud)

--- 注册_ext.cpp ---

#include "registration.h"

class myclass {};

void phony() {
    factory::create<double>();
    factory::create<myclass>();
}
Run Code Online (Sandbox Code Playgroud)

然后编译:

$ g++ registration.cpp registration_ext.cpp -o registration
Run Code Online (Sandbox Code Playgroud)

运行时:

$ ./registration 
7myclass
b
d
i
Run Code Online (Sandbox Code Playgroud)

所以,它似乎在 main 被调用之前注册了这些类。

编辑:我注意到这个解决方案依赖于实现。在 3.6.2 下,C++ 标准说:

是否在main的第一条语句之前完成静态存储持续时间的非局部变量的动态初始化是实现定义的。如果初始化推迟到 main 的第一条语句之后的某个时间点,则它应发生在与要初始化的变量在同一翻译单元中定义的任何函数或变量的第一次 odr-use (3.2) 之前。