Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C/C++ build a generic stack frame to call different callback functions

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.

like image 656
Obsidian Avatar asked Dec 30 '22 13:12

Obsidian


1 Answers

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

like image 77
user7860670 Avatar answered Jan 13 '23 11:01

user7860670