C++ Migration Guide

Pointers to extern "C" Functions

A function can be declared with a language linkage, such as

extern "C" int f1(int);

If you do not specify a linkage, C++ linkage is assumed. You can specify C++ linkage explicitly:

extern "C++" int f2(int);

You can also group declarations:


extern "C" { 
    int g1(); // C linkage 
    int g2(); // C linkage
    int g3(); // C linkage
} // no semicolon 

This technique is used extensively in the standard headers.

Language Linkage

Language linkage means the way in which a function is called: where the arguments are placed, where the return value is to be found, and so on. Declaring a language linkage does not mean the function is written in that language. It means that the function is called as if it were written in that language. Thus, declaring a C++ function to have C linkage means the C++ function can be called from a function written in C.

A language linkage applied to a function declaration applies to the return type and all its parameters that have function or pointer-to-function type.

The C++ 4.2 compiler implements the ARM rule that the language linkage is not part of the function type. In particular, you can declare a pointer to a function without regard to the linkage of the pointer, or of a function assigned to it. The C++ 5.0 compiler in compatibility mode uses the same rule.

The C++ 5.0 compiler in standard mode implements the new rule that the language linkage is part of its type, and is part of the type of a pointer to function. The linkages must therefore match.

The following example shows functions and function pointers with C and C++ linkage, in all four possible combinations. The 4.2 compiler and the 5.0 compiler in compatibility mode accept all combinations. The 5.0 compiler in standard mode accepts the mismatched combinations only as an anachronism.


extern "C" int fc(int) { return 1; }      // fc has C linkage
int fcpp(int) { return 1; }               // fcpp has C++ linkage
// fp1 and fp2 have C++ linkage
int (*fp1)(int) = fc;                     // Mismatch
int (*fp2)(int) = fcpp;                   // OK 
// fp3 and fp4 have C linkage 
extern "C" int (*fp3)(int) = fc;          // OK 
extern "C" int (*fp4)(int) = fcpp;        // Mismatch

If you encounter a problem, be sure that the pointers to be used with C linkage functions are declared with C linkage, and the pointers to be used with C++ linkage functions are declared without a linkage specifier, or with C++ linkage. For example:


extern "C" {
    int fc(int);
    int (*fp1)(int) = fc; // Both have C linkage
}
int fcpp(int); 
int (*fp2)(int) = fcpp;   // Both have C++ linkage

In the worst case, where you really do have mismatched pointer and function, you can write a "wrapper" around the function to avoid any compiler complaints. (On Solaris, C and C++ function linkage is the same. However, in the most general case, the mismatched linkage really is an error. Hence the new language rule.)

In the following example, composer is a C function taking a pointer to a function with C linkage.


extern "C" void composer( int(*)(int) );
extern "C++" int foo(int); 
composer( foo ); // Mismatch

To pass function foo (which has C++ linkage) to the function composer, create a C-linkage function foo_wrapper that presents a C interface to foo:


extern "C" void composer( int(*)(int) );
extern "C++" int foo(int);
extern "C" int foo_wrapper(int i) { return foo(i); }
composer( foo_wrapper ); // OK

In addition to eliminating the compiler complaint, this solution works even if C and C++ functions really have different linkage.

A Less-Portable Solution

The Sun implementation of C and C++ function linkage is binary-compatible. That is not the case with every C++ implementation, although it is reasonably common. If you are not concerned with possible incompatibility, you can employ a cast to use a C++-linkage function as if it were a C-linkage function.

A good example concerns static member functions. Prior to the new C++ language rule regarding linkage being part of a function's type, the usual advice was to treat a static member function of a class as a function with C linkage. Such a practice circumvented the limitation that you cannot declare any linkage for a class member function. You might have code like the following:


// Existing code
typedef int (*cfuncptr)(int);
extern "C" void set_callback(cfuncptr);
class T {
    ...
    static int memfunc(int);
};
...
set_callback(T::memfunc); // no longer valid

As recommended in the previous section, you can create a function wrapper that calls T::memfunc and then change all the set_callback calls to use a wrapper instead of T::memfunc. Such code will be correct and completely portable.

An alternative is to create an overloaded version of set_callback that takes a function with C++ linkage and calls the original, as in the following example:


// Modified code
extern "C" {
    typedef int (*cfuncptr)(int); // ptr to C function
    void set_callback(cfuncptr);
}
typedef int (*cppfuncptr)(int); // ptr to C++ function
inline void set_callback(cppfuncptr f) // overloaded version
    { set_callback((cfuncptr)f); }
class T {
    ...
    static int memfunc(int);
};
...
set_callback(T::memfunc); // unchanged from original code

This example requires only a small modification to existing code. An extra version of the function that sets the callback was added. Existing code that called the original set_callback now calls the overloaded version that in turn calls the original version. Since the overloaded version is an inline function, there is no runtime overhead at all.

Although this technique works with Sun C++, it is not guaranteed to work with every C++ implementation, since the calling sequence for C and C++ functions may be different on other systems.

Pointers to Functions as Function Parameters

A subtle consequence of the new rule for language linkage involves functions that take pointers to functions as parameters, such as


extern "C" void composer( int(*)(int) );

:.

An unchanged rule about language linkage is that if you declare a function with language linkage, and follow it with a definition of the same function with no language linkage specified, the previous language linkage applies. For example:


extern "C" int f(int);
int f(int i) { ... } // Has "C" linkage

In this example, function f has C linkage. The definition that follows the declaration (the declaration might be in a header file that gets included) inherits the linkage specification of the declaration. But suppose the function takes a parameter of type pointer-to-function, as in the following example:


extern "C" int g( int(*)(int) );
int g( int(*pf)(int) ) { ... } // Is this "C" or "C++" linkage?

Under the old rule, and with the 4.2 compiler, there is only one function g. Under the new rule, the first line declares a function g with C linkage that takes a pointer-to-function-with-C-linkage. The second line defines a function that takes a pointer-to-function-with-C++-linkage. The two functions are not the same; the second function has C++ linkage. Because linkage is part of the type of a pointer-to-function, the two lines refer to a pair of overloaded functions each called g. Code that depended on these being the same function breaks. Very likely, the code fails during compilation or linking.

It is a good programming practice to put the linkage specification on the function definition as well as on the declaration:


extern "C" int g( int(*)(int) );
extern "C" int g( int(*pf)(int) ) { ... }

You can further reduce confusion about types by using a typedef for the function parameter:


extern "C" {typedef int (*pfc)(int);} // ptr to C-linkage function
extern "C" int g(pfc);
extern "C" int g(pfc pf) { ... }