In C++, Is It Safe/Portable to Use Static Member Function Pointer for C API Callbacks

In C++, is it safe/portable to use static member function pointer for C API callbacks?

It is not safe per the C++ standard. As stated in this SO posting:

A C callback function implemented in C++ must be extern "C". It may seem to work as a static function in a class because class-static functions often use the same calling convention as a C function. However, doing that is a bug waiting to happen (see comments below), so please don't - go through an extern "C" wrapper instead.

And according to comments made by Martin York in that answer there are real-world problems trying to do so on some platforms.

Make your C ABI callbacks extern "C".


Edit: Adding some supporting quotes from the standard (emphasis mine):

3.5 "Program and linkage":

After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified by all declarations referring to a given object or function shall be identical, except that declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4). A violation of this rule on type identity does not require a diagnostic. [3.5/10]

[Note: linkage to non-C++ declarations can be achieved using a linkage-specification (7.5). ] [3.5/11]

And

7.5 "Linkage specifications":

... Two function types with different language linkages are distinct types even if they are otherwise identical. [7.5/1]

So if the code making the callback is using C language bindings for the callback, then the callback target (in the C++ program) must as well.

C++ idiom when using a C library which expects a function pointer?

On most platforms your code is ever likely to encounter, your solution is fine. However, strictly speaking, there can be a platform where the C calling convention and C++ calling convention differ. On this platform, your code would not work.

The 100% correct solution (as opposed to the 99.9% correct one which you use) is to define a function with C language linkage as the callback. If you need this function to have member access to your class, it can then call a static member function in your class:

class MyClass
{
static int secret;
public:
static void callback() { secret = 42; }
};

extern "C" void callback() { MyClass::callback(); }

Note that it is considered good practice for authors of callback registration points (that is, authors of the library which will call you back) to provide a way for the user to associated void* data with the callback, which will be passed in as they call you. If your library allows that, you can use this void *user_data to pass in a pointer to your C++ class. With such a nicely-designed callback library, it could look like this:

class MyClass
{
int secret;

public:
void setSecret() { secret = 42; }
};

extern "C" void callback(void *userData) { static_cast<MyClass*>(userData)->setSecret(); }

MyClass mc;

int main()
{
cLibrary_registerCallback(&callback, &mc);
}

C API function callbacks into C++ member function code

You cannot directly pass a member function. A member function has the implicit parameter this and C functions don't.

You'll need to create a trampoline (not sure the signature of the callback, so just doing something random here).

extern "C" int fmod_callback( ... args ...)
{
return object->member();
}

One issue is where does that object pointer come from. Hopefully, fmod gives you a generic context value that will be provided to you when your callback is made (you can then pass in the object pointer).

If not, you'll just need to make it a global to access it.

how to pass a non static-member function as a callback?

You could keep it static, but use the userdata to store the this pointer in addition to whatever other userdata you want (by packing them into a structure, for example) and then call an object-specific callback from the static version by calling this->someCallback (where this is the pointer stored in the userdata, of course).

Reference to non-static member function must be called when processing callbacks inside a class

An unbroken C-style callback will accept a void* argument to pass arbitrary user-defined data. Since this one doesn't, its a bug in the library design. Raise an issue with the library authors.

To work around this issue you need to create a closure as a C-compatible function. There's no way to do that is standard C or C++. There are libraries (non-standard-conforming and not portable by necessity) that solve this problem though. One such library is GNU libffcall, another one is libffi. Both allow you to create closures that behave like normal C functions.

extern C for member static callback function

The external callback is by design not linked to a specific class.

Making it a static class member is perhaps nice according to the internals of your code, but it misrepresents the reality.

I'd therefore advise to make it an independent extern "C" function. This avoids misunderstanding and highlights assumptions (for example that self is assumed to be a C but could in reality be something else). If f() is public, all this will be very clean. If it would be private, you'd need to make your callback a friend and this tight coupling would be again highlighted.

The wrapper alternative would just add a redundant middleman to come to the same result.

What kinds of C++ functions can be placed in a C function pointer?


I have a C library that uses a struct of function pointers for callbacks. The callbacks will be called from C code.

A C library only understands C. So you can only pass back things that are explicitly supported and understood by C.

Since there is no definition of calling conventions for any function types in C++ you can not explicitly pass back C++ functions. You can only pass back C function (those explicitly declared with extern "C") and guarantee that they are compatible.

Like all undefined behavior this may seem to work (like passing back normal C++ functions or static members). But like all undefined behavior it's allowed to work. You just can't guarantee that it's actually correct or portable.

extern "C" {
typedef struct callbacks_t {
void (*foo) (const char*);
int (*bar) (int);
} callbacks_t;

// Any functions you define in here.
// You can set as value to foo and bar.

}// extern C

What kinds of C++ functions can I safely place in those function pointers to be called from the C library?

Static member functions?

No. But this happens to work on a lot of platforms. This is a common bug and will bite people on some platforms.

Fully specified template functions?

No.

Non-capturing Lambdas?

No.

g++ seemingly lets me use all of the above,

Yes. But it is assuming that you are passing to objects that are built with the C++ compiler (which would be legal). As C++ code will use the correct calling convention. The problem is when you pass these things to a C library (pthreads springs to mind).

Accessing non static members inside static method via passed pointer


args->myPublicMethod(); //Is this legal and valid?

No. That is neither legal nor valid. However, you can use:

reinterpret_cast<MyClass*>(args)->myPublicMethod();

You can access a private member function of a class from a static member function. So, you can access a private member of the class using:

reinterpret_cast<MyClass*>(args)->myPrivateMember;

Another SO question and its answers discuss the pros and cons of using static_cast and reinterpret_cast. Since you are using void* as the intermediate type, you can use either of them.



Related Topics



Leave a reply



Submit