2013-08-28 249 views
3

我有一个我想存储在结构中的类树。我看到如何实现具有空或预定义构造函数的元素的结构,但我无法看到如何使用未预定义的构造函数参数签名来避开。所以说我们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,这些构造函数使用常量参数?

所以我希望在结束时得到这样的使用示例:

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

我认为你必须使用某种类型的擦除。你可以使用C++ 11,BTW吗? – Nawaz

+0

顺便说一句,'Example'不是从'ExampleBase'派生的。另外,'Example.cpp'文件中似乎有错字。请先解决这些问题。 – Nawaz

+0

@Nswaz:修正了一些错别字,VS2012主要的痛苦...所以一些C++ 11还没有完成它。 – DuckQueen

回答

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

可变参数模板可以帮助在这里:你需要的是能够帮助识别需要调用哪个构造的助洗剂可以是复杂的。我使用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 

希望有所帮助。 :-)

+0

你好,炉排回答,谢谢你的无效*主意!您能否展示如何在C++ 03/C中完成可变长度参数? – DuckQueen

+0

@DuckQueen:为了支持C++ 03中的变量参数,您需要在代码中添加重载函数模板。只要看看当前的代码是如何工作的,那会给你一些想法如何在C++ 03中完成。 – Nawaz

相关问题