C++ Class Member Function Callback

Using a C++ class member function as a C callback function

You can do that if the member function is static.

Non-static member functions of class A have an implicit first parameter of type class A* which corresponds to this pointer. That's why you could only register them if the signature of the callback also had the first parameter of class A* type.

How can I pass a class member function as a callback?

That doesn't work because a member function pointer cannot be handled like a normal function pointer, because it expects a "this" object argument.

Instead you can pass a static member function as follows, which are like normal non-member functions in this regard:

m_cRedundencyManager->Init(&CLoggersInfra::Callback, this);

The function can be defined as follows

static void Callback(int other_arg, void * this_pointer) {
CLoggersInfra * self = static_cast<CLoggersInfra*>(this_pointer);
self->RedundencyManagerCallBack(other_arg);
}

C++ callback using class member

Instead of having static methods and passing around a pointer to the class instance, you could use functionality in the new C++11 standard: std::function and std::bind:

#include <functional>
class EventHandler
{
public:
void addHandler(std::function<void(int)> callback)
{
cout << "Handler added..." << endl;
// Let's pretend an event just occured
callback(1);
}
};

The addHandler method now accepts a std::function argument, and this "function object" have no return value and takes an integer as argument.

To bind it to a specific function, you use std::bind:

class MyClass
{
public:
MyClass();

// Note: No longer marked `static`, and only takes the actual argument
void Callback(int x);
private:
int private_x;
};

MyClass::MyClass()
{
using namespace std::placeholders; // for `_1`

private_x = 5;
handler->addHandler(std::bind(&MyClass::Callback, this, _1));
}

void MyClass::Callback(int x)
{
// No longer needs an explicit `instance` argument,
// as `this` is set up properly
cout << x + private_x << endl;
}

You need to use std::bind when adding the handler, as you explicitly needs to specify the otherwise implicit this pointer as an argument. If you have a free-standing function, you don't have to use std::bind:

void freeStandingCallback(int x)
{
// ...
}

int main()
{
// ...
handler->addHandler(freeStandingCallback);
}

Having the event handler use std::function objects, also makes it possible to use the new C++11 lambda functions:

handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });

Map C++ member function to C callback

Use the user_data pointer to give you back a pointer to the class that owns the callback. For example.

void MyCallback(char port, uint8_t interrupt_mask, uint8_t value_mask, void *user_data)
{
static_cast<Example *>(user_data)->Callback(port, interrupt_mask, value_mask);
}

class Example
{
public:

Example()
{
//setup callback here - i dont know what library you're using but you should be able to pass a this pointer as user_data
init_lib(fictionalparameter1, fictionalparameter2, this);
}

private:

void Callback(char port, uint8_t interrupt_mask, uint8_t value_mask)
{
//callback handler here...
}

friend void MyCallback(char port, uint8_t interrupt_mask, uint8_t value_mask, void *user_data);
};

C++ Member function as a callback

There are two problems:

  • std::mem_fn(&app::callback) takes an instance of app as the first parameter.
  • The most idiomatic alternative would be to use a capturing lambda such as
    [this] (Json::Value& json) { return callback(json); }
    but these cannot be converted to function pointers, which makes implementing generic containers for storing and dynamically dispatching to them extremely difficult to do.

One possible approach is to amend your websocket interface and accept an optional void* context parameter like this:

using cb = int(*)(Json::Value &json, void* ctx);

static void websocket::listen(cb callback, const char* address, void* ctx = nullptr)
{
...
}

int app::callback(Json::Value& json)
{
return db->insert(json);
}

void app::start()
{
websocket::listen([] (Json::Value &json, void* ctx) -> int {
return static_cast<app*>(ctx)->callback(json);
}, path, this);
}

If you prefer more type safety for this erasure, you can consider std::any:

using cb = int(*)(Json::Value &json, std::any ctx);

static void websocket::listen(cb callback, const char* address, std::any ctx = {})
{
...
}

int app::callback(Json::Value& json)
{
return db->insert(json);
}

void app::start()
{
websocket::listen([] (Json::Value &json, const std::any& ctx) -> int {
return std::any_cast<app*>(ctx)->callback(json);
}, path, this);
}

The std::any_cast will throw a std::bad_any_cast if ctx was empty or storing a pointer that wasn't a type erased app*.

How to make a callback in C++: use a class member function as a parameter

Here is a working way to do that:

void myOtherFunction(std::function<void()> oneOfMyFunctions) {
oneOfMyFunctions();
}

And inside my class:

myOtherFunction([&] {
oneOfMyFunctions();
});

