C ++ метапрограммирование - PullRequest
1 голос
/ 30 сентября 2010

У меня следующая проблема:

Предположим, у меня есть какой-то базовый счетчик class Counter. И предположим, что у нас также есть несколько наборов классов, которые можно сосчитать. Давайте назовем некоторые из них class CountedA и class CountedB.

Теперь каждый класс, который может быть подсчитан (например, CountedA и CountedB) , имеет следующие статически объявленные части: one enum и одна int часть, который действует как часть подсчитанных данных .

Например, это объявление может выглядеть следующим образом:

enum CountedType { A, B };

template <CountedType Type, int N>
class Counted { };

// Now we can declare 'CountedA' and 'CountedB'
typedef Counted<A, 25> CountedA;
typedef Counted<B, 7> CountedB;

Теперь объявление счетчика:

// C++0x variadic or simply bunch of 'typename XX' definitions for C++03
template <typename T0, typename T1, typename ...>
class Counter
{
   // I don't know how to implement this
   // for now!
   int GetTotalN() { ... }

   // Retrieve the corresponding type
   // so that GetTypeAt<0> returns
   // enum from 'T0'
   template <int Pos>
   CountedType GetTypeAt() { ... }
};

Я хочу написать что-то вроде:

class RealCounter : public Counter<CountedA, CountedB> { };

И используйте его следующим образом:

RealCounter counter;
int n = counter.GetTotalN();
CountedType type = counter.GetTypeAt<0>();

Теперь я почти уверен, что это можно сделать. Но как лучше всего это реализовать? (не спрашивайте меня, зачем мне такие безумные вещи:)

boost::mpl предлагает что-нибудь для этого случая?

Спасибо.


Небольшое обновление:

В этом конкретном примере GetTotalN() должно вернуть 25 + 7.

Если мы добавим, например, typedef Counted<C, 2> CountedC, то результат для

RealCounter : public Counter<CountedA, CountedB, CountedC>

должно стать 25 + 7 + 2.

Ответы [ 2 ]

1 голос
/ 30 сентября 2010

Вот код C ++ 03, который работает (до 10 аргументов шаблона).Основная хитрость заключается в предоставлении классу Counter множественного наследования и передаче объектов типа Counter в шаблоны функций, которые должны выбирать базовый класс.Фактическое суммирование выполняется рекурсивно.

Counter.hpp

enum CountedType { A, B };

template <CountedType Type, int N>
struct Counted {};

struct DummyCounted {};

template <int Pos, typename T>
struct IndexedType {};

template <unsigned int Terms>
struct PartialSum
{
  template <typename CounterT>
  static int getSum(const CounterT& ctr)
  { return PartialSum<Terms-1>::getSum(ctr) + ctr.template GetNAt<Terms>(); }
};

template <> struct PartialSum<0U>
{
  template <typename CounterT>
  static int getSum(const CounterT& ctr)
  { return ctr.template GetNAt<0>(); }
};

template <typename T0, typename T1=DummyCounted,
  typename T2=DummyCounted, typename T3=DummyCounted,
  typename T4=DummyCounted, typename T5=DummyCounted,
  typename T6=DummyCounted, typename T7=DummyCounted,
  typename T8=DummyCounted, typename T9=DummyCounted>
class Counter :
  public IndexedType<0, T0>, public IndexedType<1, T1>,
  public IndexedType<2, T2>, public IndexedType<3, T3>,
  public IndexedType<4, T4>, public IndexedType<5, T5>,
  public IndexedType<6, T6>, public IndexedType<7, T7>,
  public IndexedType<8, T8>, public IndexedType<9, T9>
{
public:
  static int GetTotalN() {
    return PartialSum<9>().getSum( Counter() );
  }

  template <int Pos>
  static CountedType GetTypeAt() { return _getTypeAt<Pos>( Counter() ); }

  template <int Pos>
  static int GetNAt() { return _getNAt<Pos>( Counter() ); }

private:
  template <int Pos, CountedType Type, int N>
  static CountedType _getTypeAt(const IndexedType<Pos, Counted<Type,N> >&)
  { return Type; }

  template <int Pos, CountedType Type, int N>
  static int _getNAt(const IndexedType<Pos, Counted<Type,N> >&)
  { return N; }

  template <int Pos>
  static int _getNAt(const IndexedType<Pos, DummyCounted>&)
  { return 0; }

};

Counter.cpp

#include "Counter.hpp"
#include <iostream>

typedef Counted<A, 25> CountedA;
typedef Counted<B, 7> CountedB;

class RealCounter : public Counter<CountedA, CountedB> {};

int main()
{
  RealCounter counter;
  int n = counter.GetTotalN();
  CountedType type = counter.GetTypeAt<0>();

  std::cout << "n is " << n
            << "\ntype check is " << (type == A) << std::endl;
  return 0;
}

Выход:

n is 32
type check is 1

Этот шаблон C ++ 0x с переменным шаблоном выглядит интересным, но я еще не очень хорошо его рассмотрел.Но я думаю, что в C ++ 0x все функции этого примера (кроме, конечно, main) могут быть constexpr.

0 голосов
/ 30 сентября 2010

Я не уверен, почему вам нужно встраивать эти параметры в аргументы шаблонов, а не просто в конструктор, так как они являются одинаковыми типами для каждого "производного" типа CountedA / B.

В любом случае вы можете встраивать результирующие типы в std :: tuple, как показано в ссылке ниже (пример см. В классе сообщений). Затем создайте шаблонную функцию с переменным аргументом, аналогичную версии applyTuple в приведенной ниже ссылке, которая добавит все ваши целочисленные аргументы и вернет конечный результат, как только все аргументы будут развернуты. Что касается возврата значения enum для элемента в «Pos», просто вызовите get (tuple) .getEnum () или .value, чтобы получить его.

Как развернуть кортеж в аргументы функции-шаблона?

...