これは、@MichaelAndersonのソリューションの拡張です。
同じことを行いますが、Pythonでand配列を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
渡したり操作したりするタイプのリストをパッケージ化したがあります。には2つの操作があります。これは、インデックスからリストから型を抽出し、型を取得して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>
。Enum
値がある場合は、基本クラスへのポインタを返しますn
(範囲外の場合)。MetaFactory.GetItem(n)
FactoryBonusModifier
nullptr
n
工場の存続期間は、MetaFactory
オブジェクトの存続期間によって管理されます。
要するに、コード生成は、テンプレートの前にこの種のものによく使用されていました。しかし、バリアードテンプレートを使用すると、整数<->
型のマッピングが非常に簡単になり、非常に印象的なコード生成が可能になります。
副次的なボーナスとして、このバージョンには、Pythonで生成されたバージョンにはなかった多くの型安全性が含まれています。を取り、問題のファクトリの適切な型へのポインタを使用して渡されたファンクタを呼び出す関数を記述してn
、さらに型の安全性を生成することもできます。
さて、100を超えるボーナスタイプがある場合、コンパイラの再帰制限に達するため、この手法はより困難になります。それでも、数千のタイプのリストをこの方法で処理できるようにする手法があります(ただし、より厄介です)。
上記のコードはコンパイルされていないため、ほぼ確実にバグが含まれていますが、基本設計はしっかりしています。これは以前に行ったことがあります。