The example snippet:
#include <iostream>
#include <functional>
#include <vector>
// Simulated callback type
using CallbackType = void(*)(void*, uint32_t);
class DataProcessor{
public:
void process(void* data) {
//process data
std::cout << "process data" << std::endl;
}
};
class ExampleClass {
public:
ExampleClass() {
// Initialize the callback with a lambda that wraps the member function
callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };
}
void executeCallback(void* data, uint32_t id) {
if (callback) {
callback(data, id);
}
}
private:
void memberFunction(void* data, uint32_t id) {
std::cout << "Member function called with id: " << id << std::endl;
// Process data...
processor.process(data);
}
CallbackType callback;
DataProcessor processor;
};
int main() {
ExampleClass example;
int dummyData = 42;
example.executeCallback(&dummyData, 1);
return 0;
}
I need to assign a member function to a function pointer void(*)(void*, uint32_t);
as shown in the above code. The memberFunction
need to be non-static, as it also needs to access a non-static member field processor
. But the lambda
I assigned to field callback
gives error, any way to work around this issue?
compiler error
error: cannot convert ‘ExampleClass::ExampleClass()::<lambda(void*, uint32_t)>’ to ‘CallbackType’ {aka ‘void ()(void, unsigned int)’} in assignment 19 | callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };
Be noted, using CallbackType = void(*)(void*, uint32_t);
is external code I cant change it.
The example snippet:
#include <iostream>
#include <functional>
#include <vector>
// Simulated callback type
using CallbackType = void(*)(void*, uint32_t);
class DataProcessor{
public:
void process(void* data) {
//process data
std::cout << "process data" << std::endl;
}
};
class ExampleClass {
public:
ExampleClass() {
// Initialize the callback with a lambda that wraps the member function
callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };
}
void executeCallback(void* data, uint32_t id) {
if (callback) {
callback(data, id);
}
}
private:
void memberFunction(void* data, uint32_t id) {
std::cout << "Member function called with id: " << id << std::endl;
// Process data...
processor.process(data);
}
CallbackType callback;
DataProcessor processor;
};
int main() {
ExampleClass example;
int dummyData = 42;
example.executeCallback(&dummyData, 1);
return 0;
}
I need to assign a member function to a function pointer void(*)(void*, uint32_t);
as shown in the above code. The memberFunction
need to be non-static, as it also needs to access a non-static member field processor
. But the lambda
I assigned to field callback
gives error, any way to work around this issue?
compiler error
error: cannot convert ‘ExampleClass::ExampleClass()::<lambda(void*, uint32_t)>’ to ‘CallbackType’ {aka ‘void ()(void, unsigned int)’} in assignment 19 | callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };
Be noted, using CallbackType = void(*)(void*, uint32_t);
is external code I cant change it.
3 Answers
Reset to default 3Only a lambda without any capture can be converted and assigned to a function pointer.
There is no way to invoke a non-static method via a plain function pointer as is. You need your this
context somehow.
There are 2 possible ways to overcome this:
- Use the
void*
parameter to the callback to allow to pass thethis
context. Then in your callback convert it toExampleClass*
and call the method. If you need another data pointer you can: (1) store it in anExampleClass
member or (2) instead of passingthis
as mentioned above pass a pointer to a pair ofthis
and another data pointer. - Store
this
in a global variable - this is quite horrible and should be the last resort.
A better solution (if you could have changed the external code) is to make CallbackType
a type of std::function
instead:
// Simulated callback type
using CallbackType = std::function<void(void*, uint32_t)>;
Live demo
But the lambda I assigned to field callback gives error, any way to work around this issue?
In C++, a lambda expression may be implicitly converted to a function pointer only if it does not capture any variables (i.e., has an empty capture list).
In your case, since you can not modify the function pointer API:
using CallbackType = void(*)(void*, uint32_t);
You just might need to find a way to save the this
pointer context, so that the non-static member function pointer can be wrapped and assigned to CallbackType callback
.
For example:
class ExampleClass
{
// Structure to hold both instance pointer and data
struct Context
{
ExampleClass* instance{ nullptr };
void* userData{nullptr};
};
public:
ExampleClass()
{
// Initialize the context with this pointer
context.instance = this;
// Use static lambda as the callback (no captures)
callback = [](void* contextPtr, uint32_t id)
{
if (Context* ctx = static_cast<Context*>(contextPtr);
ctx && ctx->instance)
ctx->instance->memberFunction(ctx->userData, id);
};
}
void executeCallback(void* data, uint32_t id) { ... }
private:
void memberFunction(void* data, uint32_t id) { ... }
Context context{ this, nullptr };
CallbackType callback;
DataProcessor processor;
};
See live demo
any way to work around this issue?
Yes, but we'll need to understand what the issue is, first.
using CallbackType = void(*)(void*, uint32_t);
This is an address of a single function. The CallbackType
variable contains only the information necessary to call the function, everything else must be provided by the caller. Two calls using the same variable may be different only if either call arguments are different or the call checks some global state.
The
memberFunction
need to be non-static, as it also needs to access a non-static member fieldprocessor
.
This means there may potentially be five different objects carrying five different processor
s and using them as callbacks should lead to different results. We have what seems like a contradiction, one of our assumptions has to be discarded.
We may switch from a function pointer to an
std::function
object. Those can contain more data, "capturing"this
. If you design all interfaces from the ground and not bound to being compatible with some existing library, that's a good choice.We may alter the call behavior by passing a different argument. Your callback signature already includes
void*
a.k.a. "any client-specified information", you may pass an instance ofstruct Onion {void* inner; ExampleClass* thiz;}
and in thememberFunction
(which by itself is a static member) castdata
back toOnion*
and usethiz->processor
.Finally, for the sake of completeness, we may create a global or thread local storage
ExampleClass*
variable and use it as "pointer to the object whoseprocessor
should be used" in (once again, static)memberFunction
. It is usually not a good approach but it's there.
CallbackType
astd::function
instead. – wohlstad Commented Mar 17 at 11:24this
in some global variable which is quite horrible. – wohlstad Commented Mar 17 at 11:30void*
argument in callback - most likely the callback is called with your custom data? The typical solution is then to pass a static member function/free function/captureless lambda thatreinterpret_cast
s thevoid*
back into original type and calls member function/capturing lambda with it. – Yksisarvinen Commented Mar 17 at 11:35static_cast
is enough for that. – Weijun Zhou Commented Mar 17 at 12:07