Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++11 not support declaring extern "C" on a static member function?

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?

like image 604
xmllmx Avatar asked Jan 18 '13 08:01

xmllmx


People also ask

Can you extern a static function in C?

3.1. Static variables in C have the following two properties: They cannot be accessed from any other file. Thus, prefixes “ extern ” and “ static ” cannot be used in the same declaration.

Can we use a variable in a file using extern which is defined as both static as well as global in the base file?

It is possible to create a global variable in one file and access it from another file. In order to do this, the variable must be declared in both files, but the keyword extern must precede the "second" declaration. A static variable can be either a global or local variable.

Can we declare a static function as virtual?

Can Static Functions Be Virtual in C++? In C++, a static member function of a class cannot be virtual. Virtual functions are invoked when you have a pointer or reference to an instance of a class.

What does extern C do when attached to the front of a function declaration?

In a const variable declaration, it specifies that the variable has external linkage. The extern must be applied to all declarations in all files. (Global const variables have internal linkage by default.) extern "C" specifies that the function is defined elsewhere and uses the C-language calling convention.


2 Answers

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.

like image 65
Potatoswatter Avatar answered Oct 06 '22 23:10

Potatoswatter


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.

like image 20
James Kanze Avatar answered Oct 07 '22 00:10

James Kanze