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
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.
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.
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.
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).
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.
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;
}
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