Modern C++ (C++17/20) Best Practices for Embedded Systems

Modern C++ (C++17/20) Best Practices for Embedded Systems

Bringing Embedded Systems into the 21st Century

Embedded systems have traditionally been written in pure C. However, modern C++ (specifically C++17 and C++20) offers incredible features that improve type safety, readability, and abstraction without sacrificing runtime performance. In fact, many modern C++ features resolve entirely at compile time (Zero-Cost Abstractions).

1. constexpr for Compile-Time Evaluation

Why calculate a lookup table at runtime when the compiler can do it for you? constexpr guarantees that expressions are evaluated at compile time, saving precious MCU cycles.

constexpr float pi = 3.1415926f;

// Evaluated entirely at compile time
constexpr int degrees_to_adc(float deg) {
    return static_cast((deg / 360.0f) * 4096);
}

2. std::array and std::span

Stop using raw C-arrays that decay to pointers and lose their size information. std::array provides the same performance as a raw array but with boundary checks and STL iterator support.

std::span (C++20) provides a safe, non-owning view over a contiguous block of memory, perfect for passing buffers to DMA functions without pointer-arithmetic bugs.

3. Auto and Structured Bindings (C++17)

Make your code cleaner when returning multiple values from sensor reading functions.

struct SensorData { float temp; float humidity; };

SensorData read_bme280() { return {24.5f, 50.2f}; }

// C++17 Structured Binding
auto [temperature, humidity] = read_bme280();

4. Don't Fear Templates, but Fear RTTI and Exceptions

Templates are resolved at compile time and cost nothing at runtime (though they can increase code size). However, you should generally disable Exceptions (-fno-exceptions) and Run-Time Type Information (-fno-rtti) in embedded environments, as they introduce unpredictable overhead and bloat the binary.

Software
Back to Blog