Jump to content
 

What every embedded programmer should know about ...

  • entries
    29
  • comments
    13
  • views
    1,923

Contributors to this blog

Using : Extern "C"

Orunmila

135 views

I realize now that I have a new pet peeve.

The widespread and blind inclusion, by Lemmings calling themselves embedded C programmers, of  extern "C" in C header files everywhere.

To add insult to injury, programmers (if I can call them that) who commit this atrocity tend to gratuitously leave a comment in the code claiming that this somehow makes the code "compatible" with C++ (whatever that is supposed to mean - IT DOES NOT !!!).

It usually looks something like this (taken from MCC produced code - uart.h)

#ifdef __cplusplus  // Provide C++ Compatibility
    extern "C" {
#endif

So why does this annoy me so much? (I mean besides the point that I am unaware of any C++ compiler for the PIC16, which I generated this code for - which in itself is a hint that this has NEVER been tested on any C++ compiler ! )

First and foremost - adding something to your code which claims that it will "Provide C++ Compatibility" is like entering into a contract with the user that they can now safely expect the code to work with C++.  Well I have bad news for you buddy - it takes a LOT more than wrapping everything in extern "C" to make your code "Compatible with C++" !!!  

If you are going to include that in your code you better know what you are doing and your code better work (as in actually being tested) with a real C++ compiler. If it does not I am going to call out the madness you perpetrated by adding something which only has value when you mix C and C++, but your code cannot be used with C++ at all in the first place - because you are probably doing a host of things which are incompatible with C++ !

In short, as they say -  your header file comment is writing cheques your programming skills cannot cash.

Usually a conversation about this starts with me asking the perpetrator if they can explain to me what "That thing" actually does, and when do you think you need this to use the code with a C++ compiler, so let's start there. I can count on one hand the people who have been able to explain to me how this works correctly.

Extern "C" - What it does and what it is meant for

This construct is used to tell a C++ compiler that it should set a property called langage linkage which affects mangling of names as well as possible calling conventions. It does not guarantee that you can link to C code compiled with a different compiler.

This is a good point to quote the C++14 specification, section 7.5 on Language linkage 

Quote

Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved.

That said, if you are e.g. using GCC for your C as well as your C++ compiler you will have a very good chance of being lucky enough that the implementation-defined linkage will be compatible!

A C++ compiler will use "name mangling"  to ensure that symbols with identical names can be distinguished from each other by passing additional semantic information to the linker. This becomes important when you use e.g. function overloading, or namespaces. This mangling of names is a feature of C++ (which allows duplicate names across the program, but does NOT exist in C (which does not allow duplicate symbols to be linked at all).

When you place an extern "C" wrapper around the declaration of a symbol you are telling the C++ compiler to disable this name mangling feature and also to alter it's calling convention when calling a function to be more likely to link with C code, although calling conventions are a topic all in it's own.

As you may have deduced by now there is a reason that this is not called  "extern C++"! This is required to make your C++ calls match the names and conventions in the C object files, which does not contain mangled names.  If you ARE going to place a comment next to your faux pas, at least get it right and say "For compatibility with C naming and calling conventions" instead of claiming incorrectly that this is required for C++ compatibility somehow! 

There is one very specific use case, one which we used to encounter all the time in what would now be called "the olden days" of Windows 3.1 when everything was shared through dynamic link libraries (.dll files). It turns out that in order to use a dll which was written using C++ from your C program you had to wrap the publicly exported function declarations in extern "C". 

Yes that is correct, this is used to make C++ code compatible with C, NOT THE OTHER WAY AROUND!

So when do I really need this then?

Let's take a small step back. If you want to use your C code with a C++ compiler, you can simply compile it all with the C++ compiler! The compiler will resolve the names just fine after mangling them all, and since you are properly using name spaces with your C++ code this will reduce the chances of name collisions and you will be all the happier for doing the right thing. No need for disabling name mangling there. 

If you do not need this to get your code to compile with a C++ compiler, then when DO you need it?

Ahhh, now you are beginnng to see why this has become a pet peeve of mine ... but the plot still has some thickening to do before we are done...

Pretty much the only case where it makes sense to do this is when you are going to compile some of your code with a C compiler, then compile some other code with a C++ compiler, and in the end take the object files from the two compilers and link them together, probably using the C++ linker. Of course when you feed your .c files and your .cpp files to a typical C++ compiler it will actually run a C compiler on the .c files and the C++ compiler on the .cpp files by default, and mangling will be an issue and you will exclaim "you see! He was wrong!", but not that fast ... it is simple enough to tell the compiler to compile all files with the C++ compiler, and this remains the best way to use C libraries in source form with your C++ code.

If you are going to compile the C code into a binary library and link it in (which sometimes happens with commercial 3rd party libraries - like .dll's - DUH ! ) then there is a case for you to do this, but most likely this does not apply to you at all as you will have the source available all the time and you are working on an embedded system where you are making a single binary.

To help you ease the world of hurt you are probably in for - you should read up on how to tell your compiler to use a particlar calling convention which has a chance of being compatible with the linker. If you are using GCC you can start here.

To be clear, if you add extern "C" to your C code and then compiles it into an object file to be linked with your C++ program the extern "C" qualifier is entirely ignored by the C compiler. Yes that is right, all the way to producing the object file this has no effect whatsoever. It is only when you are producing calls to the C code from the C++ code that the C++ code is altered to match the C naming and calling conventions, so this means that the C++ code is altered to be compatible with C.

In the end

There is a hell of a lot of things you will need to consider if you want to mix C and C++. I promise that I will write another blog on that next time around if I get anybody asking for it in the comments.

Adding extern "C" to your code is NOT required to make it magically "compatible with C++". In order to do that you need to heed with extreme caution the long list of incompatibilities between C and C++ and you will probably have to be much more specific than just stating C and C++. Probably something like C89 and C++11 if I had to wager a guess to what will be most relevant.

And remember the reasons why it is almost always just plain stupid to use extern "C" in your C headers.

  1. It does not do what you think it does.
  2. It especially does not do what your comment claims it does - it does not provide C++ compatability!
  3. Don't let your comments write cheques your code cannot cash! - Before you even think of adding that, make sure you KNOW AND FOLLOW ALL the C++ compatability rules first.
  4. For heaven's sake TEST your code with a C++ compiler !
  5. If you want to use a "C" library with your C++ code simply compile all the code with your C++ compiler. QED - no mess no fuss!
  6. If it is not possible to do 5 above, then compile the C code normally (without that nonsense) and place extern "C" around the #include of the C library only! (example below). After all, this is for providing C linkage compatability to C++ compiled code!
  7.  If you are producing a binary library using C to be used/linked with a C++ compiler, then please save us all and just compile the library with both C and C++ and provide 2 binaries! 
  8. If all of the above fail, beacuase you really just hit that one in a million case where you think you need this, then for Pete's sake educate yourself before you attempt something that hard, hopefully in the process you will realize that it is just a bad idea after all!

Now please just STOP IT!

I feel very much like pulling a Jeff Atwood and saying after all that only a moron would use extern "C" in their C headers (of course he was talking about using tabs).

Orunmila

 

Oh - I almost forgot - the reasonable way of using extern "C" looks like this:

#include <string>  // Yes <string>, not <string.h> - this is C++ !
#include <stdio>

// Include C libraries
extern "C" {
  #include "clib1.h"
  #include "clib2.h"
}

using namespace std; // Because the cuticles on my typing finger hurt if I have to type out "std::" all the time!

// This is a proper C++ class because I am too patrician to use "C" like that peasant library writer!
class myClass {
  private:
     int myPrivateInt;
  ...
  ...

 

Appeals to Authority

Dan Saks CppCon talk entitled “extern c: Talking to C Programmers about C++” 
A very good answer on Stackoverflow
Some decent answers on this Stackoverflow question
Fairly OK post on geeksforgeeks
The dynamic loading use case with examples 

Note: That first one is a bit of a red herring as it does not explain extern C at all - nevertheless it is a great talk which all embedded programmers can benefit from 🙂 

  • I Agree 1


1 Comment


Recommended Comments

Fantastic.  I "unfortunately" remember those olden days of writing DLL's for use in Windows 3.1.  One of my first JAVA experiences was writing a DLL to call a JAVA program to access hardware for an embedded system.  There is a long list of things that don't "quite" work the same in C++ and C, never mind the differences between the different revision.

Share this comment


Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...