> ## Content Index
> Fetch the complete content index at: https://www.smallstepsystems.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Is the Header Included from extern "C" Compiled as C or C++?
- URL: https://www.smallstepsystems.com/is-the-header-included-from-extern-c-compiled-as-c-or-c/
- Published: 2021-09-09T16:27:48.000Z
- Updated: 2021-09-09T16:27:48.000Z
- Author: Burkhard Stubert

It depends! If a C++ source file includes the header inside an `extern "C"` section, the header is compiled as C++. If a C source file includes the header, the header is compiled as C. Hence, the header file should be both valid C and valid C++.

## Including C Headers from C++ Sources

[GoogleTest](http://google.github.io/googletest/?ref=smallstepsystems.com), [CppUTest](https://cpputest.github.io/?ref=smallstepsystems.com) and [QtTest](https://doc.qt.io/qt-5/qtest-overview.html?ref=smallstepsystems.com) are widely used unit test frameworks written in C++. The first two come with a mocking framework. The last one provides table-driven tests and some special Qt features (e.g., QSignalSpy). Especially if we know them from testing C++ code, we want to use them for testing C code, too. And we can do this - with a little bit of care.

We define the test class `TestRingbuffer` in GoogleTest as follows, where *ringbuffer.h* and *ringbuffer.c* contain the code to be tested.

```
// File: test_ringbuffer.cpp

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

class TestRingbuffer : public testing::Test
{
};

TEST_F(TestRingbuffer, capacity)
{
    ringbuffer_t rb = ringbuffer_create(5);
    EXPECT_EQ(ringbuffer_capacity(rb), 5);
}
```

`ring buffer_t` is an [opaque pointer to an abstract data type](https://interrupt.memfault.com/blog/opaque-pointers?ref=smallstepsystems.com) (ADT) mimicking an object in C. The functions of the ADT are declared in *ringbuffer.h* and defined in *ringbuffer.c*. Both files are written in C as is the rest of our software (sorry, firmware). The important parts of *ringbuffer.h* look as follows:

```
// File: ringbuffer.h

typedef struct ringbuffer_instance_t* ringbuffer_t;

ringbuffer_t ringbuffer_create(uint32_t capacity);
uint32_t ringbuffer_capacity(ringbuffer_t this);
void ringbuffer_destroy(ringbuffer_t this);
```

The above type and functions are implemented in *ringbuffer.c*, which includes *ringbuffer.h*. Their implementation is not relevant for this post.

```
// File: ringbuffer.c

#include "ringbuffer.h"
```

We add the source files *test\_ringbuffer.cpp* and *ringbuffer.c* to the *CMakeLists.txt* of the `test_ringbuffer` project.

```
// File: CMakeLists.txt

project(test_ringbuffer LANGUAGES CXX C)
enable_testing()
add_executable(${PROJECT_NAME} test_ringbuffer.cpp ringbuffer.c <more source files>)
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
```

When we build the project `test_ringbuffer`,

- *test\_ringbuffer.cpp* and all other files ending with *.cpp* are compiled with the C++ compiler, and
- **ringbuffer.c* and all other files ending with *.c* are compiled with the C compiler.

When the C++ compiler sees `#include "ring buffer.h"` inside the `extern "C"` section of *test\_ringbuffer.cpp*, it regards the header *ringbuffer.h* as C++. Hence, the code in *ringbuffer.h* and its included headers must bevalid C++ code. However, the symbols in these header files have C linkage due to `extern "C"`, hence the C++ compiler doesn't mangle the symbol names.

As `this` is a C++ keyword, the code in *ringbuffer.h* is not valid C++. We fix this by replacing `this` with `instance`.

```
// File: ringbuffer.h (now valid C and C++)

typedef struct ringbuffer_instance_t* ringbuffer_t;

ringbuffer_t ringbuffer_create();
uint32_t ringbuffer_capacity(ringbuffer_t instance);
void ringbuffer_destroy(ringbuffer_t instance);
```

When the C compiler sees `#include "ringbuffer.h"` at the beginning of *ringbuffer.c*, it regards the header *ringbuffer.h* as C. Hence, the code in *ringbuffer.h* and its included headers must bevalid C code, which is true for *ringbuffer.h*. All symbols in these files have C linkage.

**In summary**: The code in *ringbuffer.h* must be both valid C and valid C++. The `extern "C"` declaration has no influence whether the headers included in the extern-C section are compiled as C or C++. It only switches off C++ name mangling.

## List of Incompatibilities between C and C++

You find the example code for the incompatibilities [in the GitHub repository add-training-add-ons](https://github.com/bstubert/tdd-training-add-ons?ref=smallstepsystems.com). The top-level CMake file is *examples/CMakeLists.txt*. Uncomment the line `#add_subdirectory(incompatible-c-cpp)` and build the project. The compiler will complain about the incompatibilities. The header *examples/incompatible-c-cpp/src/ringbuffer.h* contains a description of the error messages and how to fix them.

### Do Not Use C++ Keywords in C Headers

As seen above, we must not use [C++ keywords](https://en.cppreference.com/w/cpp/keyword?ref=smallstepsystems.com) like `this` in C headers. We replace them by variable names that are valid in both C and C++. For example, we replace `this` by `instance`. The C++ compiler emits an error messages like this:

```
ringbuffer.h:19:43: error: invalid parameter name: 'this' is a keyword
```

### Do Not Use `struct void*` for Opaque Pointers to C Objects

We usually declare [the opaque pointer type to a C object](https://interrupt.memfault.com/blog/opaque-pointers?ref=smallstepsystems.com) as follows:

```
// File: ringbuffer.h (valid C and C++)

typedef struct ringbuffer_instance_t* ringbuffer_t;
```

The structure `ringbuffer_instance_t` is declared in the corresponding source file ringbuffer.c so that clients don't see its declaration.

```
// File: ringbuffer.c

struct ringbuffer_instance_t 
{
    uint32_t capacity;
};

```

Some C developers want to make extra sure that `ringbuffer_t` is opaque and declare it as follows:

```
// File: ringbuffer.h (valid C, but invalid C++)

typedef void ringbuffer_instance_t;
typedef struct ringbuffer_instance_t* ringbuffer_t;
```

The C compiler is happy with the resolved type definition: `typedef struct void* ringbuffer_t`. The C++ compiler complains with the error message:

```
ringbuffer.h:12:16: error: typedef 'ringbuffer_instance_t' cannot be referenced with a struct specifier
```

The fix is to remove the first `typedef`.

## Interesting Resources

- Arne Mertz: [Calling C Code from C++ With ‘extern “C”‘](https://arne-mertz.de/2018/10/calling-cpp-code-from-c-with-extern-c/?ref=smallstepsystems.com). Arne explains why we should use `extern "C"` only in C++ files and not in C files.
- Nick Miller: [Practical Design Patterns - Opaque Pointers and Objects in C](https://interrupt.memfault.com/blog/opaque-pointers?ref=smallstepsystems.com). The opaque pointer pattern enables us to use objects (abstract data types) in C. The pattern can also be used if we don't want to allocate memory dynamically. Nick points that we should use the pattern only when we need multiple instances.