2015-03-19 75 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