5

I have a C/C++ program that's a plugin to Firefox. Because it's a plugin, it has non-main entry points. Those entrypoints need to be compiled in C because otherwise they get name mangled.However, other functions are over-loaded, so they need to be C++. The solution is extern "C". That much I've already figured out.

However, if I extern "C" around the .c file, I get link errors because the C++ files got name mangled, but the calls in the .c file didn't. At least I THINK that's what's happening.

The solution APPEARS to be to put the extern "C" around the .h file. This SEEMS to mean that the names of the functions declared in the .h file aren't mangled, even when they're defined in the (presumably mangled) .c file.

However, it doesn't really make any sense to me why this would work. Is this a kludge? Am I setting myself up for a hard to find bug later? or is this the right way to solve this?

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
Brian Postow
  • 11,709
  • 17
  • 81
  • 125
  • @Michael's answer is dead-on. This technique is so common as to be the water that C++ programmers swim in. Plug-in architectures almost always use a 'C' API with an object-like hierarchy (using "handles" to represent object instances) because, at least in the past, C++ linking has been far too complex and implementation-dependent to be reliable. – David Gladfelter Feb 10 '10 at 16:46

7 Answers7

12

The least confusing thing (at least for me) to do is to make the declarations in the header match the function definitions. As other answers have mentioned, it's the declaration/prototype that matters most - the extern "C" linkage specification can be omitted from the function definition as long as the prototype has been 'seen' by the compiler already when it gets to the definition. However, personally I find it preferable and less potentially confusing to have the declarations and the definitions match, but that's a preference that other programmers might not share.

Note that you don't have to extern "C" everything in a header or implementation file - the extern "C" can be applied to a single name or to a group of names (by applying it to a block).

So, in your header you can have something like:

// entrypoints.h

#ifdef __cplusplus
// this opens an extern "C" block, but only if being included for a C++ compile
//  if this is being included in a C compile, the extern "C" bits won't be seen
extern "C" {
#endif

int foo(void);
int bar( void*);


#ifdef __cplusplus
// close the extern "C" block, but only if we started it in the first place
}
#endif

And your implementation of them:

// entrypoints.cpp

// Note: since this is a .cpp file, it's compiled as C++ so we
//  don't need the #ifdefs around the extern "C"

#include "entrypoints.h"

extern "C"
int foo(void)
{
    return 42;
}


