Embedded C++: What’s the value?

It’s not uncommon to hear this statement: "C++ just doesn’t work well for embedded systems." Often, there will be vague, sweeping reasons such as:

  • C code is going to be smaller than C++!
  • C++ is totally going to blow up your stack / heap allocations!
  • C++ is a powerful tool – and you have to be wary of shooting yourself in the foot! Those features can cause code bloat!
  • C++ won’t work well if you are using an 8-bit microcontroller!
  • Exceptions and RTTI make the output so much bigger and make things take longer!

I was also in the C-only camp – I thought that C++ would penalize me in size and speed. I was forced to confront my ignorance as we converted Pearl’s RearVision firmware from C to C++. I saw the power, convenience, and speed that C++ offered firsthand and the outcome assuaged my fears: the firmware binary is 1.6MB, even with RTTI and exceptions enabled in certain objects.

Here are some things that didn’t sink in until I started working with C++:

  1. If you are space/memory constrained, you can ruin yourself in the same way with C and C++. C is not inherently safe.
  2. C and C++ are different languages. You must understand your tools to apply them correctly.
  3. C++ was designed to be competitive in performance with C.
  4. There are many valuable features of C++ that do not cost you anything.
  5. You can use or selectively disable features you don’t need.
  6. RAII and SBRM are extremely helpful patterns that are easier to utilize in C++.
  7. Many teams recreate C++-like object models and vtables in C – the compiler can do it more efficiently than you can.

Your next question is likely "ok, fine – what’s so great about C++ then?" My short answer: C++ helps me write neater, more explicit, and less error-prone code than C does.

I’ve included a summary of C++ features that I utilize on a daily basis, grouped into two sets: those that help me write higher quality code and those that work well for embedded systems. Over the next few weeks I will be exploring these C++ features, including examples, tricks, and pitfalls to avoid.

In the meantime, if you’re looking for more information on C++, check out the Field Atlas Entry.

Code Quality

  • Stronger type safety
    • More explicit casting rules
    • nullptr
    • Virtual functions and templated types allow me to eliminate void * usage
    • Passing arguments by reference can replace many error-prone uses of pass-by-pointer in C.
      • const can allow you to pass-by-reference (eliminating a copy) but still indicate no modifications are allowed.
  • Function overloading
    • Enables neater code – interfaces that provide the same functionality but use different types can be grouped together, allowing for easier logical understanding of the functionality
  • auto
  • Smart pointers
    • std::unique_ptr and std::shared_ptr can eliminate many memory bugs that result from missing a free() call
  • Better utilization of scope-based resource management
    • Take a lock, and ensure that a lock is released at all scope exit points automatically (never forget to unlock() before return!)
    • Ensure memory is freed properly at all scope exit points
  • Templates
    • reduce repetition and generalize APIs, especially when differentiated primarily by type
    • Stronger type-safety over C-style macros
    • Enables more compile-time checking / computation

Embedded-Friendly Features

  • constexpr
    • Indicate to the compiler that you intend for a constant/function to act as a compile-time constant
    • Enables compiler optimization
  • std::align, std::aligned_storage, std::alignment_of
    • std library interfaces for aligning memory
  • Ability to customize allocators/deleters
    • Works with smart pointers too – safely pass around memory without worrying about whether to call free() or aligned_free()
  • Virtual functions/classes
    • Cost is minimal: one vtable per class, one pointer per object, one dereference per function call. Not slow!
    • Uses: * Defining interfaces
      • Abstract objects using the same interface (e.g. operate on an array of SPI devices)
  • Templates
    • Eliminate dead code – only use versions of a function that are required at compilation time (useful for safety-critical systems). Extra code is not ever generated.
  • std::mutex
    • Provides a useful wrapper for your RTOS mutex
    • Implementing std::mutex allows you to utilize SBRM and RAII type features such as std::lock_guard.
  • std::array
    • Statically allocate memory, defined at compile time

Further Reading

Using C++ Without the Heap

Want to use C++, but worried about how much it relies on dynamic memory allocations? Our course provides a hands-on approach for learning a diverse set of patterns, tools, and techniques for writing C++ code that never uses the heap.

Learn More on the Course Page

Migrating from C to C++ Articles

2 Replies to “Embedded C++: What’s the value?”

  1. There are two tails I like to relate when I hear people say "C++ doesn’t work for embedded".

    1:
    For 12 years I coded for bare board embedded systems, first in assembler then, for the majority of the time, C.
    Over the years I created a variety of techniques and code libraries to make the process easier and eliminate reinventing the wheel.
    I was very pleased with what I had created!
    Moving jobs, I was eventually tasked to look at how new applications could be created and I decided to look at C++.
    I’d heard all of the FUD about C++ and was a fully signed up member of the "Embedded doesn’t need the complexities of C++" club.
    As I started to read up about C++ I was hit by the sudden realisation of how much of C++ I had been re-inventing in C!
    Objects, multiple instances, virtual functions, polymorphism, the lot!
    And not only that, my C implementations were more verbose, error prone, and probably noware as efficient as the C++ compiler’s generated versions.
    I’ve never perposefully written C for embedded since, unless there was a REALLY good reason not to use C++ (and there are very few good reasons).

    2:
    Before I really got to know the power of C++ fully, I had created an image library for our product line.
    It used old school C pointers and navigated through the image using pointer arithmetic (+-1 for horizontal, +-line length for vertical, combination for diagional).
    Eventually I rewrote the library and experimented with implementing image ‘iterators’.
    The iterator class definitions had a heirarchy about four levels deep, so I was expecting that there could be a performance hit.
    To my surprise (and pleasure) compiling the code, with optimisation enabled, showed the C++ code to have EXACTLY the same execution times of my old, clunky, pointer based library!

    1. Hi John,

      Thanks for sharing your stories. Your point #1 is the exact same realization we had on a project team when we switched from C to C++. I can’t tell you how many different implementations of objects and virtual tables in C I’ve seen over the years. In general, I guess it ties into our overall reluctance as programmers to buy a module instead of build it in-house. 🙂

Share Your Thoughts

This site uses Akismet to reduce spam. Learn how your comment data is processed.