Reputation: 4795
There is a C library (which I cannot change) that supports a callback function of the type
void (*callback)(void *appContext, int eventid)
I want to set a C++ function as the callback.
Specifically I have following questions?
Do I need to declare the callback function under "extern C"
block?
Does a member function need to be static to be the callback function? Is it possible to use a non-static member function? If yes, how? And when is it recommended to use a non-static member function?
Does it matter if the function is a template function or not?
Does a non-class C style function have any advantages over a class member function?
I am trying these variants on a old VC++ compiler, which does not support the latest C++ standard. But the code needs to be platform independent and should work on most C++ compilers. I want to know what is recommended practice with callbacks?
Upvotes: 16
Views: 9020
Reputation: 4795
Does callback function need to be declared under extern "C"?
NO. extern "C" is necessary only when you are calling a C++ function directly, without the use of function pointers, from C. If function pointers are used, extern "C" is not required.
Can I use non-static member functions as a callback?
NO. Non-static member functions of class A have an implicit first parameter corresponding to this pointer.
Can I use static member functions as a callback?
YES, as long as signature matches with that of the callback.
Does it matter if the function is a template function or not?
NO, template function can be used as callbacks as long as the signature of the instantiated template matches with the callback.
Upvotes: 16
Reputation: 100638
Assuming appContext
is an opaque pointer that you pass to the function making the callback, you can get a callback to a member function of a specific object like this:
class myclass {
void do_something() {
// call function making the callback using _event_handler
// as the callback function and the "this" pointer as appContext
}
// make sure the raw callback uses the correct calling convention (cdecl, stdcall, etc.)
static void _handle_event(void* appContext, int eventid) {
// forward the event to the actual object
static_cast<myclass *>(appContext)->handle_event(eventid);
}
void handle_event(int eventid) {
// do object-specific event handling
}
};
Several answers mention extern "C"
as a requirement. This is simply incorrect.
extern "C"
is necessary only when you are calling a C function directly from C++. It's used to tell the C++ compiler "do not apply name-mangling when generating the symbol name for this function". You are passing a C++ function pointer to a C function. As long as the calling conventions match, it will work just fine. The function's name is never involved.
Upvotes: 8
Reputation: 119847
Strictly speaking, you cannot. A C callback must be designated extern "C"
, which is not possible for member functions. Only freestanding functions can be used. You may forward calls from there to any C++ function, including static or non-static member functions.
Depending on the platform, you sometimes may be able to get away with skipping extern "C"
, but I wouldn't test my luck.
Upvotes: 1
Reputation: 109109
It is not as simple as declaring the callback function under an extern "C"
block. You need to figure out what calling convention the C library uses for its functions.
The terms I'm going to use are Microsoft specific, but the same rules should apply to other platforms too.
By default, the Visual C++ compiler makes C function __stdcall
and C++ functions __cdecl
. You cannot mix and match these calling conventions since each of these makes different assumptions about who cleans the stack. Here's a more detailed explanation.
Once you've matched the calling conventions, the easiest approach is to declare your C++ callback function as a namespace scope free standing function; or if it needs to be a member function, then a static member function. In both cases you should be able to bind a pointer to the instance of the class using std::bind
. You might even be able to use std::bind
to bind a non-static member function but I can't recall the syntax off the top of my head.
Upvotes: 3
Reputation: 210352
Make sure it's in global scope
Use extern "C"
Use __cdecl
, if needed: void (_cdecl *callback)
Upvotes: 2