Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate nested loops at compile time

I have an integer N which I know at compile time. I also have an std::array holding integers describing the shape of an N-dimensional array. I want to generate nested loops, as described bellow, at compile time, using metaprogramming techniques.

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

Note the parameter N is known at compile time but might change unpredictably between compilations, hence I can't hard code the loops as above. Ideally the loop generation mechanism will provide an interface which accepts the lambda function, generates the loops and calls the function producing the equivalent code as above. I am aware that one can write an equivalent loop at runtime with a single while loop and an array of indices, and there are answers to this question already. I am, however, not interested in this solution. I am also not interested in solutions involving preprocessor magic.

like image 428
Teodor Nikolov Avatar asked Jul 15 '16 10:07

Teodor Nikolov


People also ask

How do you create nested loops?

A nested loop is a loop within a loop, an inner loop within the body of an outer one. How this works is that the first pass of the outer loop triggers the inner loop, which executes to completion. Then the second pass of the outer loop triggers the inner loop again. This repeats until the outer loop finishes.

What is the run time of nested for loop?

The outer loop's runtime (by itself) is O(n), and the inner loop's runtime is O(n-i). So the loop's time would be (n)(n-i), and when you throw away the constant i, the runtime would be O(n^2).

Can you have nested for loops?

Nested Loops The placing of one loop inside the body of another loop is called nesting. When you "nest" two loops, the outer loop takes control of the number of complete repetitions of the inner loop. While all types of loops may be nested, the most commonly nested loops are for loops.


2 Answers

Something like this (NOTE: I take the "shape" as a variadic 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; } 
like image 150
Nim Avatar answered Sep 18 '22 13:09

Nim


Assuming you don't want total loop unrolling, just generation of i, j, k etc. argument tuples for 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>() );     } } 
like image 20
Cheers and hth. - Alf Avatar answered Sep 19 '22 13:09

Cheers and hth. - Alf