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

这是对@MichaelAnderson 解决方案的补充。

做同样的FactoryBonusModifier事情,但不要enum在 python 中生成 and 数组。在模板元编程中进行。

首先,一些样板。这些东西只是模板元编程的工具包:

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_listnth_type它们从列表中的索引中提取一个类型,并index_in获取一个类型并返回它的索引。

最后,我们有一个名为 的辅助函数do_in_order,我们可以将其与参数包扩展一起使用来迭代参数包并对参数包中的每个元素执行操作。与其他 hack 相比,我更喜欢它,因为它很容易编写并且带来的惊喜更少。

一旦我们有了这套基本的工具,我们就可以很容易地编写一个工厂工厂:

// 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>. 如果你有Enumvalue n,则MetaFactory.GetItem(n)返回一个指向FactoryBonusModifier基类的指针(nullptr如果n超出范围)。

工厂生命周期由MetaFactory对象的生命周期管理。

简而言之,代码生成通常在模板之前用于此类事情。但是可变参数模板使整数<->类型映射变得非常容易,并且可以生成令人印象深刻的代码。

作为附带的好处,这个版本包括许多 python 生成的版本没有的类型安全。我什至可以编写一个函数n,并调用传入的函子,并使用指向相关工厂的正确类型的指针,从而产生更多的类型安全性。

现在,如果您有超过 100 种奖励类型,则此技术会变得更加困难,因为已达到编译器递归限制。即使这样,也有一些技术允许以这种方式处理 1000 种类型的列表(但是,它们更尴尬)。

上面的代码还没有编译,所以几乎可以肯定包含错误,但基本设计是可靠的——我以前做过。

于 2013-03-16T01:45:39.007 回答
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 回答