Nano Hash - криптовалюты, майнинг, программирование

Макрос фабрики классов C ++

Я хотел бы создать макрос, чтобы легко создавать новые классы, производные от одного и того же базового класса с другим именем и немного другим поведением.

у меня уже есть

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

  • Я бы не сказал, что это строго повторяется, поскольку это касается конкретно (по крайней мере частично) использования макросов для этой цели, а другой вопрос - нет. 03.02.2015

Ответы:


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>(),
};
15.03.2013
  • Что интересно, макросы часто использовались для такого рода вещей раньше, чем шаблоны. 15.03.2013

  • 2

    Это расширение решения @ 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
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..