Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is `extern "C"` a part of the type of a function?

I don't see any comment in the standard except linkage related things.

Though the standard doesn't say anything about calling convention, the calling conventions might be different between C and C++ in the real world, so I expected that the types of a C function and a C++ function are different. But it seems not, especially in GCC.

#include <type_traits>

extern "C" {
  int c_func(int);
}

int cpp_func(int);

static_assert(!std::is_same<decltype(c_func), decltype(cpp_func)>::value,
              "It should not be the same type");

static_assert fails since GCC considers those functions have the same type.

  • Is extern "C" a part of the type of a function?
  • How do I check if a function uses C calling convention or C++ calling convention?
like image 263
Inbae Jeong Avatar asked Oct 30 '14 07:10

Inbae Jeong


1 Answers

The standard makes it clear that language linkage is indeed a property of a function type itself:

All function types, function names with external linkage, and variable names with external linkage have a language linkage.

In case that wasn't clear enough, there's a note (emphasis mine) that makes the intended meaning unambiguous:

[ Note: Because the language linkage is part of a function type, when indirecting through a pointer to C function, the function to which the resulting lvalue refers is considered a C function. — end note ]

Furthermore,

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

So the answer to your first question is:

  • Yes, extern "C" is part of the type of a function.

However, most compilers fail to distinguish between the types of functions with C and C++ language linkage. This is for example a long-standing bug in GCC (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316; see list of duplicates). I didn't read the entire thread carefully, but it appears that a lot of existing code would break if GCC started enforcing the rule that they really are different types. This is presumably also why other compilers also fail to conform to the standard.

Given that, the answer to your second question would seem to be:

  • There is probably no portable way to perform this check at compile time. Of course after translation you can always go in and look at the object file and see whether the name is mangled or not.

But in theory, your static assertion is supposed to work the way you think it should. That just isn't the case in practice.

Addendum:

If my understanding of the standard is correct, then for example the following function template

template <typename R, typename... A>
void f(R(*)(A...));

cannot be instantiated to produce a function that would accept a pointer to a function with C language linkage as an argument, since the type R(*)(A...) is "pointer to function with C++ language linkage taking arguments of types A... and returning R".

If compilers really worked like this, it's easy to see how you could generically determine whether a function has C or C++ language linkage.

But this example should also make it clear how badly existing code would break if compilers really worked this way.

like image 182
Brian Bi Avatar answered Oct 05 '22 06:10

Brian Bi