Использование шаблонов для реализации универсального фильтра - PullRequest
2 голосов
/ 17 февраля 2012

У меня есть фильтр, который должен работать на произвольных тензорах.Для моего случая достаточно, когда фильтр работает с тензорами ранга 1,2 и 3, которые являются списками, матрицами и 3d-матрицами или объемами соответственно.Кроме того, фильтр может применяться в каждом возможном направлении.Для списка это только одно, для матриц существует 2 возможных направления (а именно для направления X и Y), а для томов существует 3 возможных направления.

Прежде чем углубляться в детали, позвольте мне спроситьпервый вопрос: У меня все в порядке с фильтром, или я забыл что-то важное, что может доставить мне неприятности позже?Я не новичок в шаблонах C ++, но я не чувствую себя как рыба в воде.Можно ли сжимать этот макет дальше (может быть, есть способ обхода фиктивных XDirection классов или более коротких Type2Type)?

Основная процедура фильтра для каждого ранга тензора и для каждого направлениятот же самый.Здесь всего несколько строк кода, функция callKernel, которые отличаются.Чтобы заставить перегруженный operator() вызвать правильную функцию callKernel, это единственная интересная часть в коде ниже.Поскольку частичная специализация для шаблонов не работает для методов класса, вы можете преобразовать аргументы шаблона в реальный тип класса и передать его в качестве фиктивного аргумента в callKernel.

Следующий код - это макет доУровень 2. Он компилируется с g++ и может быть испытан.

template <class DataType, int Rank>
class Tensor { };

class XDirection;
class YDirection;

template <class TensorType, class Direction>
struct Type2Type {
  typedef TensorType TT;
  typedef Direction D;
};

template <class TensorType, class Direction>
struct Filter {
  Filter(const TensorType &t){}
  TensorType operator()(){
    /* much code here */
    callKernel(Type2Type<TensorType,Direction>());
    /* more code */
    TensorType result;
    return result;
  } 
  void callKernel(Type2Type<Tensor<double,1>, XDirection>) {} 
  void callKernel(Type2Type<Tensor<double,2>, XDirection>) {}
  void callKernel(Type2Type<Tensor<double,2>, YDirection>) {}
};

int main(void) {
  Tensor<double, 2> rank_two_tensor;
  Filter<Tensor<double,2>,XDirection> f(rank_two_tensor);
  f();
}

Позвольте мне добавить несколько важных вещей: необходимо, чтобы логика фильтра была в operator(), потому что вы видите здесьсобирается использовать с Intel Threading Building Blocks , которые требуют эту структуру.Очень важно, чтобы callKernel был встроен.Из всего, что я знаю, так и должно быть.

Заранее благодарим за любой полезный и критический комментарий.

1 Ответ

1 голос
/ 17 февраля 2012

Во-первых, неплохо для первой попытки шаблонов.

Если у вас есть последняя версия GCC, вы можете упростить это, как это, есть лучший способ условно выполнить код для типа, используя std::is_same<>. Он вернет true, если типы идентичны. Это также делает ваши намерения более понятными.

#include <type_traits>

template <class TensorType, class Direction> 
struct Filter { 
  Filter(const TensorType &t) { } 

  TensorType operator()(){ 
    /* much code here */ 
    callKernel(); 
    /* more code */ 
    TensorType result; 
    return result; 
  }  

  void callKernel() {
      // Check which type is called at compile time (the if will be simplified by the compiler)
      if (std::is_same<TensorType, Tensor<double, 2> >::value) {
          if (std::is_same<Direction, XDirection>::value) {
              // do stuff
          } else {
              ...
          }
      } else if (...) {
          ...
      }
  }  
}; 

Изменить: Если вы хотите, вы можете даже переместить его на op(), чтобы убедиться, что код встроен.

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