Это расширение решения @ 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
, которую мы можем использовать с расширением пакета параметров, чтобы перебирать пакет параметров и выполнять действие для каждого элемента в пакете параметров. Я предпочитаю его другим хитростям, потому что он прост в написании и дает меньше сюрпризов.
Когда у нас есть этот базовый набор инструментов, мы можем действительно легко написать 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();
};
который пока ничего не делает. Добавьте ответ Майклса:
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
базовый класс (и nullptr
, если n
вне допустимого диапазона).
Время жизни фабрик управляется временем жизни объекта MetaFactory
.
Короче говоря, генерация кода часто использовалась для такого рода вещей до создания шаблонов. Но вариационные шаблоны делают отображение integer<->
type чертовски простым и позволяют генерировать довольно впечатляющий код.
В качестве дополнительного бонуса эта версия включает в себя большую безопасность типов, которой не было в версии, созданной с помощью python. Я мог бы даже написать функцию, которая принимает n
и вызывает переданный функтор с указателем на соответствующий тип рассматриваемой фабрики, обеспечивая еще большую безопасность типов.
Теперь, если у вас было несколько 100 типов бонусов, этот метод усложняется, потому что достигнуты ограничения на рекурсию компилятора. Даже в этом случае существуют методы, позволяющие обрабатывать списки из 1000 типов таким образом (однако они более неудобны).
Приведенный выше код не был скомпилирован, поэтому почти наверняка содержит ошибки, но базовая конструкция надежна - я делал это раньше.
16.03.2013