I am trying to write a C++ loop that I want evaluated at compile-time. This is because I need to use the looping variable as a template argument to initialize a class. In the very simple case it would look something like:
for (unsigned int i = 1; i < 8; i++) {
Vector<i> foo;
// do something with foo
}
After going through some similar StackOverflow questions, I found a way to write a static for loop recursively. Now my code looks something like this:
template <unsigned int i, unsigned int end>
struct static_for {
template <typename Lambda>
void operator()(const Lambda& function) const {
if (i < end) {
function(i);
static_for<start + 1, end>(function);
}
}
};
template <int N>
struct static_for<N, N> {
template <typename Lambda>
void operator()(const Lambda& function) const {
// This is just to avoid the unused variable warning - does nothing.
(void)function;
}
};
This works perfectly and as expected. In order to call this static_for
loop I can simply write:
static_for<0, 8>()([](int size) {
// some code here.
});
The problem, however, is that I can still not evaluate i
at compile time. As in I cannot create a templated object within the lambda expression:
static_for<0, 8>()([](int size) {
Vector<size> var; // does not compile as size is not a constexpr.
});
How can I make this work? My static_for
loop has the i
variable as a template argument which is available to it at compile time. I would like my lambda expression to receive this argument (size
) also at compile time rather than being passed in as a runtime variable. How would this work? Conceptually this seems simple and useful as a more general language feature as well.
If you can use C++14, you could pass an std::integral_constant
to a generic lambda instead of an int
. gcc.godbolt.org example
template <unsigned int i, unsigned int end>
struct static_for {
template <typename Lambda>
void operator()(const Lambda& function) const {
if (i < end) {
function(std::integral_constant<int, i>{});
static_for<start + 1, end>(function);
}
}
};
static_for<0, 8>()([](auto size) {
Vector<decltype(size)::value> var; // does not compile as size is not a constexpr.
});
I've made a bit different solution based on @Vittorio Romero's answer which should work with c++11 and g++ 4.9. You can run it here. In @Vittorio's solution you have to define Lambda
function outside block scope due to integral_constant
(see example code in comments) use. In my solution you have more lambda-like behaviour. You can define your structure within other function and pass to it any number of arguments.
[EDIT] Ok, it was a bit too late for my mind work ;) Unfortunately I can't come out with a type of lambda-like solution to work in compile-time. So you have to define your lambda structures outside block scope. You can run modified code here.
#include <type_traits>
#include <iostream>
template <int COUNT, int MAX, typename Lambda>
struct StaticFor
{
template <typename... Args>
static void impl(Args&&... args)
{
Lambda::impl(std::integral_constant<int, COUNT>(), std::forward<Args>(args)...);
StaticFor<COUNT + 1, MAX, Lambda>::impl(std::forward<Args>(args)...);
}
};
template <int N, typename Lambda>
struct StaticFor<N, N, Lambda>
{
template <typename... Args>
static void impl(Args&&... args)
{
}
};
static const int DIM = 3;
static const int POINTS = 6;
template <int m, typename T>
T foo(T value)
{
return value * m;
}
struct IterateDim
{
template <typename D, typename P>
static void impl(D dIdx, P pIdx, float (&smpl)[POINTS][DIM])
{
smpl[P::value][D::value] = foo<D::value>(P::value);
}
};
struct IteratePoints
{
template <typename P>
static void impl(P pIdx, float (&smpl)[POINTS][DIM])
{
StaticFor<0, DIM, IterateDim>::impl(pIdx, smpl);
}
};
int main(void)
{
float table[POINTS][DIM];
StaticFor<0, POINTS, IteratePoints>::impl(table);
for (int p = 0; p < POINTS; ++p)
{
for (int d = 0; d < DIM; ++d)
{
std::cout << table[p][d] << ", ";
}
}
}