template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> ) {
return [](auto&&...args){
return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
};
}
template<std::size_t N>
auto dispatch() {
return dispatch( std::integral_constant<std::size_t, N>{} );
}
this little beauty gives you much of the magic of if constexpr in c++14.
template <std::size_t N, std::size_t MAX>
void _inner_print() {
dispatch( std::integral_constant<bool, N==0>{} )
(
// 0, aka false branch:
[&](auto Nval){
std::cout << "Call #"<<MAX-Nval<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<Nval-1, MAX>();
},
// 1, aka true branch:
[](auto&&){}
)( std::integral_constant<std::size_t, N>{} );
}
Live example.
Note that we have to pass in the N via an argument to the lambda, because we want the validity of the lambda's body to vary based on its argument, which is only passed to the right one.
Alternatively, you can use an overload helper:
template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
overloaded<Lhs>, overloaded<Rhs,Ts...>
{
using overloaded<Lhs>::operator();
using overloaded<Rhs,Ts...>::operator();
template<class A0, class...As>
explicit overloaded(A0&&a0, As&&...as) :
overloaded<Lhs>(std::forward<A0>(a0)),
overloaded<Rhs,Ts...>( std::forward<As>(as)... )
{}
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
};
template<class T0>
struct overloaded<T0> : T0 {
using T0::operator();
template<class A0>
explicit overloaded(A0&&a0):
T0(std::forward<A0>(a0))
{}
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
};
template<class R, class...Args>
struct overloaded<R(*)(Args...)> {
R operator()(Args... args) const {
return pf(std::forward<Args>(args)...);
}
R(*pf)(Args...);
overloaded( R(*f)(Args...) ):pf(f) {}
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
};
template<class...Args>
auto overload( Args&&...args ) {
return overloaded<std::decay_t<Args>...>(
std::forward<Args>(args)...
);
}
Here is how you can use overloaded:
template <std::size_t N, std::size_t MAX>
void _inner_print()
{
std::integral_constant<std::size_t, N> index;
overload(
[](std::integral_constant<std::size_t, 0>) {},
[&](auto index) {
std::cout << "Call #"<<MAX-index<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<index-1, MAX>();
}
)(index);
}
note that the rule is that the only-sometimes-compiling dynamic code must depend on the auto arguments to the lambda.
Live example.