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

时间:2012-04-02 11:35:23

标签: c++ templates static-methods factory-pattern

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

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

我的目标:

  1. 自动注册我的基类 Base 的任何派生类
    1. 只有我标记为可注册
    2. 的类
    3. 不仅是 Base 的直接子类
      • ex:基地 - >设备 - >相机 - >的网络摄像头
      • 这会使用CRTP dificult
      • 中描述的this question
  2. 我想要注册的课程的最小变化 - dummies proof
  3. 更喜欢使用 registrator类而不是宏
  4. 我有什么:

    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;
    };
    
    • 模板化抽象工厂, std :: string 密钥类型
    • 抽象工厂拥有所有成员和方法静态
    • 使用 typeid 自动恢复班级名称(注册时无需文字名称)
    • 通过致电注册:abstract_factory<Base>::register_class<MyDerived>();

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

    • registrator<Derived> class:在Derived.cpp中静态实例化的模板类,应在其构造函数中调用abstract_factory::register_class<Derived>()
      • 永远不会被调用或实例化
      • 如果我在Derived main()中创建了Derived.hpp的实例,则可以使用&gt;
      • 有点打败了目的
    • 在每个Derived.cpp中声明一个简单的静态变量,并使用abstract_factory中的静态注册方法设置它 - &gt;再一次,永远不会被召唤。
    • 让{{1}}成为一个真正的单身而不是让一切都变得静止吗?

    可以使用任何建议,无论大小,也可以使用。

2 个答案:

答案 0 :(得分:9)

我使用单身成员进行注册,基本上是:

template< typename KeyType, typename ProductCreatorType >
class Factory
{
    typedef boost::unordered_map< KeyType, ProductCreatorType > CreatorMap;
    ...
};

使用Loki然后我就有了这些方面:

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

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

#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 ) );

此设置有许多优点:

  • 使用例如boost :: shared_ptr&lt;&gt;。
  • 不需要为所有注册需求维护一个庞大的文件。
  • 对于创作者来说非常灵活,任何事情都有所不同。
  • 该宏涵盖了最常见的用例,同时保留了开启的替代方案。

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

从您的要求列表中我相信除了使用registrator类之外,它还能满足所有要求。

答案 1 :(得分:4)

--- registration.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());}

--- registration.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;
}

--- registration_ext.cpp ---

#include "registration.h"

class myclass {};

void phony() {
    factory::create<double>();
    factory::create<myclass>();
}

然后编译:

$ g++ registration.cpp registration_ext.cpp -o registration

运行时:

$ ./registration 
7myclass
b
d
i

因此,似乎在调用main之前已经注册了类。

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

  

它是实现定义的动态初始化   具有静态存储持续时间的非局部变量在之前完成   主要的第一个声明。如果初始化推迟到某些   在主要的第一个陈述之后的时间点,它应该在之前发生   第一个odr-use(3.2)中定义的任何函数或变量   与要初始化的变量相同的翻译单元。