Is it possible to develop static for loop in c++?
With if constexpr
we can improve on AOK's solution.
template <int First, int Last, typename Lambda>
inline void static_for(Lambda const& f)
{
if constexpr (First < Last)
{
f(std::integral_constant<int, First>{});
static_for<First + 1, Last>(f);
}
}
With this we can get rid of that ::apply
static_for<0, Channel>([&](auto i)
{
// code...
mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change
std::get<i.value>(some_tuple); // But here you must get the member .value
// more code...
});
Unfortunately you still have to write i.value
.
Note that this would not be possible without if constexpr
because AOK's way would require partial template specialization of static_for
.
Template metaprogramming in C++ is pure functional programming, and in pure functional programming you don't get to use loops like for or while and you don't get to have any mutable data at all. All you have is recursion. To make working with recursion easier, you need to rise abstraction level a bit. The recursive code that you have is fine, but the iteration and work can be split apart:
template <int First, int Last>
struct static_for
{
template <typename Fn>
void operator()(Fn const& fn) const
{
if (First < Last)
{
fn(First);
static_for<First+1, Last>()(fn);
}
}
};
template <int N>
struct static_for<N, N>
{
template <typename Fn>
void operator()(Fn const& fn) const
{ }
};
Now that you have this meta-function, you can write your deduce_mask function like this:
template<int Channel>
void deduce_mask(Matrix const &src, int mask[])
{
static_for<0, Channel>()([&](int i)
{
mask[mapper(0, 1, i)] = src(row - 1, col)[i];
mask[mapper(1, 1, i)] = src(row, col)[i];
mask[mapper(2, 1, i)] = src(row + 1, col)[i];
});
}
Visual C++ 2012 with /Ob1 command line switch compiles this code into this:
push 0
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 1
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 2
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 3
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
push 4
call <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h)
...
If you can't use lambda functions, you need to write a functor. Functor has one advantage over lambda function - you can specify a calling convention (if you don't mind doing that). If the operator() of the functor has __fastcall
calling convention then you will see mov edx, x
instead of push x
in the assembler code.
lego's response, while elegant and awesome, won't compile if you want the index to go into a template - e.g. std::get<i>(some_tuple)
In case you want to implement this additional feature in the future, the below code will work and should be backwards-compatible with lego's solution (except that I use a static apply method instead of operator()):
template <int First, int Last>
struct static_for
{
template <typename Lambda>
static inline constexpr void apply(Lambda const& f)
{
if (First < Last)
{
f(std::integral_constant<int, First>{});
static_for<First + 1, Last>::apply(f);
}
}
};
template <int N>
struct static_for<N, N>
{
template <typename Lambda>
static inline constexpr void apply(Lambda const& f) {}
};
Now you can do the following:
static_for<0, Channel>::apply([&](auto i) // Changed from '(int i)'. In general, 'auto' will be a better choice for metaprogramming!
{
// code...
mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change
std::get<i.value>(some_tuple); // But here you must get the member .value
// more code...
});
Tested in VC++ 2015. I didn't research why this works, but I can only assume that std::integral_constant<T,...>
defines an implicit cast to T
using value, but the compiler can't figure out that the implicit cast produces a constexpr
, so you have to retrieve the value using i.value
, which is a constexpr
.
Addressing @tom's question in the comment If you want to iterate over a parameter pack, you can do the following (same implementation):
template<typename... Args>
inline constexpr auto foo(const Args&... args)
{
static_for<0,sizeof...(Args)>::apply([&](auto N)
{
std::cout << std::get<N.value>(std::make_tuple(args...));
});
}
foo(1,"a",2.5); // This does exactly what you think it would do
If std::get<N.value>(std::make_tuple(args...))
looks ugly, you can create another constexpr
function that minimizes the code.