2015-03-19 77 views
6

我想写一个工厂类,将有一个标准的接口,看起来像这样:C++:可以调用任何构造函数的通用工厂?

Register<MyBase, MyDerived> g_regDerived("myderived"); // register to factory 

现在呼吁:

auto* d = Factory<MyBase>::instance().create("myderived", 1, 2, 3); 

将调用构造函数MyDerived(1,2,3)和指针返回创建对象

这听起来像是应该可以用C + + 11的东西,但我无法弄清楚如何做到这一点。
从标准类型擦除工厂开始:

template<typename BaseT> 
class Factory { 
public: 
    static Factory* instance() { 
     static Factory inst; 
     return &inst; 
    } 
    template<typename T> 
    void reg(const string& name) { 
     m_stock[name].reset(new Creator<T>); 
    } 
    BaseT* create(const string& name) { 
     return m_stock[name]->create(); 
    } 
private: 
    struct ICreator { 
     virtual BaseT* create() = 0; 
    }; 
    template<typename T> 
    struct Creator : public ICreator { 
     virtual BaseT* create() { 
      return new T; 
     } 
    }; 
    std::map<string, std::unique_ptr<ICreator>> m_stock; 
}; 

template<typename BaseT, typename T> 
class Register { 
public: 
    Register(const QString& name) { 
     Factory<BaseT>::instance()->reg<T>(name); 
    } 
}; 

这里的问题是事实,一旦你删除创建的对象的类型,你可以不再通过任意模板转发的参数,因为您需要通过他们一个虚拟功能。

回答这个问题:
How to pass a function pointer that points to constructor?
的东西相似,但答案没有去通过函数这是具体的每一个派生类的会谈。我想直接使用类构造函数,而不必编写create()函数。

+4

