I'm refactoring a C++ application that refers to a bunch a multiple callback functions, each having a different number of arguments and I would like to know if there's a generic way to build a dedicated argument stack frame before calling each of them.
In other words: fetching all required arguments first according to function to be called, then perform a call that would be transparent for called function.
In a way, this is the opposite of a variadic function, as this would be a single function that knows it can receive different number of arguments. Here I have a bunch of immutable, regular functions and I'd like to call them from a generic hub.
I place both C and C++ tags here because I'm interested in propositions for both these languages, I have the feeling that what would work for C would also be applyable to C++ and I'm still open to "C++ only" solutions such as variadic templates or such.
The background of all of it is that quoted program is actually parsing a command line, then calling a function according to the name of the command, passed as first argument after program name, then all required ones as plain strings, but depending of invoked command.
I know I could write something like:
if(nb_of_args == 1)
my_callback_ptr(argv[n]);
else if (nb_of_args == 2)
my_callback_ptr(argv[n],argv[n+1]);
else if (nb_of_args == 3)
my_callback_ptr(argv[n],argv[n+1],argv[n+2]);
else if (nb_of_args == 4)
my_callback_ptr(argv[n],argv[n+1],argv[n+2],argv[n+3]);
else if…
…to restrict the compiled calls to the sole number of arguments regardless of the function itself, but I'd still like to do better.
Thanks in advance to everybody.
A viable approach is to implement dispatch table that will contain pointers to wrapper functions automatically passing appropriate amount of arguments to each callback.
#include <array>
#include <utility>
#include <cstddef>
template<auto x_p_callback, ::std::size_t... x_indexes>
void invoke_callback(char const * const * const pp_args, ::std::index_sequence<x_indexes...>)
{
(*x_p_callback)(pp_args[x_indexes]...);
}
template<typename x_Callback >
struct
t_ArgsCount;
template<typename x_Result, typename... x_Args>
struct
t_ArgsCount<x_Result (x_Args...)>
: ::std::integral_constant<::std::size_t, sizeof...(x_Args)>
{};
template<auto x_p_callback>
void callback_sunk(char const * const * const pp_args)
{
using
t_Callback = typename ::std::remove_reference<decltype(*x_p_callback)>::type;
using
t_Sequence = typename ::std::make_index_sequence<t_ArgsCount<t_Callback>::value>;
invoke_callback<x_p_callback>(pp_args, t_Sequence{});
}
using
t_Callback = void (char const * const * const pp_args);
template<auto... x_p_callbacks>
constexpr auto make_table(void)
{
return ::std::array<t_Callback *, sizeof...(x_p_callbacks)>{&callback_sunk<x_p_callbacks>...};
}
void my_callback_0() {}
void my_callback_1(char const *) {}
void my_callback_2(char const *, char const *) {}
void my_callback_3(char const *, char const *, char const *) {}
int main(int argc, char const * const * const pp_args)
{
constexpr auto table{make_table<my_callback_0, my_callback_1, my_callback_2, my_callback_3>()};
table.at(argc)(pp_args);
}
online compiler
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With