Question

It has just come to my attention that the C++ standard says that C and C++ functions have different and incompatible types, even if their type signatures are the same (for more info see this question). That means that you technically are not allowed to pass a C++ function to a C function like pthread_create().

I am curious if there are any platforms in use where the two ABIs are actually different (aside from the obvious name mangling differences). Specifically, does anyone know of any platforms where this C++ program will fail to compile and run?

#include <assert.h>

extern "C" int run(int (*f)(int), int x) { return f(x); }

int times2(int x) { return x * 2; }

int main(int argc, char *argv[]) {
  int a = times2(argc);
  // This is undefined behavior according to C++ because I am passing an
  // "extern C++" function pointer to an "extern C" function.
  int b = run(&times2, argc);
  assert(a == b);
  return a;
}
Was it helpful?

Solution

I don't know of any platforms where the ABI is different, but even if the C and C++ calling conventions are the same, the C++ standard requires the compiler to issue a diagnostic for the program. A pointer-to-function-with-C-language-linkage is a different type to a pointer-to-function-with-C++-language-linkage, so you should be able to overload run() like so:

extern "C" int run(int (*f)(int), int x) { return f(x); }
extern "C++" int run(int (*f)(int), int x) { return f(x); }

Now when you call run(times) it should call the second one, so it follows that the first one is not callable (there is no conversion from pointer-to-function-with-C-language-linkage to a pointer-to-function-with-C++-language-linkage) and so the original code should cause a compiler diagnostic. Most compilers get that wrong, though, e.g. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316

N.B. The Solaris compiler does diagnose the incompatible language linkages, as a warning:

"t.c", line 11: Warning (Anachronism): Formal argument f of type extern "C" int(*)(int) in call to run(extern "C" int(*)(int), int) is being passed int(*)(int).

If you overload run with an extern "C++" function it correctly calls the extern "C++" one for run(times).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top