3

我有一个要存储在织物中的类树。我看到了如何为具有空或预定义构造函数的元素实现结构,但我看不到如何使用未预定义的构造函数参数签名来解决问题。所以说我们以一种不直接影响工厂类的方式将不同的类型注册到工厂中。我们的工厂是这样的:

工厂.hpp:

#include <unordered_map>
#include <string>

template<class BaseType, class BaseTypeCode>
class Factory {
public:
    typedef BaseType * (*base_creator_fn)();
    typedef std::unordered_map<BaseTypeCode, base_creator_fn> registry_map;

    static registry_map & registry() {
        static registry_map impl;
        return impl;
    }

    static BaseType * instantiate(BaseTypeCode const & name) {
        auto it = registry().find(name);
        return it == registry().end() ? nullptr : (it->second)();
    }

    virtual ~Factory() = default;        
};

template<class BaseClass,  class BaseTypeCode>
struct Registrar {
    Registrar(BaseTypeCode name, Factory<BaseClass>::base_creator_fn func) {
        Factory<BaseClass>::registry()[name] = func;
    }
};

所以问题是:如何将 typedef 设置为 atypedef BaseType * (*base_creator_fn)(...);而不是 current (),其次如何转发其中的参数,instantiate最后(BaseTypeCode const & name, ...)如何从采用 const 参数的构造函数中获取新的 base_creator_fn ?

所以我希望最后能得到这样的例子:

示例.hpp:

#include "Factory.hpp"

enum ExampleTypeCode { ExampleTypeCode_Simple = 1 };

class ExampleBase {
    virtual ~ExampleBase(){}
};

class Example : public ExampleBase {
    Example(int i) {}
    virtual ~Example(){}

    static Registrar<ExampleBase, ExampleTypeCode> registrar;
};

示例.cpp:

#include <boost/bind.hpp>
#include <boost/lambda.hpp>
#include "Example.hpp"

Registrar<Example, ExampleTypeCode> Example::registrar(ExampleTypeCode_Simple, boost::bind(boost::lambda::new_ptr<Example>, _firstVarArg));

主文件

#include "Example.hpp"

int main() {
    ExampleBase * p = Base::instantiate(ExampleTypeCode_Simple, 1);
}

我的主要编译目标是最新的 GCC,遗憾的是带有最新服务包的 Visual Studio 2012 - 所以 C++11 子集非常有限,但尚未提供。

4

2 回答 2

8

可变参数模板可以在这里提供帮助。我用作void*第二种类型,registry_map以便我们可以在注册表中存储任何类型的函数。这是一个演示基本思想的最小代码:

class Base
{
public:
    typedef std::unordered_map<std::string, void*> registry_map;

    virtual ~Base() = default;

    static registry_map & registry()
    {
        static registry_map impl;
        return impl;
    }
    template<typename ...T>
    static Base * instantiate(std::string const & name, T&&...args)
    {
        auto it = registry().find(name);
        if ( it == registry().end()) return 0;
        typedef Base* (*create_type)(T...);
        auto create_fun = reinterpret_cast<create_type>(it->second);
        return create_fun(args...);
    }
    virtual void f() = 0;
};

struct Registrar
{
    template<typename F>
    Registrar(std::string name, F func)
    {
        Base::registry()[name] = reinterpret_cast<void*>(func);
    }
};

下面是测试代码:我定义了两个派生类,其中一个在创建时接受两个参数,另一个不接受任何参数。

class DerivedExample : public Base
{
    static Registrar registrar;
public:
    static Base * create() { return new DerivedExample; }
    virtual void f() override { std::cout << "DerivedExample" << std::endl; }
};

Registrar DerivedExample::registrar("DerivedExample", DerivedExample::create);

class AnotherExample : public Base
{
    static Registrar registrar;
    int a;
    const char *b;
public:
    AnotherExample(int a, const char *b) : a(a), b(b) {}
    static Base * create(int a, const char *b) { return new AnotherExample(a,b); }
    virtual void f() override { std::cout << "AnotherExample. a = " << a << ", b = " << b << std::endl; }
};

Registrar AnotherExample::registrar("AnotherExample", AnotherExample::create);

int main()
{
    Base * p = Base::instantiate("DerivedExample");
    Base * q = Base::instantiate("AnotherExample", 10, "Mahfuza");
    p->f();
    q->f();

    delete p;
    delete q;
}

输出(在线演示):

DerivedExample
AnotherExample. a = 10, b = Mahfuza

希望有帮助。:-)

于 2013-08-28T16:53:35.263 回答
3

您可以将您的工厂与构建器结合起来,以获得您想要的行为。构建器将保存参数。

template<class BaseType, class BaseTypeCode, class BaseTypeBuilder>
class Factory {
public:
    typedef BaseType * (*base_creator_fn)(BaseTypeBuilder const &);
    //...
    static BaseType * instantiate(BaseTypeCode const & name,
                                  BaseTypeBuilder const & builder
                                  = BaseTypeBuilder()) {
        auto it = registry().find(name);
        return it == registry().end() ? nullptr : (it->second)(builder);
    }

    virtual ~Factory() = default;        
};

然后,对于您的Example,您创建一个构建器。构建器可以像您需要的那样复杂,以帮助区分需要调用哪个构造器:

class ExampleBuilder {
    boost::optional<int> i;

public:
    ExampleBuilder () {}

    void set_i (int x) { i = x; }

    static Example * create (const ExampleBuilder &builder) {
        if (builder.i) return new Example(builder.i);
        else return new Example();
    }
};

Registrar<Example, ExampleTypeCode, ExampleBuilder>
    Example::registrar(ExampleTypeCode_Simple, ExampleBuilder::create);

    ExampleBuilder build;
    build.set_i(1);
    ExampleBase * p = Base::instantiate(ExampleTypeCode_Simple, build);
于 2013-08-28T16:15:19.070 回答