【问题标题】:C++ class factory macro [duplicate]C ++类工厂宏[重复]
【发布时间】:2013-03-15 03:33:06
【问题描述】:

我想制作一个宏来轻松创建新类,这些新类派生自同一个基类,但名称不同,行为略有不同。

我已经有了

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

【问题讨论】:

  • 我不会说这是严格重复的,因为这专门(至少部分)涉及使用宏来达到目的,而另一个问题则没有。

标签: c++ macros c-preprocessor factory


【解决方案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>(),
};

【讨论】:

  • 宏在模板之前经常用于这种事情,很有趣。
【解决方案2】:

这是对@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 的辅助函数,我们可以将它与参数包扩展一起使用来迭代参数包并对参数包中的每个元素执行操作。与其他 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&lt;BonusType&gt;() 会为您提供。

如果您想要给定奖励类型的工厂,MetaFactory.GetItem&lt;BonusType&gt;() 将返回一个正确类型的指向Factory&lt;BonusType&gt; 的指针。如果您有Enumn,则MetaFactory.GetItem(n) 返回指向FactoryBonusModifier 基类的指针(如果n 超出范围,则返回nullptr

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

简而言之,代码生成通常在模板之前用于此类事情。但是可变参数模板使 integer&lt;-&gt;type 映射非常简单,并且可以生成令人印象深刻的代码。

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

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

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

【讨论】:

    猜你喜欢
    • 2010-12-17
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    相关资源
    最近更新 更多