Pass a c++ lambda to old c function pointer
Asked Answered
R

1

6

I have to create a c++ wrapper to a old c library.

In a class method I must call a c function that takes with other stuff also a function pointer(it is a event handler and the function takes a function that is fired when a event happens).

A simple example of it is this:

void myclass::add_handler(std::function<void()> handler, otherstuff...)
{
    /*
     *  Many things.
     */

    type_of_function_pointer_accepted_by_old_c_library_add_handler nameofvariable =
    [handler](same_arguments_as_in_definition_of_the_old_c_function_pointer_accepted)
    {
        /*
         *  Many other things with other stuff to be done before the
         *  handler but always only when fired and not when myclass::add_handler is called.
         */
        handler();
    };

    old_c_library_add_handler(nameofvariable);

    /*
     *  Last things.
     */
}

The compiler complains, as I know, that I can't assign a lambda with capture to an old c function pointer. The question is: how can I do to solve?

R answered 11/9, 2013 at 16:1 Comment(4)
You can't pass any non-static member functions to a c function pointer. You will have to create a static function to be able to pass it to the c-function. From that static function you will have to retrieve the instance you want to call the member function on.Wadsworth
I think I did not understand completely. I should write a static myclass::some_function and copy it to the c function pointer instead of the lambda. Then inside the static function how can I retrieve the capture variables I take in the lambda? Can you please write a bit of example code?R
What does old_c_library_add_handler really look like? Reasonably designed C libraries often allow one to pass a void* pointer or similar, together with the callback. When they call the callback, they pass that pointer right back to it, for context. See if your library is one of those. If not, then I'm afraid you will have to resort to non-portable hacks, like creating machine code thunks on the fly.Hauck
Yes, the old_c_library_add_handler takes a void* argument that is the blob that contains something the handler can take from the caller. I don't know how to transform a std::function<void()> handler to a void* parameter and then, inside the handler, transform it again to a std::function<void()> handler so I can call it.R
C
7

Here is an example. We are using the fact that lambdas that do not capture anything are, according to the C++ standard, usable as function pointers.

/* The definition of the C function */
typedef void (*PointerToFunction)();
void old_c_function(PointerToFunction handler, void* context);


/* An example of a C++ function that calls the above C function */
void Foo(std::function<void()> handler)
{
    auto lambda = [] (void* context) {
        auto handler = reinterpret_cast<std::function<void()>*>(context);
        (*handler)();
    };

    old_c_function(&lambda, &handler); 
}

I believe you can use the same idea in your context.

Cabob answered 12/9, 2013 at 6:24 Comment(2)
This will only work if the callback-function is called before Foo returns. If the C library stores the function pointer and calls it later, it will reference invalid memory. (Because the handler variable went out of scope)Octane
Indeed. If the user wishes something else, he should make a copy of the handler and pass it as the context (and then free it). This is more complicated, of course.Cabob

© 2022 - 2024 — McMap. All rights reserved.