Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope. In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification. [ Example:
extern "C" // the name f1 and its function type have C language linkage; void f1(void(*pf)(int)); // pf is a pointer to a C function
...
— end example ]
Observe that the pointer &foo
passed to the function c_f()
below is not a pointer to a C function. This code compiles and links normally in VS2017. But it shouldn't, according to [dcl.link]/4.
File main.cpp
:
#include <stdio.h>
extern "C" // the name c_f and its function type have C language linkage;
void c_f(void(*pf)(int)); // pf is a pointer to a C function
void foo(int i) {
printf("%d\n", i);
}
extern "C" void c_foo(int);
int main() {
c_foo(1); // Calls c_foo(int) defined in other.c
c_f(&foo); // Calls c_f(void(*)(int)) defined in other.c, but &foo is not a pointer to a C function !!
}
File other.c
:
#include <stdio.h>
void c_f(void(*pf)(int)){
pf(2);
}
void c_foo(int i) {
printf("%d\n", i);
}
I'm curious to know whether clang and GCC are compliant with the Standard, but I can't verify this in a web compiler.
Edit
It dawned on me that I really don't need two files to verify whether clang and GCC are compliant to the Standard, on the issue mentioned above. If the Standard requires the address of a C function, as an argument for the function c_f()
and the code in main.cpp
supplies the address of a C++ function, the C++ compiler has to complain1 when compiling this file. But that doesn't happen neither in clang nor in GCC. Then, I might as well say that both clang and GCC are also buggy on this regard.
1) If we assume that a diagnostic is required
Your code shows undefined behavior according to [dcl.link]/1 and [expr.call]/1 (emphases are mine):
[dcl.link]/1:
All function types, function names with external linkage, and variable names with external linkage have a language linkage. [ Note: Some of the properties associated with an entity with language linkage are specific to each implementation and are not described here. For example, a particular language linkage may be associated with a particular form of representing names of objects and functions with external linkage, or with a particular calling convention, etc. — end note ] The default language linkage of all function types, function names, and variable names is C++ language linkage. Two function types with different language linkages are distinct types even if they are otherwise identical.
[expr.call]/1:
A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. The postfix expression shall have function type or function pointer type. For a call to a non-member function or to a static member function, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion is suppressed on the postfix expression), or it shall have function pointer type. Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior ([dcl.link]). For a call to a non-static member function, the postfix expression shall be an implicit ([class.mfct.non-static], [class.static]) or explicit class member access whose id-expression is a function member name, or a pointer-to-member expression selecting a function member; the call is as a member of the class object referred to by the object expression. In the case of an implicit class member access, the implied object is the one pointed to by this. [ Note: A member function call of the form f() is interpreted as (*this).f() (see [class.mfct.non-static]). — end note ] If a function or member function name is used, the name can be overloaded, in which case the appropriate function shall be selected according to the rules in [over.match]. If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider in the dynamic type of the object expression is called; such a call is referred to as a virtual function call. [ Note: The dynamic type is the type of the object referred to by the current value of the object expression. [class.cdtor] describes the behavior of virtual function calls when the object expression refers to an object under construction or destruction. — end note ]