Calling C Code from C++ With ‘extern “C”‘

Now and then we have the need to call functionality that was written in C from our C++ programs. For that, we need to use and understand extern "C".

The probably easiest way to use C functionality in a C++ program is to simply compile the C code as C++ code. This will, however, not work reliably. While C++ is based on C, the two languages have their differences. They have even diverged insofar that modern C has features that are not valid C++.

So, we have to compile the C code as C, and the C++ code as C++. Some compilers do this automatically by file extension, others need to be told explicitly. The actual issue is to link the compiled C and C++ object files together.

Linking and name-mangling

Very broadly speaking, the linker has to resolve symbols that are referenced in one or more translation units with their definition in another translation unit. Those symbols can be variable names or function names. For simplicity, let’s assume we have a function void foo(int) that has been defined in one source file and gets called in another source file.

In C, the compiler generates a simple symbol foo for that function – this is defined in the C standard. In C++, we can have much more than one function named foo: we have different namespaces, classes with member functions, and overloaded functions that take different parameters. Therefore, the C++ compiler can not simply create a symbol foo. It has to generate names that contain all that information. The process is called name mangling and is not standardized.

Let’s assume, foo is our C function, that we want to call from main in our C++ program:

//main.cpp 

#include "foo.h"
int main() {
  foo(22);
}
//foo.h
void foo(int);
#include <stdio.h>
//foo.c
void foo(int i) {
  printf("%i\n", i);
}

When we compile the whole thing, the linker will give us an error: The C++ compiler will see the declaration of void foo(int) and the call to that function and generate a mangled name, say, void@foo(int). The C compiler will simply generate the symbol foo. The linker will, therefore, complain that it can not find void@foo(int), because that symbol simply does not exist.

extern “C” to the rescue

To solve the above problem, the C++ standard allows declaring things with language linkage. Besides the default C++ linkage, we can explicitly declare things to have “C” linkage. Declaring foo with “C” linkage will cause the C++ compiler to refer to the name foo instead of the mangled name. We can declare single entities to have “C” linkage as follows:

extern "C" void foo(int);

More convenient is to declare a whole list of declarations to be of “C” linkage:

extern "C" {
  void foo(int);

  //more declarations...
}

Note that this is strictly C++ code, as C does not allow the language linkage specification. So, how do we bring all this together without having to rewrite all the C declarations with “C” linkage for the C++ compiler?

The wrong solution

What we see often is, that developers start to alter the C headers as follows:

//foo.h
#ifdef __cplusplus
extern "C" {
#endif

  void foo(int);

#ifdef __cplusplus
} //end extern "C"
#endif

This will work as intended, as the extern "C" will be only visible to the compiler. However, it is more than ugly. It infests plain C headers with C++ specific code, which is not desirable. We write that code in C for a reason, usually because it is a library that we’d like to be reused in C programs.

We will have to add these two blocks to any C header that might be used from our C++ program, which may be quite a lot. The C headers may include each other, and while the compiler is OK with encountering several nested levels of extern "C", that’s a lot of noise.

The other argument against this practice is that it may not be our responsibility to maintain those C headers. We might not even be able to change them in the future.

The correct solution

Since #include is a simple text replacement by the preprocessor, we can put the extern "C" declaration in our C++ code, where it belongs:

//main.cpp

extern "C" {
  #include "foo.h"
}

int main() {
  foo(22);
}

This way, everything inside the header, including the indirectly included declarations in other C headers, appear inside the extern "C" declaration.

Caveats

There may be concerns that this looks unfamiliar or even ugly in the C++ code. However, it is still nicer than having the declaration surrounded by #ifdefs in all our C headers. It also may lead to hard-to-find linker errors when we forget to surround a C header include with the extern "C" linkage declaration.

Both issues should be minor concerns though if we encapsulate and restrict the use of the C functionality. If we truly have to use the C headers throughout our code base, there’s the option to write a C++ wrapper header for C headers:

//foo_for_cpp.h
extern "C" {
  #include "foo.h"
}
//main.cpp

#include "foo_for_cpp.h"

int main() {
  foo(22);
}
Previous Post

5 Comments


  1. So let us change our implementations…

    Reply

  2. The last solution is really my preferred one (through I am fine with the first).
    If I have learned one thing over the years, then its this: Give a developer the opportunity to create a bug by forgetting something about the system and he will do it.
    Forcing developers to include extern “C” around all includes they think come from C libraries will guarantee that.

    Reply

  3. This is extremely bad advice.

    Your C headers include C standard library headers, right? Not in C++: those are the C++ standard library headers with the same name. And you’re including them in an extern “C” block.

    Guess what? Now you have UB, or (if you’re lucky) compile errors.

    And it gets worse: some non-stdlib C headers provide additional interfaces when included in C++. You just broke those too. Maybe you’ll get a build break because you put a template in an extern “C” block. Maybe you’ll get a link error. But this will bite you eventually.

    The right thing is the traditional #ifdef __cplusplus / extern “C” { dance, and make sure you only wrap the extern “C” around your own code, not your #includes.

    (For bonus reading, search for Chesterton’s Fence.)

    Reply

    1. I disagree. My C headers usually do not include C standard library headers or similar. A C library that does this in its API headers cannot be used from C++ code without modification. That means the author of those headers has to explicitly prepare them for reuse in C++-land. They then have the choice to put the #ifdef... extern "C" everywhere or turn the default around: If you use headers that behave differently in C++ code, prepare your C API for C++ reuse by protecting that different behavior with #ifdef __cpluplus / extern "C++".

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *