c++ - 在工厂模式中自动注册派生类

标签 c++ c++11 factory

对于我正在编写的应用程序,一个重要的部分是用户可以为不同的进程选择任何可用选项。所有这些选项都派生自同一个基类。不过,我确实偶尔会添加新选项,而且我想让这个过程尽可能简单。所以,在浏览了网络和 SO 之后,这就是我所拥有的:

基类:

class Base {
    double some_member;
    virtual double some_method() = 0;
};

派生类:

class Derived : Base {
//...
};

保存可用类型表并根据指定名称创建派生类的工厂:

template <typename B>
class Factory{
public:
    template <typename D>
    void registerType(std::string name)
    {
        static_assert(std::is_base_of<B, D>::value, "class doesn't derive from the base");
        table_[name] = &createFunc<D>;
    }
    B* create(std::string name)
    {
        const auto it = table_.find(name);
        if(it != table_.end())
            return it->second();
        FILE_LOG(logERROR) << "unidentified option, acceptable options are:";
        for(auto const &m : list())
            FILE_LOG(logERROR) << '\t' << m;
        return nullptr;
    }

    std::vector<std::string> list()
    {
        std::vector<std::string> lst;
        for(auto const &iter : table_)
            lst.push_back(iter.first);
        return lst;
    }
private:
    template<typename D>
    static B* createFunc()
    {
        return new D();
    }
    typedef B* (*PCreateFunc)();
    std::map<std::string, PCreateFunc> table_;
};

包含特定进程的所有选项的类:

class OptsContainer {
private:
    std::vector<std::unique_ptr<Base>> opts_;
public:
    //some other stuff
    void addOption(const std::string &); //adds a new option with given name
    static Factory<Base> factory;
};

Factory<Base> OptsContainer::factory;

void OptsContainer::addOption(const std::string &name)
{
    opts_.push_back(std::unique_ptr<Base>(factory.create(name)));
}

此时,每当我添加一个新的派生类时,我只需要在工厂表中注册该类:

///All older includes
#include "derived42.h"

void initOptions()
{
    //all other registrations
    OptsContainer::factory.registerType<Derived42>("Derived42");
}

我在程序的最开始调用了 initOptions。这很好用,但我觉得应该可以让它变得更好。我想要的是只需要添加 #include "derived42" 和注册自己的类型,因此不需要 initOptions 函数并在一开始就调用它。

我是否必须使用 Boost/Loki/...(如另一篇文章所述)?这在 Vanilla C++ 中甚至可能吗?还是我应该只接受我现在拥有的东西?仅供引用,我正在使用 Visual Studio Express 2015 编写我的程序,但我也可以将代码移至 Linux (gcc)。换句话说,不喜欢使用 VS2015 中未实现的 c++11/c++14 模式,但如果需要,我可以轻松地将所有内容移动到 gcc。

最佳答案

我觉得你的代码太复杂了。也许我怀念是有原因的。

您无法避免注册。您正在寻找的是在编译时注册,我认为这是不可能的。既然你是在编译期找注册,那就意味着你不需要在接口(interface)中暴露注册方法。因此,您的客户端接口(interface)应该由基类和创建方法组成:

// { client interface header
class B
{
public:
  virtual ~B() = 0 {}
  //...
};

B* Create( const char* s );
// } client interface header

一个简单的创建函数:

// { client interface implementation

template< typename T >
B* New()
{
  return new T;
}

typedef B* (*PFNew)();

B* Create( const char* s )
{
  typedef std::map< string, PFNew > Map;

  static Map m;
  if ( ! m.size() )
  {
    m[ D1::Signature() ] = New< D1 >;
    m[ D2::Signature() ] = New< D2 >;
    //...
  }

  Map::const_iterator ci = m.find( s );
  if ( ci == m.end() )
    throw -1;
  return ci->second();
}

// } client interface implementation

关于c++ - 在工厂模式中自动注册派生类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34092685/

相关文章:

c++ - move 构造函数可以是隐式的吗?

java - 在派生类构造函数(或工厂)中初始化基类的模式

javascript - AngularJS - 如何从 json 响应中提取两条数据并在选择(组合框)中使用它们?

c++ - 恢复由NtCreateProcessEx创建的进程

c++ - 移动构造函数调用基类移动构造函数

c++ - minGW(使用 g++ 4.7.1)不编译简单的 C++11 代码(在 WinVista 下)

javascript - 为什么 OOP 中需要工厂函数?

c++ - 在 C++ 中计算字符串中的字符出现次数

c++ - 我可以通过 c 或 c++ 中的另一个 UDS 连接传递 UDS 文件描述符吗

c++ - libxrandr 库 : how to change properties of connected monitors?