C ++类工厂宏

时间:2013-03-15 03:33:07

标签: c++ macros c-preprocessor factory

我想创建一个宏来轻松创建新的类,这些类派生自具有不同名称和略微不同行为的相同基类。

我已经

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(),
    ...,
}

2 个答案:

答案 0 :(得分: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>(),
};

答案 1 :(得分:1)

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

执行相同的FactoryBonusModifier,但不要在python中生成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();
};

尚未做任何事情。加入Michaels的回答:

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基类的指针(如果nullptr不在,则返回n范围内)。

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

简而言之,代码生成通常用于模板之前的这类事情。但是,变量模板使整数<->类型的映射非常简单,并且允许非常令人印象深刻的代码生成。

作为副作用,这个版本包含许多类型安全性,而python生成的版本没有。我甚至可以编写一个带n的函数,并调用传入的仿函数,并指向正确的工厂类型指针,从而产生更多类型的安全性。

现在,如果你有超过100种奖励类型,这种技术变得更加困难,因为编译器递归限制被击中。即使这样,也有一些技术允许以这种方式处理1000种类型的列表(但是,它们更加笨拙)。

以上代码尚未编译,因此几乎肯定包含错误,但基本设计是可靠的 - 我之前已经这样做了。