The problem is the following, in C++14
:
FV&& valid_f
, FI&& invalid_f
, and argu
First, a homebrew version of C++2a's is_detected
:
#include
#include
#include
#include
namespace details {
templateusing void_t=void;
templateclass Z, class=void, class...Ts>
struct can_apply:std::false_type{};
templateclass Z, class...Ts>
struct can_apply>, Ts...>:std::true_type{};
}
templateclass Z, class...Ts>
using can_apply = typename details::can_apply::type;
As it happens, std::result_of_t is the trait we want to test.
template
using can_call = can_apply< std::result_of_t, Sig >;
now can_call< Some(Sig,Goes,Here) > is true_type iff the expression you want can be called.
Now we write some compile-time if dispatch machinery.
template
using index_t=std::integral_constant;
template
constexpr index_t index_v{};
constexpr inline index_t<0> dispatch_index() { return {}; }
template =0
>
constexpr index_t<0> dispatch_index( B0, Bs... ) { return {}; }
template =0
>
constexpr auto dispatch_index( B0, Bs... ) {
return index_v< 1 + dispatch_index( Bs{}...) >;
}
template
auto dispatch( Bs... ) {
using I = decltype(dispatch_index( Bs{}... ));
return [](auto&&...args){
return std::get( std::make_tuple(decltype(args)(args)..., [](auto&&...){}) );
};
}
dispatch( SomeBools... ) returns a lambda. The first of the SomeBools which is compile-time truthy (has a ::value that evaluates to true in a boolean context) determines what the returned lambda does. Call that the dispatch index.
It returns the dispatch_index'd argument to the next call, and an empty lambda if that is one-past-the-end of the list.
template
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
dispatch(
can_call{},
can_call{}
)(
[&](auto&& valid_f, auto&&)->decltype(auto) {
return decltype(valid_f)(valid_f)(std::forward(args)...);
},
[&](auto&&, auto&& invalid_f)->decltype(auto) {
return decltype(invalid_f)(valid_f)(std::forward(args)...);
}
)(
valid_f, invalid_f
);
}
and done, live example.
We could make this generic to enable nary version. First index_over:
template
auto index_over( std::index_sequence ){
return [](auto&&f)->decltype(auto){
return decltype(f)(f)( std::integral_constant{}... );
};
}
template
auto index_over(std::integral_constant ={}){
return index_over(std::make_index_sequence{} );
}
Then auto_dispatch:
template
auto auto_dispatch( Fs&&... fs ) {
auto indexer = index_over();
auto helper = [&](auto I)->decltype(auto){
return std::get( std::forward_as_tuple( decltype(fs)(fs)... ) );
};
return indexer
(
[helper](auto...Is){
auto fs_tuple = std::forward_as_tuple( helper(Is)... );
return [fs_tuple](auto&&...args) {
auto dispatcher = dispatch(can_call{}...);
auto&& f0 = dispatcher(std::get(fs_tuple)...);
std::forward(f0)(decltype(args)(args)...);
};
}
);
}
with test code:
auto a = [](int x){ std::cout << x << "\n"; };
auto b = [](std::string y){ std::cout << y << "\n"; };
struct Foo {};
auto c = [](Foo){ std::cout << "Foo\n"; };
int main() {
auto_dispatch(a, c)( 7 );
auto_dispatch(a, c)( Foo{} );
auto_dispatch(a, b, c)( Foo{} );
auto_dispatch(a, b, c)( "hello world" );
}
Live example