-->

Deduce argument type in template parametrized by f

2019-09-17 05:26发布

问题:

I have C framework that provides classic deferred callback execution API:

typedef void (*callback_t)(int value_1, int value_2, void* arg);

void schedule(callback_t callback, void* arg);

Here arg is arbitrary value specified by user that will be passed to the callback and other callback parameters are passed by framework. But really only last argument is used in handlers and it has to be explicitly cast from void*.

Thinking of how callback signature may be changed to match the real one I got to this solution:

template <typename TArg, void (*Callback)(TArg*)>
void run(TArg* arg)
{
    schedule([](int, int, void* arg)
    {
        Callback((TArg*)arg);
    }, arg);
}

...
void handler(int* arg)  // instead of handler(int, int, void* arg)
{
}

...
run<int, handler>(arg);     // instead of schedule(handler, arg)

The only problem here is that callback argument type has to be explicitly specified at run invocation.

Is there a way to make the compiler to deduce this type? Ideally the execution code should be just:

run<handler>(arg);

There is another solution to initial problem - to create custom function object and pass it as user argument to native API. Then unpack it from void*, execute real callback and delete function object. But there are issues with action cancelling when callback is not executed. It will require much more work to be implemented correctly and I prefer to ignore it if there is a more simple way.

回答1:

Every once in a while the Preprocessor actually can come in handy:

#define myRun(fun, arg) (::run<::std::remove_pointer_t<decltype(arg)>, fun>(arg))

Note that this will not evaluate arg or fun multiple times. The result is also a parenthesized expression (and not something more icky like a declaration) and thus behaves just like an ordinary function call.



回答2:

Different approach using std::bind (couldn't test though):

typedef void (*callback_t)(int value_1, int value_2, void *arg);

void schedule(callback_t callback, void *arg)
{
}

template <typename TArg>
void run(void (*Callback)(TArg *), TArg *arg)
{
    // needs to be freed
    std::function<void()> *bound = new std::function<void()>(std::bind(Callback, arg));

    schedule([](int, int, void *arg)
    {
        std::function<void()> &f = *(std::function<void()> *)arg;
        f();
    }, bound);
}

void foo(int *a)
{
}

int main()
{
    int *arg = nullptr;
    run(foo, arg);
}