extern C can not be used at class level?

31,601

Solution 1

You can sort of apply extern "C" to a member function via a very convoluted (but entirely legal) hack:

extern "C" typedef int bar_t(int x);

struct foo {
     bar_t bar; // yes, this declares a nonstatic member function!
};

int foo::bar(int x) { return x; } // definition

This is possible according to ISO C++03 9.3[class.mfct]/9:

a member function can be declared (but not defined) using a typedef for a function type. The resulting member function has exactly the same type as it would have if the function declarator were provided explicitly, see 8.3.5.

However, this doesn't really buy you anything, because of ISO C++03 7.5[dcl.link]/4:

A C language linkage is ignored for the names of class members and the member function type of class member functions.

Solution 2

extern "c" uses c-style linking; that is, the raw function name is what exposed from the library. Because it is just a raw function name, none of the C++-only features will work with it, including methods or extern data members in namespaces, classes, structs or unions.

Clarifying: Structs and unions are in C, but have no member functions, so their member functions in C++ cannot be exported in a c-style (and the struct and union definitions need not be exported, since it is already in the header)

Solution 3

Looking at a comment you placed on a previous answer ("[M]y question is just whether we could apply extern C at class level so that all functions in the class automatically has C style name mangling?", the answer is 'extern "C" doesn't quite work that way.'

Syntactically, extern "C" can be applied to either a single statement of a curly-delimited block:

extern "C" int my_foo(int i)
{
    ...
}

extern "C" {
    int my_bar(int i)
    {
        ...
    }

    int my_baz(int i)
    {
        ...
    }
}

It's common to use extern "C" with the appropriate #ifdef __cplusplus guards on entire C headers.

Semantically, the actual effect of applying extern "C" will only apply to "normal" (i.e., non-class) functions and pointers to functions. Of course you cannot apply it to a C++ template. Nor can you apply it to class methods (because a class method needs to know which object it was called on, and C-style linkage does not have any way to pass that information in to the function).

It is possible to apply extern "C" on functions that exist in a namespace, but the namespace information will simply disappear when used via C.


Update

If you already have a class (we'll use a POD class for simplicity), and you want to make it usable from C, you'll need to apply extern "C" to a function callable in C. Unfortunately this gets ugly even in simple cases:

// in the header file
#ifdef __cplusplus
namespace X {
#endif
    struct A
    {
        int x;
#ifdef __cplusplus
        A() : x(5) { }
        int foo()
        {
             return x += 5;
        }
#endif
    };
#ifdef __cplusplus
    extern "C" {
#endif
        int A_foo(struct A a);
        struct A A_create();
#ifdef __cplusplus
    }
}
#endif


// in the .cc file
#include "try.h"

namespace X {
    extern "C" {
        int A_foo(A* a)
        {
            return a.foo();
        }

        A A_create()
        {
            return A();
        }
    }
}

// in the .c file
#include <stdio.h>
#include "try.h"

int main()
{
    struct A a = A_create();
    printf("%d", A_foo(a));
}

Using gcc you would compile this as follows:

  • the C++ file: g++ try.cc -c -o try.o
  • the C file : gcc try.c try.o

There are a few important points:

  • If your C++ file calls into the STL behind the scenes, or calls new or delete (or new[] or delete[]) you will need to link the final program to the C++ runtime library (the command line switch for this in gcc is -lstdc++.
  • You're probably going to want to pass identical optimization flags when compiling both the C and C++ code (optimization can affect the size of the objects, and if the size doesn't match you can get into a lot of trouble). Ditto for multithreading.
  • You can use exceptions all you want in the C++ code, but once they cross C code all bets are off.
  • If you want something more complex you'll probably want to look at the PIMPL pattern.
  • When a struct falls out of scope in C code the C++ destructor is not called (some compilers may promise to do so, but it's not standard). If you need to do any clean up on these objects you'll need to call an extern "C" function that calls the destructor.

To call the destructor explicitly:

extern "C" void A_destroy(struct A a)
{
    a.~A();
}

Solution 4

I'm afraid not. But if you want to pass an object of C++ to C functions, you may refer to this link: http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.8

Solution 5

Ummm... extern "C" forces C-style linkage. It cannot be used with classes AFAIK.

Share:
31,601
George2
Author by

George2

Updated on July 05, 2022

Comments

  • George2
    George2 almost 2 years

    Just want to confirm in Windows environment, VSTS 2008 + C++ project, we could only apply extern C to function level, not be able to apply to class level (so that all member functions from the class use C language name mangling)? I have tried several ways, but always compile error.

    thanks in advance, George

  • George2
    George2 almost 15 years
    Also, can not be used with struct?
  • George2
    George2 almost 15 years
    Hi Todd, please correct me if I am wrong, structs and union are C feature, and we could not add extern C to struct and union not in namespace?
  • George2
    George2 almost 15 years
    1. "My guess is that extern "C" won't work with anything that requires name mangling so functions that are overridden are probably out too." -- extern C is used to enforce C style name mangling, but why you say extern "C" won't work with anything that requires name mangling? 2. "as long as none of the data members require construction or destruction" -- require construction or destruction means non-POD data types which does not have ctor/dtor?
  • George2
    George2 almost 15 years
    No, my question is just whether we could apply extern C at class level so that all functions in the class automatically has C style name mangling?
  • ephemient
    ephemient almost 15 years
    @George2: What do you mean by "C style name mangling"? There is no such thing, C identifiers are not mangled.
  • George2
    George2 almost 15 years
    Thanks Todd, if member functions of class can not be exported using extern C, how could we export member function? Have to define global function wrapper and export global function wrapper?
  • ephemient
    ephemient almost 15 years
    I suppose ysth was trying to provide a counterexample, but I don't see it: C identifier "" --> symbol "" for linking.
  • George2
    George2 almost 15 years
    @Max, I want to confirm with you that member functions of C++ class can not be exported using extern C, how could we export member function? Have to define global function wrapper and export global function wrapper?
  • George2
    George2 almost 15 years
    "It is possible to apply extern "C" on functions that exist in a namespace, but the namespace information will simply disappear when used via C." -- excellent! I learned something new! :-)
  • George2
    George2 almost 15 years
    @ephemient, what do you mean by "C identifier "" --> symbol "" for linking."? Could you say in some other words please?
  • michelson
    michelson almost 15 years
    C linkers don't really "mangle names" in the traditional sense. At most, they will prefix an underscore to the name. C++ "name mangling" encodes type information into the linker symbol to make function overloading and other aliasing possible. "extern C" is best described as turning off C++ name mangling. The result is that anything that the type encoding makes possible is no longer possible. Therefore, no overloads, no methods, no constructor/destructors, etc. Check en.wikipedia.org/wiki/Name_mangling for a good description.
  • Max Lybbert
    Max Lybbert almost 15 years
    You could define "C-style mangling" as "no mangling." Name mangling is used for C++ as a way of getting linkers to fake support for overloaded functions. Since C doesn't have overloaded functions, you don't have to worry about it as much. Some architectures, however, add an underscore to a variable name; so "x" in source will become "_x" in the binary. I haven't seen it, but it is possible that a C compiler could use C++ name mangling as a way of catching type errors at link time, even without overloading. But link time is too late to be helpful.
  • AnT stands with Russia
    AnT stands with Russia over 14 years
    Yeah, this is a little-known feature :) You can also include a trailing const-qualifier into the typedef-name declaration as in typedef int bar_t(int) const; in which case that typedef name will only be usable for declaring member functions, but not ordinary functions.
  • Paul Stelian
    Paul Stelian over 4 years
    @MaxLybbert And some platforms, I think Windows tends to add that underscore to at least some of the functions if not all of them.