extern "C"
int bar( void* p)
{
    return -42;
}
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • The problem is that if I wrap the implementations in extern "C", It won't link to the mangled function names of the C++ functions in other files... This iswhy I only extern "C" the .h file, which is what felt weird... – Brian Postow Feb 10 '10 at 16:50
  • 1
    What makes you say that? Are you saying that you think extern "c" will cause the linker to search for dependencies by their unmangled names? If so, please be aware that extern "C" only applies to _declarations_ at the file scope, not to method/function calls inside the function implementations. – David Gladfelter Feb 10 '10 at 16:58
  • 1
    The functions that get called by the `extern "C"` function implementations are not impacted at all - the implementation is still C++ and the declarations/prototypes that `foo()` and `bar()` see when `entrypoints.cpp` is compiled do not need to have `extern "C"` applied to them (unless the called functions actually are `extern "C"`) - the functions implemented in `entrypoints.cpp` can call any C++ code and can use all C++ facilities that a non-`extern "C"` function can use.. – Michael Burr Feb 10 '10 at 17:00
  • When I put the extern "C" around the whole .c file, the linker couldn't find any of my functions in other files. When I put the extern "C" only in the .h file, it could. This was my initial confusion... because I THOUGHT it worked as you describe, but it SEEMS to work the other way... – Brian Postow Feb 10 '10 at 19:00
  • When you put the `extern "C"` around the whole .c file (I assume it's a C++ file even if you call it a .c file), did you have the `#include` statements inside that `extern "C"` block? If so that's probably your problem - you generally let headers manage whether the things it's declaring are `extern "C"` or not. There are times when you may need to wrap the `#include` inside an `extern "C"` block, but nowadays that should only be rarely necessary and only for including older headers into a C++ module where the header is strictly C declarations and is oblivious to the existance of C++. – Michael Burr Feb 10 '10 at 19:13
  • The .c file here is standard c, but being compiled as C++ because other files need C++.. If I put the #include outside of the extern, the compiler complained about what I (I guess incorrectly) assumed was the fact that there was code (declarations) outside of the extern C. Since until this thread, I'd never seen extern "C" except around the whole .c file... At that point I did NOT have the extern "C" in the .h file, so there wasn't a double extern or anything... – Brian Postow Feb 10 '10 at 19:47
  • 1
    If the .c file is straight C code, it shouldn't need to be compiled as C++, and all (or nearly all) compilers and IDEs will handle that just fine. As long as it's only calling C++ functions that are declared as `extern "C"`, it can be compiled as C - that's probably the main reason for the `extern "C"` linkage declaration: to let C compiled modules call functions in C++ compiled modules. – Michael Burr Feb 10 '10 at 20:31
7

Extern "C" should apply to function prototype, so if you have separate prototype and implementation, put extern declaration around prototypes. Implementation, provided prototype is visible, will be extern as well and not mangled. It is not a bug.

Anycorn
  • 50,217
  • 42
  • 167
  • 261
3

This is not a kludge - this is how C/C++ compilers work. When the compiler compiles a file that uses your library, it checks the .h files for declarations of functions and globals. Hence, you wrap the .h files with extern "C" to avoid name mangling.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
2

Only the declaration of the functions needs to be wrapped in extern "C". Once the function has been declared, the compiler knows that its name shouldn't be mangled. Since most of the time your function declarations are in your header file, wrapping the extern "C" around your header or around the include of it should work fine.

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
1

It is already enough when you only extern "C" the entities you need to be externally visible. You can make a standard cc/cpp/cxx/... file, but cherry pick C-names:

class Foo {};

extern "C" int someInterfaceFun () {
    Foo foo;
    return 0xFEED;
}
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
  • The problem is that if I wrap the implementations in extern "C", It won't link to the mangled function names of the C++ functions in other files... This iswhy I only extern "C" the .h file, which is what felt weird – Brian Postow Feb 10 '10 at 16:51
  • Though when you only `extern"C"` the declarations, but not the definitions, than they might possibly not match, and you possibly get linker errors. edit: possibly == the C++ standard does not define mangling rules – Sebastian Mach Feb 10 '10 at 17:01
  • I got linker errors when I put the extern on the definitions! because it couldn't find my C++ functions... – Brian Postow Feb 10 '10 at 19:44
1

You seem to be overly obsessed with this "putting around"/"wrapping" thing. At the fundamental level you are not normally supposed to put extern "C" "around" anything. extern "C" is a declaration specifier, a linkage specifier, which assigns C-linkage to a specific name. Again, it applies to specific individual names. At the fundamental level you are supposed to declare each of your C++-to-C interface functions in your header file as extern "C", as in

extern "C" void foo(void);
extern "C" char bar(int);

Later you can specify the same extern "C" to the function definitions in the implementation file

extern "C" void foo(void) {
  /* whatever */
}

extern "C" char bar(int) {
  /* whatever */
}

But strictly speaking, it is not really necessary to repeat extern "C" in the definition, since if you have already declared your function as extern "C", this linkage specification basically gets "inherited" by the definition as well (assuming the declaration is made before the definition in this translation unit). In other words, these function names will not get "mangled", since the compiler knows already, that these names are declared as extern "C" names.

That's all there's to it. Now, as you already know, you can also use a {} form of extern "C" that lets you wrap the entire section of a file into the extern "C" region. Well, that's just a nice side-feature of extern "C" that can save you some typing. But normally, you should not just indiscriminately enclose entire files into the extern "C" region - this is akin to shooting sparrows with a cannon. The specifier is supposed to be applied selectively, only to the names that really need C-linkage. Actually, in many cases you might (and will) have a dedicated C-interface header file that contains only declarations that are supposed to have C-linkage. Putting the extern "C" around the entire header file in this case is normal practice, but in general case you should use it more carefully and make sure you are not covering with a blanket extern "C" specifier something that is not supposed to have C-linkage.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Sorry, that was just my way of saying whether I was putting it in the .c (on the definitions) or .h (on the declarations) The thing that confused me was that marking the definitions as extern C made things break, but marking the declarations didn't... – Brian Postow Feb 10 '10 at 19:44
0

if the .c file includes the .h file and the function prototypes in the .h file are in an extern "C" block, everything should be ok. The functions declared as extern "C" won't have mangled names, all the other functions will.

Chris Card
  • 3,216
  • 20
  • 15