Oak Bytes
Oak Bytes

Reputation: 4795

How to use a C++ member function as the callback function for a C framework

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?

  1. Do I need to declare the callback function under "extern C" block?

  2. 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?

  3. Does it matter if the function is a template function or not?

  4. 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

Answers (6)

Oak Bytes
Oak Bytes

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

Ferruccio
Ferruccio

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

n. m. could be an AI
n. m. could be an AI

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

Praetorian
Praetorian

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

Jonathan Wood
Jonathan Wood

Reputation: 67195

This should work if your member function is static.

Upvotes: 7

user541686
user541686

Reputation: 210352

  1. Make sure it's in global scope

  2. Use extern "C"

  3. Use __cdecl, if needed: void (_cdecl *callback)

Upvotes: 2

Related Questions