I would like the ability to disable a function if a callable template argument is not callable via std::apply
.
Perhaps it would help to explain the problem starting with what works. I have this for the regular function call case:
template <class Fn, class... Args>
using enable_if_callable = decltype(std::declval<Fn>()(std::declval<Args>()...));
...
template <class Fn, class = enable_if_callable<Fn, int, float>>
void call_fn(Fn fn) {
fn(1, 2.0f);
}
// The above is correctly disabled in this case:
call_fn([](int) {})
The above works well to disable call_fn
when fn
can't be called with arguments (int, float)
. I only get one error at the site of instantiation, as intended.
I'm trying to find an equivalent for disabling a function if the Args are specified in an std::tuple
, for use with std::apply
:
template <class Fn, class TupleArgs>
using enable_if_applicable =
decltype(std::apply(std::declval<Fn>(), std::declval<TupleArgs>()));
...
template <class Fn, class = enable_if_applicable<Fn, std::tuple<int, float>>>
void apply_fn(Fn fn) {
std::apply(fn, std::make_tuple(1, 2.0f));
}
// The above is not correctly disabled in this case:
apply_fn([](int) {})
This doesn't prevent instantiation, at least not to avoid errors from inside apply, presumably because these errors are not from substitution failures of the declaration. The techniques I'm familiar with for peeling apart tuples don't seem to apply here (index sequence, etc.).
Of course, in this non-general example, just using enable_if_callable<Fn, int, float>
would work, but I'm trying to make this work when the tuple could have any number of elements.
Any ideas on how to implement such a thing?
There is no need to reinvent the wheel. Just use what @asmmo said:
template <class Fn, class ... Args, std::enable_if_t<std::is_invocable_v<Fn, Args...>, int> = 0>
void apply_fn(Fn&& fn, const std::tuple<Args...>& t) {
std::apply(std::forward<Fn>(fn), t);
}
template<class Fn, class Tuple>
using enable_if_applicable = decltype(apply_fn(std::declval<Fn>(), std::declval<Tuple>()));