Is it possible for something like this to exist?
template<int Channel>
void deduce_mask(Matrix const &src, int mask[])
{
//I hope i could become a constant and the compiler would unroll the loop at compile time
for(int i = Channel; i != -1; --i)
{
//mapper is a helper class which translate two and three dimension into one dimension index
//constexpr makes it possible to find out the index at compile time
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];
}
}
instead of
template<int Channel>
class deduceMask
{
public:
static void deduce_mask(matrix const &src, int mask[]);
};
template<int Channel>
void deduce_mask(matrix const &src, int mask[])
{
mask[mapper(0, 1, Channel)] = src(row - 1, col)[Channel];
mask[mapper(1, 1, Channel)] = src(row, col)[Channel];
mask[mapper(2, 1, Channel)] = src(row + 1, col)[Channel];
deduceMask<Channel - 1>::deduce_mask(src, mask);
}
template<>
class deduceMask<-1>
{
public:
static void deduce_mask(matrix const &src, int mask[])
{
}
};
The second solution is the only solution I could come up of when I want the compiler to figure out the result at compile time. Do I have an easy way to make the "i" become constant value likethe metaprogramming solution? For me, a simple for loop is much more easier to work with rather than the metaprogramming version.
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With