我认为这样做*完美*是不可能的;函数的参数实际上是编译时与其他语言(如Java或C#)的对比。 (他们可以通过反射动态调用一个函数)当然C++有*编译时*反射,但是你想动态地调用函数。我建议模仿这些运行时反射:你可以使用类似'void * create(const std :: vector ikh 2015-03-19 10:26:04

+0

如果允许某种动态初始化,通过引入一个基本上是'boost :: any'列表的'config'类,并且始终允许它作为将要创建的类的单个参数,我会轻松得多。 – filmor 2015-03-19 15:16:29

+0

'create()'函数有什么问题? – Adrian 2015-03-19 20:05:58

回答

6

我不知道你为什么厌恶编写create()函数。所以这是我实施的一个。

#include <iostream> 
#include <utility> 

using namespace std; 

class C 
{ 
public: 
    virtual char const* whoAmI() const = 0; 
}; 

class A : public C 
{ 
public: 
    A(int a1) 
    { 
     cout << "A(" << a1 << ")" << endl; 
    } 

    A(float a1) 
    { 
     cout << "A(" << a1 << ")" << endl; 
    } 

    virtual char const* whoAmI() const override 
    { 
     return "A"; 
    } 
}; 

class B : public C 
{ 
public: 
    B(int a1) 
    { 
     cout << "B(" << a1 << ")" << endl; 
    } 

    B(float a1) 
    { 
     cout << "B(" << a1 << ")" << endl; 
    } 

    virtual char const* whoAmI() const override 
    { 
     return "B"; 
    } 

}; 

template<typename BASET> 
class Factory 
{ 
public: 
    // could use a is_base type trait test here 
    template <typename T, typename...ARGs> 
    static BASET* create(ARGs&&...args) 
    { 
     return new T(forward<ARGs>(args)...); 
    } 

}; 
int main() 
{ 
    Factory<C> factory; 
    C* a = factory.create<A>(1); 
    C* b = factory.create<B>(1.0f); 
    cout << a->whoAmI() << endl; 
    cout << b->whoAmI() << endl; 
    return 0; 
} 

注:我没有做你所做的一切,我只是实现了创建函数。我把最后的实施留给你。

这使用完美转发来启用varidict模板以将任意数量的参数传递给构造函数。然后,您的注册函数可以为特定的参数集存储特定模板实例的函数指针。

编辑

我忘了使用适当forward<ARGs>(args)...调用以实现完美转发。它现在已被添加。

至于你以为这是没有用的,这里是用完美的转发和varidict模板允许特定类型的参数特定数量的特定工厂实例全面推行你的工厂:

#include <string> 
#include <map> 
#include <memory> 
#include <utility> 
#include <iostream> 

using namespace std; 

    template<typename BaseT, typename...ARGs> 
    class Factory { 
    public: 
     static Factory* instance() { 
      static Factory inst; 
      return &inst; 
     } 
     template<typename T> 
     void reg(const string& name) { 
      m_stock[name].reset(new Creator<T>); 
     } 
     BaseT* create(const string& name, ARGs&&...args) { 
      return m_stock[name]->create(forward<ARGs>(args)...); 
     } 
    private: 
     struct ICreator 
     { 
      virtual BaseT* create(ARGs&&...) = 0; 

     }; 
     template<typename T> 
     struct Creator : public ICreator { 
      virtual BaseT* create(ARGs&&...args) override 
      { 
       return new T(forward<ARGs>(args)...); 
      } 
     }; 
     std::map<string, std::unique_ptr<ICreator>> m_stock; 
    }; 

    template<typename BaseT, typename T, typename...ARGs> 
    class Register { 
    public: 
     Register(const string& name) { 
      auto instance = Factory<BaseT, ARGs...>::instance(); 
      instance->template reg<T>(name); 
     } 
    }; 

struct C 
{ 
    virtual char const * whoAmI() const = 0; 
}; 

struct A : public C 
{ 
    A(int a1, int a2) 
    { 
     cout << "Creating A(" << a1 << ", " << a2 << ")" << endl; 
    } 

    virtual char const * whoAmI() const override 
    { 
     return "A"; 
    } 
}; 

struct B : public C 
{ 
    B(int b1, int b2) 
    { 
     cout << "Creating B(" << b1 << ", " << b2 << ")" << endl; 
    } 
    B(int b1, int b2, int b3) 
    { 
     cout << "Creating B(" << b1 << ", " << b2 << ", " << b3 << ")" << endl; 
    } 
    virtual char const * whoAmI() const override 
    { 
     return "B"; 
    } 
}; 

typedef int I; 
Register<C, A, I, I> a("a"); 
Register<C, B, I, I> b("b"); 
Register<C, B, I, I, I> b3("b"); 
int main() 
{ 
    C* a = Factory<C, I, I>::instance()->create("a", 1, 2); 
    C* b = Factory<C, I, I>::instance()->create("b", 3, 4); 
    C* b3 = Factory<C, I, I, I>::instance()->create("b", 5, 6, 7); 
    cout << "I am a " << a->whoAmI() << endl; 
    cout << "I am a " << b->whoAmI() << endl; 
    cout << "I am a " << b3->whoAmI() << endl; 
    return 0; 
} 

那是你要的吗?如果你不想处理函数的参数,使用一个辅助模板函数来推断他们为你喜欢这样:

template <typename BaseT, typename...ARGs> 
BaseT* create(const string& name, ARGs&&...args) 
{ 
    return Factory<C, ARGs...>::instance()->create(name, forward<ARGs>(args)...); 
} 

int main() 
{ 
    C* a = create<C>("a", 1, 2); 
    C* b = create<C>("b", 3, 4); 
    C* b3 = create<C>("b", 3, 4, 5); 
    cout << "I am a " << a->whoAmI() << endl; 
    cout << "I am a " << b->whoAmI() << endl; 
    cout << "I am a " << b3->whoAmI() << endl; 
    return 0; 
} 

其中有允许多个构造函数签名可以通过明显的单一功能的API额外的奖励(它看起来像一个,但实际上是N其中N是您允许的不同签名的数量)。这一切都可以通过这个online demo查看。

您仍然需要使用与前面描述的相同的注册,但可以通过宏缩短。

如果这是仍然不是你想要的,然后添加额外的细节到你的问题。

+0

感谢您以无用的答案发送垃圾邮件。现在请删除它 – shoosh 2015-03-19 22:23:25

+0

那么,最初的答案是没用的,因为它没有解决问题,部分原因是这样。编辑的答案确实似乎工作。你正在为每一组参数实例化Factory,所以Factory不再是一个单身人士。但是我可以做出类似于MetaFactory的东西,它将持有对所有不同工厂的引用,这些工​​厂将是一个单身人士(这对于我不重要的理由而言非常重要)。谢谢,我道歉:) – shoosh 2015-03-20 18:37:47

+2

@ shoosh,原来的答案是指着你想要去的方向。我不想为你做所有的工作。 ;) – Adrian 2015-03-20 18:40:11