Some explanations:
In std::function<void()>, void is what is returned by the function and () contains the types of its parameters (mine is empty because it doesn't have any).

In the 2nd code I am using a lambda to keep the context, as a bind would do (but lambdas replace them).

C++ member function as callback function to external library

The callback you have shown does not allow a user-defined value to be passed to it (otherwise you could use that for passing around your object pointer). It expects a standalone non-class function, so you have two options:

1) if the callback only ever calls into a single object at one time, then you can store the object pointer in a global or static variable, and then use a standalone function (or static class method) as the callback and have it use the global/static pointer to call your class method:

class MyClass
{
//somestuff
ExternalClass m_ExObj;

void Callback(int x)
{
//dosomething
}

static MyClass* objForCallback;
static void exObjCallback(int x) { objForCallback->Callback(x); }

void MyFunc()
{
objForCallback = this;
m_ExObj.ExternalFunc(&exObjCallback);
}
};

2) if you need to have callbacks for multiple objects at a time, you will have to wrap your class method inside a per-object thunk, where each thunk knows which object to call into, and then you use the thunks as callbacks. This is a more advanced technique, requiring an understanding of x86/x64 assembly and calling conventions, as you have to allocate memory dynamically and populate it with assembly instructions for each thunk to execute at runtime. For example, at least on Windows 32bit:

#pragma pack(push, 1)
struct MyThunk
{
unsigned char PopEAX_1; // POP the caller's return address off the stack
unsigned char PushThis; // PUSH the object 'this' pointer on to the stack
void *ThisValue;
unsigned char PushEAX_1; // PUSH the caller's return address back on to the stack
unsigned char Call; // CALL the callback function
__int32 CallAddr;
unsigned char PopEAX_2; // POP the caller's return address off the stack
unsigned char AddESP[3]; // Remove the object 'this' pointer from the stack
unsigned char PushEAX_2; // PUSH the caller's return address back on to the stack
unsigned char Return; // return to the caller
};
#pragma pack(pop)

typedef void (*CallbackType)(int);

class MyClass
{
CallbackType exObjCallback;

MyClass()
{
MyThunk *thunk = (MyThunk*) VirtualAlloc(NULL, sizeof(MyThunk), MEM_COMMIT, PAGE_READWRITE);
if (thunk)
{
thunk->PopEAX_1 = 0x58;
thunk->PushThis = 0x68;
thunk->ThisValue = this;
thunk->PushEAX_1 = 0x50;
thunk->Call = 0xE8;
thunk->CallAddr = reinterpret_cast<__int32>(Callback) - (reinterpret_cast<__int32>(&thunk->Call) + 5);
thunk->PopEAX_2 = 0x58;
thunk->AddESP[0] = 0x83;
thunk->AddESP[1] = 0xC4;
thunk->AddESP[2] = 0x04;
thunk->PushEAX_2 = 0x50;
thunk->Return = 0xC3;

DWORD dwOldProtect;
VirtualProtect(thunk, sizeof(MyThunk), PAGE_EXECUTE, &dwOldProtect);

FlushInstructionCache(GetCurrentProcess(), thunk, sizeof(MyThunk));

exObjCallback = (CallbackType) thunk;
}
}

~MyClass()
{
if (exObjCallback)
VirtualFree(exObjCallback, 0, MEM_RELEASE);
}

//somestuff
ExternalClass m_ExObj;

// NOTE: pCtx is the return address inside of ExternalFunc()
// where the callback is being called from. Because the
// callback is using the __cdecl calling convention, the
// thunk needs to remember this value and restore it after
// Callback() exits. Passing it as a parameter to Callback()
// is a quick-n-dirty way for the thunk to do that...
static void __cdecl Callback(void *pCtx, MyClass *pThis, int x)
{
//dosomething with pThis
}

void MyFunc()
{
if (exObjCallback)
m_ExObj.ExternalFunc(exObjCallback, ...);
}
};

When ExternalFunc() calls its callback, it will be calling the thunk, executing the instructions it contains. The thunk above is injecting the object's this pointer into the call stack as a parameter for Callback() as if ExternalFunc() had called it directly.

Update: in lieu of new information about the callback actually accepting a user-defined value, that greatly simplifies things:

class MyClass
{
//somestuff
ExternalClass m_ExObj;

static void Callback(int x, const void *p) {
MyClass *pThis = (MyClass*) p;
//dosomething with pThis
}

void MyFunc() {
m_ExObj.ExternalFunc(&Callback, this);
}
};


Related Topics



Leave a reply



Submit