I have several segments of code in my c++ project which look something like this:
system<0>::global_instance.do_something();
system<1>::global_instance.do_something();
system<2>::global_instance.do_something();
system<3>::global_instance.do_something();
//...
system<29>::global_instance.do_something();
system<30>::global_instance.do_something();
system<31>::global_instance.do_something();
It seems a bit repetitive, right? It would be great if I could just do something like this:
for(int i = 0; i < 32; i++)
{
system<i>::global_instance.do_something();
}
Unfortunately, that won't work because the value of i
isn't known at compile time and therefore cannot be used as a template parameter for system
.
What I need is a way to expand or unroll loops at compile time.
I've seen some implementations which use templates to achieve unrolling, but they don't work for what I'm trying to do. Ideally, the loop unrolling would take place during preprocessing, and could take any arbitrary statement as input.
For example, this:
#unroll NUM 0 5
foo<NUM>();
#endunroll
Would get translated into this:
foo<0>();
foo<1>();
foo<2>();
foo<3>();
foo<4>();
foo<5>();
And this:
#unroll NUM 0 5
blah blah blah NUM
#endunroll
Would get translated into this:
blah blah blah 0
blah blah blah 1
blah blah blah 2
blah blah blah 3
blah blah blah 4
blah blah blah 5
Even though blah blah blah NUM
is a syntactically incorrect statement, it should still get copied and every instance of the token NUM should get replaced by the appropriate number value.
Is there any way to achieve this in vanilla c++? Or are there any special c++ compilers which add this functionality?
Edit: I found an answer from @HTNW in the comments.
#include <iostream>
using namespace std;
template<auto begin, auto end>
inline void unroll(auto f)
{
if constexpr(begin < end)
{
f.template operator()<begin>();
unroll<begin + 1, end>(f);
}
}
template<int NUM>
struct thingy
{
static void print(){cout << NUM << endl;}
};
int main()
{
unroll<0,8>([]<int i>()
{
thingy<i>::print();
});
}
Output:
0
1
2
3
4
5
6
7
The line which makes this work is f.template operator()<begin>();
. I have no idea what this line means and I've never seen it before in my life. If someone could explain it to me, that would be great.