As greenpossum has said, the C++ library or anything that uses C++ headers
must(1) be compiled using a C++ compiler. From that you obtain an object file that you can then use link with your C program
(2), if you it’s built in the right way.
Rule #0: be clear where is your C domain and where is your C++ domain. Despite being perceived as such, C++ is not a superset of C. Both languages have a huge common part, but neither completely includes the other. The easiest way is to simply keep them separate.
What you need is:
,---------. .-----------.
| C++ lib | | C wrapper |
| headers +----------. .--------+ headers |
`----+----' | | `-----+-----'
| | | |
,----V----. .------V---V--. .----V-----.
| C++ lib | | C++ wrapper | | Your C |
| sources | | exposing | | sources |
`----+----' | C functions | `----+-----'
| `------+------' |
| | |
~~~V~~~~~~~~~~~~~~~V~~~ ~~~~~~~~V~~~~~~~~
C++ compiler C compiler
~~~~~~~~~~~+~~~~~~~~~~~ ~~~~~~~~+~~~~~~~~
| |
.----V-------------. .------V----------.
| library ,---------. | Your object |
| wrapper | exposed | | (wanting C API) |
| | C API | `------+----------'
| `---------' |
`---------+--------' |
| |
~~~~~~V~~~~~~~~~~~~~~~~~~~~~V~~~~~~
L i n k e r
~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~
|
,-------V------.
| Final binary |
`--------------'
The “C wrapper headers” is the only part that must be compatible with both C and C++. For wrapper headers this is easy to achieve. Typically that contains only:
- struct declarations (not definitions).
- Function declarations with extern C linkage only. Keep them as simple as possible, with no fancy features. Forget about restrict, about declaring pointer arguments with array notation etc.
Then, in the C wrapper source, you define functions. They are the only interface between your C code and the C++ library. In your C code you call those functions to interact with the library. And that’s all.
Now, there are some traps. Since this is about microcontrollers, some of those are not important, but be aware of a few things.
- Standard inut/output of those languages, namely the stdio (C) and iostream (C++), is by default synchronized between both implementations and mixing calls to both should not yield unexpected results. However, that synchronization may be disabled on the C++ side. The C side may also directly interface with the system to perform I/O on standard streams, bypassing both libraries. If both your code and the library does I/O over standard input/output, be aware of the risk: you may be standing on the middle of a minefield.
- Threading may or may not be compatible between both ends. Unless it is clear that both sides use the very same method — e.g. both interface directly POSIX threads — be careful on what you do and when. What seems reasonable is making the wrapper take care about proper synchronization. But it comes with caveats. First, performance may seriously hit due to excessive locking. Second: if both sides use the same threading API you may end with either a deadlock or acquiring a lock may fail unexpectedly.
- Object ownership woes. Any object created on the C++ side must be deleted using C++ code. Any object created on the C side must be freed using C code. While delete/delete/free/realloc may appear interchangeable for simple cases on most platforms, this behaviour is not guaranteed by either of the languages. It is, however, guaranteed that free and realloc will not work for proper objects created on C++ side.
- If both compilers are from the same family (e.g. gcc, clang, …) this will not be the issue. But in general ABI may be different between C and C++. This is unexpected, but if you ever experience that, consider using either the leastN types or exact-width types. For that purpose use <stdint.h> (not <cstdint>) in the wrapper headers. C++ guarantees that <stdio.h> must be present and all definitions in it will be in the global namespace.
____
(1) Unless the library explicitly tells you that it can be used with a C compiler, but in the past 15 years I came across only a few of those. This is very rare, as it requires excessive effort from the authors to make it compatible with both languages.
(2) In fact you may with many other languages. This is how PHP extensions work, how you interface native code with JNI or JNA in Java, how it was done in API imports VB6 etc.