c++类工厂宏
C++ class factory macro
我想创建一个宏来轻松创建新的类,这些类派生自相同的基类,但名称不同,行为略有不同。
我已经有
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(),
...,
}
如果可以的话,我会避免使用宏。我会用模板代替。
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();
}
}
所以棘手的部分是创建enum和工厂列表。为此,我将生成代码,而不是尝试使用模板。您可以很容易地将代码生成集成到大多数构建过程中。但是你需要仔细阅读。
我个人会使用非常简单的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>(),
};
这是对@MichaelAnderson的解决方案的扩展。
执行相同的FactoryBonusModifier
,但不生成enum
和python中的数组。在模板元编程中完成。
首先是一些样板文件。这些东西只是模板元编程的工具包:
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相比,我更喜欢它,因为它很容易编写,而且产生的意外也更少。
一旦我们有了这些基本的工具,我们就可以很容易地编写一个Factory Factory:
// 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>
的指针。如果Enum
的值为n
,则MetaFactory.GetItem(n)
返回指向FactoryBonusModifier
基类的指针(如果n
超出范围,则返回指向nullptr
的指针)。
工厂生命周期由MetaFactory
对象的生命周期管理。
简而言之,在模板出现之前,代码生成通常用于这类事情。但是可变模板使得整型<->
类型映射非常容易,并且允许相当令人印象深刻的代码生成。
作为附带的好处,这个版本包含了许多python生成的版本所没有的类型安全。我甚至可以编写一个接受n
的函数,并调用一个传入的函子,带一个指针指向所讨论的工厂的正确类型,从而产生更多的类型安全性。
现在,如果您有超过100个额外类型,则该技术将变得更加困难,因为会达到编译器递归限制。即使这样,也有一些技术允许以这种方式处理1000个类型的列表(然而,它们更尴尬)。
上面的代码没有被编译过,所以几乎可以肯定包含bug,但是基本的设计是可靠的——我以前做过这个。
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 使用Unique_ptr确保工厂中的对象唯一
- 带有继承的C++工厂
- 如何在工厂方法中返回指向基于基础操作系统的派生类的有效指针
- 工厂方法:分配和strcpy_s的差异
- C++库和自注册类:客户端应用程序中的工厂映射为空
- 这个工厂类在这个C++视频中的意义何在?
- 此工厂功能有什么问题?
- 注册对对象工厂的调用会导致段错误
- 工厂设计模式优化
- 在 C++ 中返回新的构造函数(*this)工厂
- 确保在编译期间仅同时使用来自同一工厂的对象
- 在工厂或C++类中包含数据库 .h 文件
- 在基类中编写工厂方法
- WIC 工厂将始终在 Windows7 上为 nullptr("What's a Creel?"教程中使用)
- 抽象工厂结构的差异
- 编写可测试的代码 - lambda 函数和unique_ptr中的basic_istream工厂
- 下面抽象工厂设计模式的实现是正确的吗
- 没有用于初始化模拟工厂的匹配构造函数
- 从工厂方法返回的ComPtr的引用计数增加两次