2016-07-15 24 views
27

Ich habe eine ganze Zahl N, die ich zur Kompilierzeit kenne. Ich habe auch eine Std :: Array Halte Ganzzahlen beschreiben die Form eines N-dimensionalen Array. Ich möchte geschachtelte Schleifen, wie unten beschrieben, zur Kompilierzeit unter Verwendung von Metaprogrammierungstechniken erzeugen.So generieren Sie verschachtelte Schleifen zur Kompilierzeit

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. 
       } 
      } 
    } 
} 

Hinweis der Parameter N wird zum Zeitpunkt der Kompilierung bekannt, aber möglicherweise unvorhersehbar zwischen Compilations ändern, daher kann ich nicht hart codiert die wie oben Schleifen. Im Idealfall wird der Schleifenerzeugungsmechanismus eine Schnittstelle bereitstellen, die die Lambda-Funktion akzeptiert, die Schleifen erzeugt und die Funktion aufruft, die den äquivalenten Code wie oben erzeugt. Mir ist bewusst, dass man zur Laufzeit eine äquivalente Schleife mit einer einzelnen while-Schleife und einem Array von Indizes schreiben kann, und es gibt bereits Antworten auf diese Frage. Ich bin jedoch nicht an dieser Lösung interessiert. Ich bin auch nicht an Lösungen interessiert, die Präprozessormagie beinhalten.

+0

Ist das nicht der Compiler Entrollen der Schleifen, wenn Optimierung ist eingeschaltet? – Arunmu

+0

Er könnte sehr gut einige Schleifen ausrollen, aber das ist nicht der Punkt. Ich frage nach Loop-Generierung nicht Schleife abrollen. Jedenfalls kann das Array Werte enthalten, die viel größer sind als die oben angegebenen Ganzzahlen. Ein vollständiges Abwickeln der Loops ist nicht möglich. –

+1

Verwenden Sie eine einzelne Schleife und berechnen Sie 'i',' j', 'k' und' l' usw. aus dem Index der einzelnen Schleifen. –

Antwort

24

So etwas wie diese (ANMERKUNG: Ich nehme die "Form" als variadische Template-Argument Set ..)

#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; 
} 
+0

Bedeutet das nicht, dass "f" "constexpr" ist? –

+0

@ Cheersandthth.-Alf - ähm, glaube nicht so .. Stick die 'cout' in der Lambda, sollte immer noch gut funktionieren ... – Nim

+0

@Nim - nette Lösung; +1; Der einzige Fehler, den ich sehe, ist, dass Sie eine Lambda-Funktion ('f()', 'g()' usw.) für jedes 'N' entwickeln müssen; Ich kann jetzt nicht im Allgemeinen lösen, aber wenn Sie C++ 14 verwenden können und die Lamda eine einfache Operation wie die Summe der Argumente ausführen muss, können Sie ein variadisches Lambda wie 'auto f = [] (auto i, auto ... ist) {auto ret = i; char unbenutzt [] {((ret + = ist), '0') ...}; Rückkehr ret; }; ' – max66

3

Ich nehme an, das ist genau das, was Sie gefragt:

#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; 
} 
2

Another Variante derselben Sache:

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); 
} 

Beispiel:

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); 

Live demo

4

Angenommen, Sie wollen nicht total Schleifenentrollen, gerade Generation von i, j, k usw. Argument Tupel für 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>()); 
    } 
} 
Verwandte Themen