Развертывание вложенных циклов с помощью метапрограммирования

У меня есть несколько вложенных циклов с небольшими размерами Я, Дж, … известны во время компиляции, например

for(int i = 0; i < I; ++i) {
for(int j = 0; j < J; ++j) {
// ...
// do sth with (i,j,...)
}
}

Мне нужно развернуть петли, используя размеры Я, Дж, … таким образом, что я могу использовать каждую комбинацию координат в время компиляции.

Чтобы уточнить, рассмотрим следующую структуру и возьмем 2 вложенных цикла с размерами Я = 2, J = 3.

template<int... I>
struct C {
static void f() {
// do sth
}
};

Я не могу использовать индексы я, дж (аналогично вышеуказанному) для индексации структуры С так как они не известны во время компиляции. Однако я хотел бы получить именно то, что было бы, если бы мне было позволено использовать индексы, например,

C<0,0>::f();
C<0,1>::f();
C<0,2>::f();
C<1,0>::f();
C<1,1>::f();
C<1,2>::f();

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

9

Решение

Вы можете сделать это, создавая шаблоны в древовидной форме, отслеживая посещенные узлы.

namespace detail{
//This is used to store the visited nodes
template<int...> struct int_pack;

//Primary template
template<typename, int... I>
struct C;

//This is the leaf node
template<int... Is>
struct C<int_pack<Is...>> {
//The loop body goes here
static void f() {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};

//This is the recursive case
template <int I, int... Is, int... PIs>
struct C<int_pack<PIs...>, I,Is...> {
template <std::size_t... Idx>
static void f_help (std::index_sequence<Idx...>) {
//Store the current node in the pack
//and call `C::f` for each loop iteration
(void)std::initializer_list<int> {
(C<int_pack<PIs...,Idx>,Is...>::f(), 0)...
};
}

//Use tag dispatching to generate the loop iterations
static void f() {
f_help(std::make_index_sequence<I>{});
}
};
}

//Helper alias
template<int... Is>
using C = detail::C<detail::int_pack<>, Is...>;

Использование довольно просто:

C<2,3>::f();

На Clang это печатает:

static void detail::C<detail::int_pack<0, 0>>::f() [I = <>]
static void detail::C<detail::int_pack<0, 1>>::f() [I = <>]
static void detail::C<detail::int_pack<0, 2>>::f() [I = <>]
static void detail::C<detail::int_pack<1, 0>>::f() [I = <>]
static void detail::C<detail::int_pack<1, 1>>::f() [I = <>]
static void detail::C<detail::int_pack<1, 2>>::f() [I = <>]

Live Demo


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

namespace detail{
template<int...> struct int_pack;

template<typename, int... I>
struct C;

template<int... Is>
struct C<int_pack<Is...>> {
template <typename Func>
static void f(const Func& func) {
func(Is...);
}
};

template <int I, int... Is, int... PIs>
struct C<int_pack<PIs...>, I,Is...> {
template <std::size_t... Idx, typename Func>
static void f_help (std::index_sequence<Idx...>, const Func& func) {
(void)std::initializer_list<int>{ (C<int_pack<PIs...,Idx>,Is...>::f(func), 0)... };
}

template <typename Func>
static void f(const Func& func) {
f_help(std::make_index_sequence<I>{}, func);
}
};
}

Вы бы использовали это так:

C<2,3>::f([](int i, int j){
std::cout << "i " << i << " j " << j << '\n';
});

Live Demo


Вот быстрая версия, с которой я издевался boost::hana, Вероятно, есть лучшие способы сделать это, но это должно дать вам представление о том, что можно сделать.

template <typename Func>
void unroll (const Func& func) {
func();
}

template <std::size_t I1, std::size_t... Is, typename Func>
void unroll (const Func& func) {
hana::for_each(hana::range_c<std::size_t, 0, I1>,
[&](auto x) {
unroll<Is...>([x, &func] (auto... xs) { func(x,xs...); });
});
}
8

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector