C++ Specifiers and Attributes Cheatsheet for Embedded Systems

When creating a function for my embedded projects, I am often overwhelmed by the many possible ways to define methods or variables. Maybe this summary helps you as well!

Specifiers Overview

  • inline: For small, often-used functions. Example: High-frequency sensor data processing functions.
  • static: For shared class members or persistent local variables. Example: Counter in a function tracking the number of calls.
  • constexpr: For compile-time constants. Example: Predefined configuration parameters like buffer sizes.
  • volatile: For variables modified outside the program, such as by interrupts. Example: Flags set in an ISR (Interrupt Service Routine).
  • register: Suggests storing a variable in a register. Example: Rarely specified, best left to compiler’s optimization.
  • restrict: For performance optimization in pointer-intensive functions. Example: Signal processing functions using non-overlapping buffers.
  • noexcept: For functions guaranteed not to throw exceptions. Example: Critical error handlers in embedded systems.
  • [[noreturn]]: For functions that never return. Example: System reboot or fatal error handling functions.
  • const: For immutable values and methods that do not modify the object. Example: Getters in classes representing device settings.
  • mutable: For members modified in const member functions. Example: Cache variables in a const method.
  • override: Ensures a member function overrides a virtual function in the base class. Example: Derived class methods that modify base class behavior.
  • final: Prevents further derivation of classes or overriding of methods. Example: Critical base classes in a control system.
  • explicit: Prevents implicit conversion by constructors. Example: Constructors that should not be called with a single argument implicitly.
  • thread_local: For variables that are local to a thread. Example: Error state variable specific to each thread in a multithreaded application.
  • [[nodiscard]]: Ensures function results are not ignored. Example: Functions returning error status that must always be checked.
  • [[maybe_unused]]: Suppresses warnings for unused entities. Example: Parameters that are only used in debug builds.
  • [[deprecated]]: Marks functions or entities as outdated. Example: Old API functions superseded by newer versions.
  • [[likely]] and [[unlikely]]: Provides hints about branch prediction. Example: Conditions in performance-critical loops that are rarely true.

Practical Applications in Embedded Systems

  • Prioritize consistent, predictable behaviors and execution times.
  • Use constexpr and inline to reduce runtime overhead.
  • Leverage volatile for hardware interaction to ensure real-time responsiveness.
  • Avoid dynamic memory allocation where possible; instead, use static storage duration.
  • Disable exceptions and use noexcept to clarify this intent.
  • Minimize use of register, instead relying on the compiler for register allocation.

Best Practices for Specifier Usage

  • Use const and explicit to enforce immutability and prevent implicit behaviors.
  • Utilize thread_local for per-thread data to avoid costly synchronization in multi-threaded environments.
  • Mark critical functions with [[nodiscard]] to ensure that their results are handled correctly.
  • Employ [[deprecated]] to manage evolving firmware and API changes smoothly.

 

This has been summarized with the help of the almighty ChatGPT. For specific examples, take a look at the Roboost firmware, where I try to incorporate these practices.

So Long, and Thanks for All the Fish

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments