Integrating C++ Drivers into C-Based Firmware

Story
Imagine this scenario: you are working on a firmware project written entirely in C. Midway through development, your team finds a new sensor that is 10× cheaper than the current one. Naturally, management wants it integrated into the next hardware version.
Here’s the catch — the official driver for this sensor is only available in C++.
Now, instead of rewriting the entire driver in C (which is time-consuming and error-prone), you can integrate the C++ driver into your C-based firmware with minimal changes.
There are a couple of ways to achieve this quickly.
1. Expose C++ classes as a c API ( Recommended )
Since C does not understand C++ classes, we can build a C wrapper around the C++ code.
This approach works by providing extern “C” functions that operate on opaque pointers to C++ objects. Opaque pointers allow us to manage class instances in C without exposing their internal structure.
Let’s look at the project structure:
- Driver.hpp
- Driver.cpp
- Driver_C_Wrapper.h
- Driver_C_Wrapper.cpp
- main.c
- CMakeLists.txt
This wrapper creates new C++ class instances, exposes them as opaque pointers, and provides simple C functions that forward calls to the underlying C++ methods.
below you can find the actual code related to all this files.
#pragma once
#include <string>
class Driver {
public:
Driver(const std::string &name);
~Driver();
void sayHello();
private:
std::string name_;
};
#include "Driver.hpp"
#include <iostream>
Driver::Driver(const std::string &name) : name_(name) {}
Driver::~Driver() {}
void Driver::sayHello() {
std::cout << "Hello, " << name_ << "!" << std::endl;
}
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Use opaque pointer for the class
typedef void* DriverHandle;
DriverHandle Driver_create(const char* name);
void Driver_destroy(DriverHandle obj);
void Driver_sayHello(DriverHandle obj);
#ifdef __cplusplus
}
#endif
#include "Driver.hpp"
#include "Driver_C_Wrapper.h"
extern "C" {
DriverHandle Driver_create(const char* name) {
return new Driver(name);
}
void Driver_destroy(DriverHandle obj) {
delete static_cast<Driver*>(obj);
}
void Driver_sayHello(DriverHandle obj) {
static_cast<Driver*>(obj)->sayHello();
}
}
#include "Driver_C_Wrapper.h"
int main() {
DriverHandle obj = Driver_create("Saurabh");
Driver_sayHello(obj);
Driver_destroy(obj);
return 0;
}
cmake_minimum_required(VERSION 3.10)
project(test C CXX)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
# Create library from C++ code
add_library(Driver STATIC
Driver.cpp
Driver_C_Wrapper.cpp
)
# Main C program
add_executable(main main.c)
# Link C++ library into C program
target_link_libraries(main PRIVATE Driver)
2. Compile C code with a C++ Compiler
If your project is mostly C but you can switch the compiler (for example, .c files compiled as C++), then you don’t need wrappers. Just rename .c files to .cpp (or use g++/clang++ with -x c++) and include your class directly.
For example, you can rename .c files to .cpp (or tell the compiler to treat them as C++ using flags like -x c++) and then include the C++ driver headers directly.
Warning
But this can break compatibility if the C code uses constructs not valid in C++ (implicit void* conversions, different keywords, etc.).