Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C-style Callbacks in C++11

In a C++11 project I'm using a C-style third-party library (curl in my case) which needs C-style callbacks.

To achieve this I used the "poiner-to-member" operator:

size_t c_callback_wrapper(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    MyClass *p = (MyClass*)userdata;
    return (p->*&MyClass::actualCallback)(ptr, size, nmemb, userdata);
}


void Myclass::performSomething() {
    // register callback function
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, c_callback_wrapper);
    // register userdata
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
    ...
    res = curl_easy_perform(curl);
}

This solution actually works, but it's not satisfying for me.

What I actually want to do is to write a lambda inside my registering function.

The advantage would be locality in general: I would be able to capture some local variables and there would be no need to write a complex "actualCallback" member routine.

I already read, that it seems not to be possible to use Lambdas as C-style Functions when they capture variables.

Are there any ways to achieve this using some tricks? (e.g. playing with the calling convention of lambdas, ...)

Thanks

like image 912
rralf Avatar asked Aug 26 '14 19:08

rralf


People also ask

What is C callback?

A callback in C is a function that is provided to another function to "call back to" at some point when the other function is doing its task. There are two ways that a callback is used: synchronous callback and asynchronous callback.

How are callbacks implemented in C?

In C, callback functions are implemented using function pointers. These function pointers hold the address of the function to be called back, thus allowing the code that receives it as argument to call it back.

What is callback function in C with example?

A callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time [Source : Wiki]. In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called as a Callback function.

Why are callback functions used in C?

Callback functions are an essential and often critical concept that developers need to create drivers or custom libraries. A callback function is a reference to executable code that is passed as an argument to other code that allows a lower-level software layer to call a function defined in a higher-level layer(10).


2 Answers

A lambda that captures context variables cannot be converted to a bare function pointer because that would make it impossible to carry the captured state along. What you've shown in the example is the right way to go about dealing with the problem of invoking C++ member functions via a C callback.

You could replace c_callback_wrapper with a capture less lambda, if you find that any more appealing.

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, 
                 [](char *ptr, size_t size, size_t nmemb, void *userdata) {
                    // invoke the member function via userdata
                    auto p = static_cast<MyClass *>(userdata);
                    return p->actualCallback(ptr, size, nmemb, userdata);
                 });

Note that you should probably get rid of the last parameter to the actualCallback() member function, since that is just the this pointer, which the non-static member function shouldn't need to be passed explicitly.

like image 96
Praetorian Avatar answered Oct 14 '22 05:10

Praetorian


Here is what I would do (please note that I only know enough about C++ to blow off the occasional foot):

#include <iostream>
#include <functional>

void external_c_function(void cb(void *), void *userdata)
{
    cb(userdata);
}

void c_callback_wrapper(void *userdata)
{
    auto &lambda = *static_cast< std::function<void(void)>* >(userdata);
    std::cout << "calling lambda" << std::endl;
    lambda();
}

int main(void)
{
    int foo = 42;
    std::function<void(void)> lambda = [&] { std::cout << foo << std::endl; };
    external_c_function(c_callback_wrapper, &lambda);
    return 0;
}
like image 25
Christoph Avatar answered Oct 14 '22 07:10

Christoph