while sentencia que for estructura ejercicios dev ciclos ciclo anidados c++ templates c++11 metaprogramming c++14

c++ - sentencia - que es el ciclo for



Cómo generar bucles anidados en tiempo de compilación (4)

Algo como esto (NOTA: tomo la "forma" como un argumento de plantilla variable).

#include <iostream> template <int I, int ...N> struct Looper{ template <typename F, typename ...X> constexpr void operator()(F& f, X... x) { for (int i = 0; i < I; ++i) { Looper<N...>()(f, x..., i); } } }; template <int I> struct Looper<I>{ template <typename F, typename ...X> constexpr void operator()(F& f, X... x) { for (int i = 0; i < I; ++i) { f(x..., i); } } }; int main() { int v = 0; auto f = [&](int i, int j, int k, int l) { v += i + j + k + l; }; Looper<1, 3, 5, 2>()(f); auto g = [&](int i) { v += i; }; Looper<5>()(g); std::cout << v << std::endl; }

Tengo un número entero N que sé en tiempo de compilación. También tengo una matriz std :: que contiene números enteros que describen la forma de una matriz N- dimensional. Quiero generar bucles anidados, como se describe a continuación, en tiempo de compilación, utilizando técnicas de metaprogramación.

constexpr int N {4}; constexpr std::array<int, N> shape {{1,3,5,2}}; auto f = [/* accept object which uses coords */] (auto... coords) { // do sth with coords }; // This is what I want to generate. for(int i = 0; i < shape[0]; i++) { for(int j = 0; j < shape[1]; j++) { for(int k = 0; k < shape[2]; k++) { for(int l = 0; l < shape[3]; l++) { f(i,j,k,l) // object is modified via the lambda function. } } } }

Tenga en cuenta que el parámetro N se conoce en tiempo de compilación, pero puede cambiar de manera impredecible entre compilaciones, por lo tanto, no puedo codificar los bucles como se indicó anteriormente. Idealmente, el mecanismo de generación de bucles proporcionará una interfaz que acepta la función lambda, genera los bucles y llama a la función que produce el código equivalente como se indicó anteriormente. Soy consciente de que se puede escribir un ciclo equivalente en el tiempo de ejecución con un ciclo while y una matriz de índices, y ya hay respuestas a esta pregunta. Sin embargo, no estoy interesado en esta solución. Tampoco estoy interesado en soluciones que involucren la magia del preprocesador.


Otra variante de lo mismo:

template <size_t shape_index, size_t shape_size> struct Looper { template <typename Functor> void operator()(const std::array<int, shape_size>& shape, Functor functor) { for (int index = 0; index < shape[shape_index]; ++index) { Looper<shape_index + 1, shape_size>() ( shape, [index, &functor](auto... tail){ functor(index, tail...); } ); } } }; template <size_t shape_size> struct Looper<shape_size, shape_size> { template <typename Functor> void operator()(const std::array<int, shape_size>&, Functor functor) { functor(); } }; template <size_t shape_size, typename Functor> void loop(const std::array<int, shape_size>& shape, Functor functor) { Looper<0, shape_size>()(shape, functor); }

Ejemplo de uso:

constexpr size_t N {4}; constexpr std::array<int, N> shape {{1,3,5,2}}; void f(int i, int j, int k, int l) { std::cout << std::setw(5) << i << std::setw(5) << j << std::setw(5) << k << std::setw(5) << l << std::endl; } // ... loop(shape, f);

Demo en vivo


Supongo que esto es exactamente lo que pediste:

#include <array> #include <iostream> constexpr int N{4}; constexpr std::array<int, N> shape {{1,3,5,2}}; // Diagnositcs template<typename V, typename ...Vals> struct TPrintf { constexpr static void call(V v, Vals ...vals) { std::cout << v << " "; TPrintf<Vals...>::call(vals...); } }; template<typename V> struct TPrintf<V> { constexpr static void call(V v) { std::cout << v << std::endl; } }; template<typename ...Vals> constexpr void t_printf(Vals ...vals) { TPrintf<Vals...>::call(vals...); } // Unroll template<int CtIdx, typename F> struct NestedLoops { template<typename ...RtIdx> constexpr static void call(const F& f, RtIdx ...idx) { for(int i = 0; i < shape[CtIdx]; ++i) { NestedLoops<CtIdx + 1, F>::call(f, idx..., i); } } }; template<typename F> struct NestedLoops<N-1, F> { template<typename ...RtIdx> constexpr static void call(const F& f, RtIdx ...idx) { for(int i = 0; i < shape[N-1]; ++i) { f(idx..., i); } } }; template<typename F> void nested_loops(const F& f) { NestedLoops<0, F>::call(f); } int main() { auto lf = [](int i, int j, int k, int l) { t_printf(i,j,k,l); }; nested_loops(lf); return 0; }


Suponiendo que no desee desenrollar el bucle total, solo generación de i , j , k , etc. Tuplas de argumentos para f :

#include <stdio.h> #include <utility> // std::integer_sequence template< int dim > constexpr auto item_size_at() -> int { return ::shape[dim + 1]*item_size_at<dim + 1>(); } template<> constexpr auto item_size_at<::N-1>() -> int { return 1; } template< size_t... dim > void call_f( int i, std::index_sequence<dim...> ) { f( (i/item_size_at<dim>() % ::shape[dim])... ); } auto main() -> int { int const n_items = ::shape[0]*item_size_at<0>(); for( int i = 0; i < n_items; ++i ) { call_f( i, std::make_index_sequence<::N>() ); } }