我有一个我想要存放在结构中的类树。我看到如何使用空的或预定义的构造函数实现元素的结构,但是我没有看到如何使用预定义的构造函数参数签名来解决这个问题。所以说我们register diferent types into factory in a way that does not affect factory class directly。我们的工厂看起来像这样:
Factory.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设为typedef BaseType * (*base_creator_fn)(...);
而不是当前()
,其次如何转发instantiate
中可能会引入(BaseTypeCode const & name, ...)
的参数最后如何从构造函数中获取新的base_creator_fn,这些构造函数采用const ammount参数?
所以我希望在最后得到这样的用例:
Example.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;
};
Example.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));
Main.cpp的
#include "Example.hpp"
int main() {
ExampleBase * p = Base::instantiate(ExampleTypeCode_Simple, 1);
}
我的主要编译目标是最新的GCC,遗憾的是Visual Studio 2012和最新的服务包 - 所以C ++ 11子集非常有限但尚未呈现。
答案 0 :(得分:7)
Variadic模板可以在这里提供帮助。我使用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;
}
输出(demo online):
DerivedExample
AnotherExample. a = 10, b = Mahfuza
希望有所帮助。 : - )
答案 1 :(得分: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);