2013-08-28 3 views
3

나는 패브릭에 저장하려는 클래스 트리가 있습니다. 비어 있거나 미리 정의 된 생성자가있는 요소에 대해 fabric을 구현하는 방법을 볼 수 있지만 미리 정의 된 생성자 인수 시그니처를 사용하여이를 해결할 수있는 방법을 찾지 못합니다. 그래서 우리는 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 BaseType * (*base_creator_fn)(...);에 형식 정의를 만들려면 두 번째 instantiate에 인수를 전달하는 방법이 (BaseTypeCode const & name, ...)을 취하고 마지막으로 const_mount 인수를 취하는 생성자에서 새 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

Variadic 템플릿이 여기에서 도움이 될 수 있습니다. 어떤 유형의 함수도 레지스트리에 저장할 수 있도록 의 두 번째 유형으로 void*을 사용했습니다. 여기

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

그리고 테스트 코드 간다 : 다음은 기본 개념을 보여 최소한의 코드는 내가 창조에 2 개 개의 인수를 취하는 다른 어떤 주장을지지 않습니다 중 하나 두 파생 클래스를 정의한 .

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

안녕하세요, grate answer, 무효 * 아이디어에 감사드립니다!C++ 03/C에서 가변 길이 인수 forvarding이 어떻게 수행 될 수 있는지 보여 주시겠습니까? – DuckQueen

+0

@DuckQueen : C++ 03에서 가변 인수를 지원하려면 코드에서 오버로드 된 함수 템플릿을 추가해야합니다. 현재 코드의 작동 방식을 보시면 C++ 03에서 어떻게 작동하는지 알 수 있습니다. – Nawaz

관련 문제