In C++, is it safe/portable to use static member function pointer for C API callbacks? Is the ABI of a static member function the same as a C function?
views:
470answers:
4For all the Windows C++ compilers that I'm aware of, the answer is yes, but nothing in the language standard guarantees this. I wouldn't let that stop you however, it's a very common way of implementing callbacks using C++ - you may find you need to declare the static functions as WINAPI however. This is taken from an old threading library of my own:
class Thread {
...
static DWORD WINAPI ThreadFunction( void * args );
};
where this is the callback use by te Windows threading API.
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.
After searching and several breaks while attacking other problems, I found an answer which is clear and succinct (for standardese, anyway):
Calling a function through an expression whose function type has a language linkage that is different from the language linkage of the function type of the called function's definition is undefined. [5.2.2/1]
I still maintain that it is problematic at a fundamental level to use text from the C++ standard to define the behavior of a C library compiled with a C compiler, and exactly how that interlanguage interoperability works is very implementation-specific; however, this is the closest I think either standard can (currently) hope to define such interaction.
In particular, this is undefined behavior (and isn't using a C library so that issue doesn't arise):
void call(void (*pf)()) { pf(); } // pf() is the UB
extern "C" void f();
int main() { call(f); }
// though I'm unsure if a diagnostic is required for call(f)
Comeau does give a diagnostic at call(f)
(though it can do that even if the diagnostic isn't required).
This isn't undefined behavior, and shows how to include language linkage in a function pointer type (which is through a typedef):
extern "C" typedef void F();
void call(F* pf) { pf(); }
extern "C" void f();
int main() { call(f); }
Or could be written:
extern "C" {
typedef void F();
void f();
}
void call(F* pf) { pf(); }
int main() { call(f); }