Compile Time Loops

compile time loops

Nope, it's not directly possible. Template metaprogramming is a pure functional language. Every value or type defined through it are immutable. A loop inherently requires mutable variables (Repeatedly test some condition until X happens, then exit the loop).

Instead, you would typically rely on recursion. (Instantiate this template with a different template parameter each time, until you reach some terminating condition).

However, that can solve all the same problems as a loop could.

Edit: Here's a quick example, computing the factorial of N using recursion at compile-time:

template <int N>
struct fac {
enum { value = N * fac<N-1>::value };
};

template <>
struct fac<0> {
enum { value = 1 };
};

int main() {
assert(fac<4>::value == 24);
}

Template metaprogramming in C++ is a Turing-complete language, so as long as you don't run into various internal compiler limits, you can solve basically any problem with it.

However, for practical purposes, it may be worth investigating libraries like Boost.MPL, which contains a large number of data structures and algorithms which simplify a lot of metaprogramming tasks.

c++ generic compile-time for loop

  1. Is there a way to write compile_time_for such that it accepts a template function as its first argument?

Short answer: no.

Long answer: a template function isn't an object, is a collection of objects and you can pass to a function, as an argument, an object, non a collection of objects.

The usual solution to this type of problem is wrap the template function inside a class and pass an object of the class (or simply the type, if the function is wrapped as a static method). That is exactly the solution you have adopted in your working code.


  1. If question 1. is positive, is there an overhead in the first working code, due to the fact that the routine create an object of type OperatorType at every loop iteration?

Question 1 is negative.


  1. Are there plans to introduce a feature like a compile-time for loop in the upcoming c++20?

I don't know C++20 enough to respond this question but I suppose not passing a set of function.

Anyway, you can do a sort of compile-time for loop using std::make_index_sequence/std::index_sequence starting from C++14.

By example, if you accept to extract the touple value outside your myprint() function, you can wrap it inside a lambda and write something as follows (using also C++17 template folding; in C++14 is a little more complicated)

#include <utility>
#include <tuple>
#include <string>
#include <iostream>

template <typename T>
void myprint (T const & t)
{ std::cout << t << " "; }

template <std::size_t start, std::size_t ... Is, typename F, typename ... Ts>
void ctf_helper (std::index_sequence<Is...>, F f, std::tuple<Ts...> const & t)
{ (f(std::get<start + Is>(t)), ...); }

template <std::size_t start, std::size_t end, typename F, typename ... Ts>
void compile_time_for (F f, std::tuple<Ts...> const & t)
{ ctf_helper<start>(std::make_index_sequence<end-start>{}, f, t); }

int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};

compile_time_for<0, 3>([](auto const & v){ myprint(v); }, x);

return 0;
}

If you really want extract the tuple element (or tuples elements) inside the function, the best I can imagine is transform your first example as follows

#include <utility>
#include <tuple>
#include <string>
#include <iostream>

template <std::size_t start, template <std::size_t> class OT,
std::size_t ... Is, typename... Args>
void ctf_helper (std::index_sequence<Is...> const &, Args && ... args)
{ (OT<start+Is>{}(std::forward<Args>(args)...), ...); }

template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }

template <std::size_t I>
struct print_tuple_i
{
template <typename ... U>
void operator() (std::tuple<U...> const & x)
{ std::cout << std::get<I>(x) << " "; }
};

int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};

compile_time_for<0u, 3u, print_tuple_i>(x);

return 0;
}

-- EDIT --

The OP asks

Is there some advantage of using index_sequence over my first code?

I'm not an expert but this way you avoid recursion.
Compilers have recursion limits, from the template point of view, that can be strict. This way you avoid they.

Also, your code does not compile if you set the template parameters end > start. (One can imagine a situation where you want the compiler to determine if a loop is instantiated at all)

I suppose you mean that my code does not compile if start > end.

The bad part is that there aren't check about this problem so the compiler try to compile my code also in this case; so encounter

 std::make_index_sequence<end-start>{}

where end - start is a negative number but used by a template that expect an unsigned number. So end - start become a very great positive number and this can cause problems.

You can avoid this problem imposing a static_assert() inside compile_time_for()

template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{
static_assert( end >= start, "start is bigger than end");

ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...);
}

Or maybe you can use SFINAE to disable the function

template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
std::enable_if_t<(start <= end)> compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }

If you want, using SFINAE you can add an overloaded compile_time_for() version to manage the end < start case

template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename ... Args>
std::enable_if_t<(start > end)> compile_time_for (Args && ...)
{ /* manage the end < start case in some way */ }

Is that possible to have a for loop in compile time with runtime or even compile time limit condition in c++11?

I would like to know if it is possible to have a for loop in compile time with runtime or even compile time limit condition in c++11

I don't know a reasonable way to have such loop with a runtime condition.

With a compile time condition... If you can use at least C++14, you can use a solution based on std::integer_sequence/std::make_integer_sequence (see Caleth answer) or maybe std::index_sequence/std::make_index_sequence (just a little more synthetic).

If you're limited with C++11, you can create a surrogate for std::index_sequence/std::make_index_sequence or you can create a recursive template struct with static function (unfortunately you can partially specialize a template function but you can partially specialize classes and structs).

I mean... something as follows

template <std::size_t I, std::size_t Top>
struct for_loop
{
static void func ()
{
templated_func<I>();
for_loop<I+1u, Top>::func();
}
};

template <std::size_t I>
struct for_loop<I, I>
{ static void func () { } };

that you can call

constexpr auto n = 10u;

for_loop<0, n>::func();

if you want to call templated_func() with values from zero to n-1u.

Unfortunately this solution is recursive so you can have troubles with compilers recursion limits. That is... works only if n isn't high.

How to generate nested loops at compile time

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

Unroll loop at compile time

A solution closer to the "macro way" can be the template recursivity :

template<int X> class MyClass { public: void foo() { std::cout << X << std::endl; } };
template<int X> inline void MyTest() { MyTest<X - 1>(); MyClass<X-1> bar; bar.foo(); }
template<> inline void MyTest<1>() { MyClass<0> bar; bar.foo(); }
int main()
{
MyTest<5>();
return 0;
}

And the output of this example is :

0
1
2
3
4


Related Topics



Leave a reply



Submit