In C++, I'm trying to write something similar to boost-mp11's mp_for_each
. However, whilst mp_for_each
always calls the supplied function for every T
in the given mp_list<Ts...>
, I'm trying to come up with a solution that stops traversal once a run-time call to the function yields a value evaluating to false
in an if-statement.
See the implementation of mp_for_each
and a usage example:
Usage example in Boost reference manual
Apparently, the implementation of mp_for_each
manages to pass the function argument as a constant expression, thus enabling the user to apply it where a constant expression is required. Whilst I took a different approach incorporating template tail recursion, I expected the function argument to be passed as a constant expression as welll. However, GCC complains that it "is not a constant expression".
My code looks like this:
#include <cstdlib>
#include <iostream>
#include <typeinfo>
#include <utility>
#include <boost/mp11.hpp>
template<std::size_t T_counter>
struct WhileGreaterZero
{
template<typename T_Function>
constexpr WhileGreaterZero(T_Function&& function)
{
if (function(T_counter)) // pass function argument
WhileGreaterZero<T_counter - 1>(std::forward<T_Function>(function));
}
};
template<>
struct WhileGreaterZero<0>
{
template<typename T_Function>
constexpr WhileGreaterZero(T_Function&&) {}
};
int main()
{
using boost::mp11::mp_at_c;
using boost::mp11::mp_list;
using boost::mp11::mp_size;
using Types = mp_list<bool, int, double>;
WhileGreaterZero<mp_size<Types>::value - 1>(
[](auto counter) { // function parameter
using Type = mp_at_c<Types, counter>;
if (typeid(Type) == typeid(int))
return false;
return true;
}
);
}
When compiling with g++ 7.4.0, the following error is encountered (formatted to my taste):
$ g++ -std=c++17 -I/path/to/boost
wgz.cpp:
In substitution of ‘
template<
class L,
long unsigned int I
>
using mp_at_c =
typename boost::mp11::detail::mp_if_c_impl<
(I < typename boost::mp11::detail::mp_size_impl<L>::type:: value),
boost::mp11::detail::mp_at_c_impl<L, I>,
void
>::type::type
[
with L = boost::mp11::mp_list<bool, int, double>;
long unsigned int I = counter
]
’:
wgz.cpp:42:49:
required from ‘
main()::<lambda(auto:1)>
[with auto:1 = long unsigned int]
’
wgz.cpp:14:21:
required from ‘
constexpr WhileGreaterZero<T_counter>::WhileGreaterZero(T_Function&&)
[
with T_Function = main()::<lambda(auto:1)>;
long unsigned int T_counter = 2
]
’
wgz.cpp:49:5:
required from here
wgz.cpp:42:49:
error: ‘counter’ is not a constant expression
using Type = mp_at_c<Types, counter>;
^
wgz:42:49:
note: in template argument for type ‘long unsigned int’
Why is counter
not considered as a constant expression in my code?
What's the crucial difference between mp11's code and mine in this regard?
Change
function(T_counter)
to
function(std::integral_constant< std::size_t, T_counter >{})
within function
the argument is not a compile time value. But an integral_constant
that isn't a compile time value can be cast to an integer, and that integer is a compile time constant, because it doesn't depend on this
.
A related trick is:
template<std::size_t...Is>
constexpr auto indexes( std::index_sequence<Is...> ={} ) {
return std::make_tuple( std::integral_constant<std::size_t, Is>{}... );
}
then you can do:
template<std::size_t N, class F>
void While( F&& f ) {
std::apply( [&](auto...Is) {
(f( Is ) && ...);
}, indexes( std::make_index_sequence<N>{} ) );
}
Live example, no recursion.