1

同じ基本クラスから派生し、名前が異なり、動作がわずかに異なる新しいクラスを簡単に作成するためのマクロを作成したいと思います。

私は既に持っています

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に書かれた実装部分。

コピー/貼り付けをできるだけ少なくして、これらの新しいクラスの列挙型と配列を作成するマクロを作成できるかどうかを知りたいです。

結局、私は次のようなものが欲しいです

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

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

2 に答える 2

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>(),
};
于 2013-03-15T04:03:34.127 に答える
1

これは、@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_typeindex_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)FactoryBonusModifiernullptrn

工場の存続期間は、MetaFactoryオブジェクトの存続期間によって管理されます。

要するに、コード生成は、テンプレートの前にこの種のものによく使用されていました。しかし、バリアードテンプレートを使用すると、整数<->型のマッピングが非常に簡単になり、非常に印象的なコード生成が可能になります。

副次的なボーナスとして、このバージョンには、Pythonで生成されたバージョンにはなかった多くの型安全性が含まれています。を取り、問題のファクトリの適切な型へのポインタを使用して渡されたファンクタを呼び出す関数を記述してn、さらに型の安全性を生成することもできます。

さて、100を超えるボーナスタイプがある場合、コンパイラの再帰制限に達するため、この手法はより困難になります。それでも、数千のタイプのリストをこの方法で処理できるようにする手法があります(ただし、より厄介です)。

上記のコードはコンパイルされていないため、ほぼ確実にバグが含まれていますが、基本設計はしっかりしています。これは以前に行ったことがあります。

于 2013-03-16T01:45:39.007 に答える