Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible ambiguity with extern "C", overloading, and function pointers

Tags:

c++

standards

With normal functions, one can write

extern "C" int Frotz(int);  // in a header

int Frotz(int x) { return x; }

With function pointers, however, this appears to have been implemented inconsistently between compilers.

extern "C" int Klutz(int (*)(int), int);

int Klutz(int (*fptr)(int), int x) { return (*fptr)(x); }

In the declaration, the argument is also extern "C". In the definition, most compilers appear to match these functions and make Klutz an extern "C" function. The Sun and Cray compilers, however, interpret these functions as being different, producing an overloaded int Klutz(int (*fptr)(int), int x), which later generates a link-time error.

Although Section 7.5.5 of C++98 and C++11 guarantees the interpretation of Frotz, I cannot tell if the standard is ambiguous about whether extern "C" matching should occur before or after checking for overloading.

Should Klutz above generate a mangled (C++) symbol or an extern "C" symbol?

EDIT 1

I could use a typedef to disambiguate the function pointer to have C or C++ ABI, but I'm interested in whether the code here (a) defines Klutz to have C++ linkage, (b) defines it to have C linkage, or (c) is ambiguous according to the standard, so that compilers are free to choose how to interpret it.

EDIT 2

This appears to be a known issue, at least by those compilers with searchable bug trackers. In my tests, GCC, Clang, Intel, MSVC, IBM XL, PathScale, PGI, and Open64 all fail to distinguish function types that are identical except for language linkage, as explicitly required by the standard (see section 7.5.1, quoted in the accepted answer). Fixing this would break a lot of existing code and require an ABI change. I'm not aware of any compiler that actually uses a different calling convention for C versus C++ language linkage.

  • GCC bug: "Finding reasons to ask for the removal of this feature from the next standard is kind of relevant ;-)" ... "And we may even decide on an official WONTFIX."

  • Clang bug: "I'm terrified of actually enforcing this rule, because doing it properly means making language linkage part of the canonical type, which is going to break a ton of code."

like image 449
Jed Avatar asked Mar 20 '13 23:03

Jed


1 Answers

The C ABI and the C++ ABI are not guaranteed to be the same. So, an extern "C" function pointer is a different type from a C++ function pointer. You need something like this:

extern "C" {
    typedef int (*KlutzFuncType)(int);
    int Klutz (KlutzFuncType, int);
}

int Klutz (KlutzFuncType fptr, int x) { return (*fptr)(x); }

There is some discussion of this issue here.


I only have a copy of the draft. From 7.5p1:

Two function types with different language linkages are distinct types even if they are otherwise identical.

My reading of this is that the first parameter of your first Klutz has a different type than the first parameter of your second Klutz, and so your second Klutz should have C++ linkage.


There are C++ implementations that do not take language linkage into account for function types, despite what the standard says. In the following code snippet, KlutzCxxFuncType refers to a function with C++ linkage, while KlutzCFuncType refers to a function with C linkage.

typedef int (*KlutzCxxFuncType)(int);

extern "C" {
    typedef int (*KlutzCFuncType)(int);
    int Klutz (KlutzCFuncType, int);
}

int Klutz (KlutzCxxFuncType fptr, int x) { return (*fptr)(x); }
int Klutz (KlutzCFuncType fptr, int x) { return (*fptr)(x); }

A compiler that does not distinguish function types based on language linkage will generate a redefinition error on this code. For example, g++ 4.7.2 will emit:

prog.cpp: In function ‘int Klutz(KlutzCFuncType, int)’:
prog.cpp:9:5: error: redefinition of ‘int Klutz(KlutzCFuncType, int)’
prog.cpp:8:5: error: ‘int Klutz(KlutzCxxFuncType, int)’ previously defined here
like image 131
jxh Avatar answered Oct 14 '22 19:10

jxh