2013-03-15 75 views
1

我想要創建一個宏,以輕鬆創建從相同基類派生的具有不同名稱和稍微不同行爲的新類。C++類工廠宏

我已經有

class FactoryBonusModifier 
{ 
public: 
    /// Function to overload 
    virtual BonusModifierAbstract* createBonus() const = 0; 
protected: 
}; 

#define DEFAULT_BONUS_FACTORY_DECLARATION(FactoryName)  \ 
class Factory## FactoryName : public FactoryBonusModifier \ 
{               \ 
public:              \ 
    virtual BonusModifierAbstract* createBonus() const;  \ 
}; 

#define DEFAULT_BONUS_FACTORY_IMPLEMENTATION(FactoryName)     \ 
    BonusModifierAbstract* Factory## FactoryName::createBonus() const \ 
    { return new FactoryName(); } 


DEFAULT_BONUS_FACTORY_DECLARATION(BonusModifierGoThroughWall); 

,並在CPP實現部分writen。

我想知道如果我可以有一個宏,將建立一個枚舉和一個這些新類的數組儘可能複製/粘貼。

最後,我想有像

enum BonusType{ 
Bonus1, 
Bonus2, 
..., 
Nb_Bonus 
}; 

FactoryBonusModifier* factories[Nb_Bonus] = 
{ 
    new FactoryBonus1(), 
    new FactoryBonus2(), 
    ..., 
} 
+0

我不會說這是嚴格重複的,因爲這特別涉及(至少部分)使用宏來達到目的,而另一個問題則沒有。 – CodeMouse92 2015-02-03 04:06:56

回答

1

我會避免使用宏這個,如果你能避免它。我會使用模板。

class FactoryBonusModifier 
{ 
public: 
    /// Function to overload 
    virtual BonusModifierAbstract* createBonus() const = 0; 
protected: 
}; 

template<typename Bonus> 
class Factory : public FactoryBonusModifier 
{ 
public: 
    virtual Bonus* createBonus() const 
    { 
     return new Bonus(); 
    } 
} 

所以有點棘手創造枚舉和工廠名單。爲此,我會生成代碼,而不是嘗試使用模板。你可以很容易地將代碼生成集成到大多數構建過程中,但是你需要閱讀它。

個人而言,我會用一個非常簡單的Python腳本,像

bonuses = ["BonusModifierGoThroughWall", "SomeOtherBonus" ]; 
print "enum BonusType {" 
for x in bonuses: 
    print "t"+x+"," 
print "nBonusTypes" 
print "};" 

print "FactoryBonusModifier* factories["+len(bonuses)+"] = {" 
for x in bonuses: 
    print "new Factory<"+bonus+">()," 
print "};" 

應該輸出:

enum BonusType { 
    tBonusModifierGoThroughWall, 
    tSomeOtherBonus, 
    nBonusTypes 
}; 

FactoryBonusModifier* factories[2] = { 
    new Factory<BonusModifierGoThroughWall>(), 
    new Factory<SomeOtherBonus>(), 
}; 
+0

宏在模板之前經常用於這種事情,有趣的是足夠。 – ApproachingDarknessFish 2013-03-15 04:08:01

1

這是增強一個以@ MichaelAnderson的解決方案。

照此FactoryBonusModifier,但不產生蟒蛇的enum和陣列。在模板元編程中進行。

首先,一些樣板。這東西是隻爲模板元編程工具包:

template<typename...> 
struct type_list {}; 

// get the nth type from a list: 
template<int n, typename list> 
struct nth_type; 

// the 0th type is the first type: 
template<0, typename t0, typename... types> 
struct nth_type<0, type_list<t0,types...>> { 
    typedef t0 type; 
}; 
// the nth type is the n-1th type of the tail of the list: 
template<int n, typename t0, typename... types> 
struct nth_type<n, type_list<t0,types...>>:nth_type<n-1,type_list<types...>> 
{}; 

// Get the index of T in the list. 3rd parameter is for metaprogramming. 
template<typename T, typename list, typename=void> 
struct index_in; 

// If T is the first type in the list, the index is 0 
template<typename T, typename t0, typename... list> 
struct index_in<T, type_list<t0, list...>, typename std::enable_if<std::is_same<T,t0>::value>::type> { 
    enum {value = 0}; 
}; 
// If T is not the first type in the list, the index is 1 plus the index of T 
// in the tail of the list: 
template<typename T, typename t0, typename... list> 
struct index_in<T, type_list<t0, list...>, typename std::enable_if<!std::is_same<T,t0>::value>::type> { 
    enum {value = index_in<T, type_list<list...>>::value+1}; 
}; 

// calls() on the arguments passed to it in order: 
inline void do_in_order() {} 
template<typename L0, typename... Lambdas> 
void do_in_order(L0&& l0, Lambdas&&... lambdas) { 
    std::forward<L0>(l0)(); // std::forward is for insane corner cases, not usually needed 
    do_in_order(std::forward<Lambdas>(lambdas)...); 
} 

我們有type_list,其中封裝了一個類型列表以繞過或操縱。我們在type_list,nth_type上有兩個操作,它從索引中提取一個類型,index_in接受一個類型並返回它的索引。

最後,我們有一個名爲do_in_order的幫助函數,我們可以通過參數包擴展來遍歷參數包,併爲參數包中的每個元素執行一個操作。我更喜歡其他黑客,因爲它很容易編寫,而且結果更少意外。

一旦我們有了這個基本的工具集,才能真正輕鬆地編寫一個廠廠房:

// bad name for this template. It takes the type list list and applies 
// the producer template on each, then stores pointers to instances of those 
// in an array of base pointers (well unique_ptrs). It also provides 
// a type-to-index mapping, an index-to-instance mapping, and a type-to 
// instance mapping: 
template<typename list, template<typename>class producer, typename base> 
struct mapping; 

template<typename... Ts, template<typename>class producer, typename base> 
struct mapping<type_list<Ts...>, producer, base> 
{ 
    enum Enum { 
    min_value = 0, 
    max_value = sizeof...(list) 
    }; 
    template<typename T> 
    static Enum GetIndex() constexpr { return (Enum)index_in<T, type_list<Ts...>>::value; } 
    std::unique_ptr<base> Array[max_value]; 
    mapping() { 
    do_in_order([&Array](){ 
     Array[ GetIndex<Ts>() ].reset(new producer<Ts>()); 
    }...); 
    } 
    // typed get: 
    template<typename T> 
    producer<T>* GetItem() const { 
    return static_cast<producer<T>*>(Array[GetIndex<T>].get()); 
    } 
    // index get: 
    base* GetItem(std::size_t n) const { 
    if (n >= max_value) 
     return nullptr; 
    return Array[n].get(); 
}; 

這無助尚未。添加在邁克爾的回答是:

class FactoryBonusModifier 
{ 
public: 
    /// Function to overload 
    virtual BonusModifierAbstract* createBonus() const = 0; 
protected: 
}; 

template<typename Bonus> 
class Factory : public FactoryBonusModifier 
{ 
public: 
    virtual Bonus* createBonus() const 
    { 
    return new Bonus(); 
    } 
}; 

再喂Factory和支持的類型的列表,以mapping

type_list< ItemBonus, InheritBonus, TaxBonus, PerformanceBonus > BonusList; 

typedef mapping< BonusList, Factory, FactoryBonusModifier > MetaFactory_t; 
MetaFactory_t MetaFactory; 

,我們正在做。

MetaFactory_t::Enum是一種代表單個工廠的類型,以及它們在數組中的偏移量。要獲得給定獎勵類型的Enum值,請提供MetaFactory_t::GetIndex<BonusType>()

如果您想要某個特定獎金類型的工廠,MetaFactory.GetItem<BonusType>()將返回正確類型的指針Factory<BonusType>。如果您有Enumn,則MetaFactory.GetItem(n)會返回指向FactoryBonusModifier基類的指針(如果n超出範圍,則返回nullptr)。

工廠的生命週期由MetaFactory對象的生存期管理。

簡而言之,代碼生成通常用於模板之前的這類事情。但variardic模板使整數<->類型映射相當簡單,並允許相當令人印象深刻的代碼生成。

作爲一個附帶的好處,這個版本包含了許多python生成版本沒有的類型安全。我甚至可以寫一個需要n的函數,並用指向正確問題工廠類型的指針調用傳入函數,從而生成更多的類型安全性。

現在,如果你有超過幾百種獎勵類型,這種技術變得更加困難,因爲編譯器遞歸限制被擊中。即使如此,仍有技術可以用這種方式處理1000種類型的清單(然而,它們更笨拙)。

上面的代碼沒有被編譯,所以幾乎肯定包含錯誤,但基本設計是堅實的 - 我以前做過。