I have a std::tuple
with a bunch of functors that implements callbacks with different arguments. I want to iterate over the tuple at compilation time and execute the functors compatibles with the arguments:
Let's say I have a set of functors:
struct functor_a { void operator()(const class_x&) {} };
struct functor_b { void operator()(const class_x&) {} void operator()(const class_y&) {} };
struct functor_c { void operator()(const class_x&) {} void operator()(const class_z&) {} };
Given a tuple containing them:
using tuple_type = std::tuple<functor_a, functor_b, functor_c>;
I want to do something like:
struct executor {
template<typename message_type, typename... Type>
static constexpr void exec(std::tuple<Types...>& tup, const message_type& msg) {
exec(std::index_sequence_for<Types...>(), tup. msg);
}
template<typename message_type, std::size_t... Is>
static void exec(std::index_sequence<Is...>, tuple_type& tup, const message_type& msg) {
if constexpr (std::is_nothrow_invocable_v<typename std::tuple_element<Is, tuple_type>::type..., message_type>) {
std::invoke(std::get<Is>(tup)..., msg);
}
}
}
};
In order to do this (or alternatively use std::invoke):
auto tup = get_tuple();
executor::exec(tup, class_x()); // Call functor a, b, c
executor::exec(tup, class_y()); // Call functor b
executor::exec(tup, class_z()); // Call functor c
I am having some problems with the constexpr condition that is always evaluated to true, and the code does not compile because it does not find the operator with the given argument:
std::is_nothrow_invocable_v<typename std::tuple_element<Is, tuple_type>::type..., message_type>
You can use a combination of std::apply
, some generic lambdas and fold expressions to make a single function
template <typename Tuple, typename... Args>
constexpr void dispatch(Tuple&& tup, Args const&... args)
{
constexpr auto dispatch_helper = [] (auto&& func, auto&&...args)
{
if constexpr (std::is_invocable_v<decltype(func), decltype(args)...>)
std::invoke(std::forward<decltype(func)>(func), std::forward<decltype(args)>(args)...);
};
std::apply([&](auto&&... funcs)
{
(dispatch_helper(std::forward<decltype(funcs)>(funcs), args...), ...);
}, std::forward<Tuple>(tup));
}
Here is a full example.