Provided that I have a C library containing a function declared as void g(void (*callback)());
The following code is elegant yet illegal:
struct A
{
// error C2159: more than one storage class specified (VC++ Nov 2012 CTP)
static extern "C" void callback()
{}
};
g(A::callback);
Why does C++11 not support this?
This is a particularly confusing topic to wade into. Let's attack §7.5 "Linkage specifications" [dcl.link].
1) All function types, function names with external linkage, and variable names with external linkage have a language linkage.
Note that the property of language linkage applies to two completely different kinds of entities: types and names.
A function has a generally-invisible bit of information in its type which identifies which ABI it conforms to: C calling conventions, Pascal, Fortran, all might be specified to use the stack in different ways, so calling them through a pointer requires knowing the invisible language-tag.
The name of a variable or function from another language can be accessed syntactically through C++, or from the other language referring to a C++ declaration. But not every language can match up with C++'s naming scheme and OO model. So interfaces in this scheme don't include classes.
Because these things are managed separately, it's possible to have something with different linkage in its type (calling conventions) and its name (linker symbol).
4) 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 (3.3). 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. A C language linkage is ignored in determining the language linkage of the names of class members and the function type of class member functions.
The extern "C" {}
affects all function declarations, including pointers and references, except member functions. Since a function may only be defined in a namespace or as a member, C functions can only be defined at namespace scope.
The standard gives an example here:
extern "C" typedef void FUNC_c();
class C {
// the name of the function mf1 and the member
// function’s type have C++ language linkage; the
// parameter has type pointer to C function
void mf1(FUNC_c*);
// the name of the function mf2 and the member
// function’s type have C++ language linkage
FUNC_c mf2;
// the name of the data member q has C++ language
// linkage and the data member’s type is pointer to
// C function
static FUNC_c* q;
};
You can emulate the behavior you want using a typedef
, though. From another example in §7.5/4,
extern "C" typedef void FUNC();
// the name f2 has C++ language linkage and the
// function’s type has C language linkage
FUNC f2;
Combining these examples with yours, you can have
extern "C" typedef void callback_t();
callback_t A_callback; // declare function with C++ name and C type
struct A
{
static callback_t &callback; // not a member function
};
// in source file:
// definition matches semantics of declaration, although not syntax
void A_callback() { ... }
// define static member reference
callback_t &A::callback = A_callback;
g(A::callback); // call syntax is emulated
In practice, it seldom makes a difference. C and C++ use compatible calling conventions on most platforms (see Jonathan Wakely's comments on this page for exceptions), as long as you don't try to pass or return a non-POD C++ class type. This is a less-implemented feature of C++, due to the confusing overloading of terms and conceptual distinctions ranging from subtle to academic.
First, the function declaration is legal. The extern "C"
,
however, is ignored for class members, so if g
expects an
extern "C" void (*)()
, you can't pass it callback
.
As to the why of this, I suspect that originally, it was mainly
a question of orthogonality: it makes no sense for a class
member function to be extern "C"
in general, and orthogonality
(or simply no one considered the case of static members) means
that this applies to static member functions as well, although
allowing them to be extern "C"
would have been useful. Today
(i.e. C++11), it would be problematic to change the rule,
because it could break existing code. IMHO, the change would
be acceptable, because the amount of code it would break is
probably very small, and the breakage results in a compile time
error—not a change in runtime semantics—so is easily
detected and fixed. Still, as far as I know, no one made
a proposal to change this, so it didn't get changed.