Преобразовать параметр шаблона в разделенный запятыми список параметров шаблона - PullRequest
2 голосов
/ 21 марта 2019

Извиняюсь, если заголовок вводит в заблуждение или если на этот вопрос уже был дан ответ.

Я работаю с модулем Тенгора Эйгена, в частности с классом Eigen::TensorFixedSize, поскольку знаю форму во время компиляции.

По сути, поскольку это проблема Лоренца, тензор ранга 2 будет иметь вид,

Eigen::TensorFixedSize<double, Eigen::Sizes<4,4>> t;

тензор ранга 3,

Eigen::TensorFixedSize<double, Eigen::Sizes<4,4,4>> t;

и т. Д.

Я хотел бы написать класс, способный инициализировать тензор в зависимости от ранга.В псевдокоде

template<typename RANK>
 class Foo
 {
  public:
   ...
  private:
   Eigen::TensorFixedSize<double, Eigen::Sizes<4,4,4,...,RANK times>> _t;
 }

каким-либо образом преобразует параметр шаблона из

<2> --> <4,4>

<3> --> <4,4,4>

до произвольного целого без знакав <N>.

Можно ли это сделать?

Ответы [ 2 ]

3 голосов
/ 21 марта 2019

Да.

template <class RankIdx>
struct TensorForRank;

template <std::size_t... RankIdx>
struct TensorForRank<std::index_sequence<RankIdx...>> {
    using type = Eigen::TensorFixedSize<double, Eigen::Sizes<(void(RankIdx), 4)...>>;
};

template <std::size_t Rank>
using TensorForRank_t = typename TensorForRank<std::make_index_sequence<Rank>>::type;

Использовать как:

template<std::size_t Rank>
class Foo
{
    // ...
private:
    TensorForRank_t<Rank> _t;
};

Посмотреть вживую в Wandbox (с заполнителем test<...> шаблон, так как Eigen недоступен)

1 голос
/ 21 марта 2019

Ответ Квентина очень хорош, и с чем бы я пошел.

Единственным недостатком является «бесполезная» генерация последовательности индексов [0, 1, 2, ...], значения которой мы игнорируем и заменяем нашими собственными.

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

Начните с создания типа, который может содержать несколько значений std::size_t, добавив псевдоним a std::integer_sequence:

template<std::size_t... vals>
using value_sequence = std::integer_sequence<std::size_t, vals...>;

Цель состоит в том, чтобы в конечном итоге создать value_sequence<4, 4, 4>, а затем создать экземпляр Eigen::Sizes, используя эти 4.

Следующее, что нам нужно сделать, это объединить две последовательности, потому что мы собираемся построить их так:

concat(value_sequence<4>, value_sequence<4>) --> value_sequence<4, 4>

Мы можем сделать это с помощью метода-заглушки, который принимает два типа value_sequence и возвращает объединенный результат. Обратите внимание, что мы никогда не пишем определения для этого метода; мы просто используем преимущества системы типов для написания меньшего количества кода, чем требовалось бы для специализации шаблона:

template<std::size_t... lhs, std::size_t... rhs>
constexpr auto concat(value_sequence<lhs...>, value_sequence<rhs...>) -> value_sequence<lhs..., rhs...>;

На данный момент у нас достаточно машин для создания value_sequence<4,4,4>, поэтому теперь нам нужен способ указать значение, которое мы хотим использовать (4), и количество раз, чтобы повторить его (3) для его получения:

template<std::size_t value, std::size_t num_repeats>
struct repeated_value
{
    using left_sequence = value_sequence<value>;
    using right_sequence = typename repeated_value<value, num_repeats-1>::type;
    using type = decltype(concat(left_sequence{}, right_sequence{}));
};

repeated_value<4, 3>::type производит value_sequence<4, 4, 4>.

Поскольку repeated_value<...>::type является рекурсивным, нам необходимо предоставить базовый вариант с частичной специализацией:

template<std::size_t value>
struct repeated_value<value, 1>
{
    using type = value_sequence<value>;
};

Отлично. Нам осталось только получить класс Eigen::Sizes и тип value_sequence<4, 4, 4> и произвести Eigen::Sizes<4, 4, 4>.

Мы можем сделать это снова с частичной специализацией шаблона:

template<template<std::size_t...> class T, class...>
struct InstantiateWithRepeatedVals;

template<template<std::size_t...> class T, std::size_t... vals>
struct InstantiateWithRepeatedVals<T, value_sequence<vals...>> 
{
    using type = T<vals...>;
};

Вот оно! Добавьте несколько помощников, чтобы упростить его использование, и все готово:

template<std::size_t value, std::size_t num_repeats>
using repeated_value_t = typename repeated_value<value, num_repeats>::type;

template<template<std::size_t...> class T, std::size_t Value, std::size_t N>
using InstantiateWithRepeatedVals_t = typename InstantiateWithRepeatedVals<T, repeated_value_t<Value, N>>::type;

Теперь мы можем использовать его так:

using my_type = InstantiateWithRepeatedVals_t<EigenSizes, 4, 3>;
static_assert(std::is_same_v<my_type, EigenSizes<4, 4, 4>>);

Live Demo

...