Wyatt Hepler | f9fb90f | 2020-09-30 18:59:33 -0700 | [diff] [blame] | 1 | .. _docs-embedded-cpp: |
Keir Mierle | 2c1e56b | 2019-11-15 16:32:11 -0800 | [diff] [blame] | 2 | |
| 3 | ================== |
| 4 | Embedded C++ Guide |
| 5 | ================== |
| 6 | |
| 7 | This page contains recommendations for using C++ for embedded software. For |
| 8 | Pigweed code, these should be considered as requirements. For external |
| 9 | projects, these recommendations can serve as a resource for efficiently using |
| 10 | C++ in embedded projects. |
| 11 | |
| 12 | These recommendations are subject to change as the C++ standard and compilers |
| 13 | evolve, and as the authors continue to gain more knowledge and experience in |
| 14 | this area. If you disagree with recommendations, please discuss them with the |
| 15 | Pigweed team, as we're always looking to improve the guide or correct any |
| 16 | inaccuracies. |
| 17 | |
| 18 | Constexpr functions |
| 19 | =================== |
| 20 | Constexpr functions are functions that may be called from a constant |
| 21 | expression, such as a template parameter, constexpr variable initialization, or |
| 22 | ``static_assert`` statement. Labeling a function ``constexpr`` does not |
| 23 | guarantee that it is executed at compile time; if called from regular code, it |
| 24 | will be compiled as a regular function and executed at run time. |
| 25 | |
| 26 | Constexpr functions are implicitly inline, which means they are suitable to be |
| 27 | defined in header files. Like any function in a header, the compiler is more |
| 28 | likely to inline it than other functions. Marking non-trivial functions as |
| 29 | ``constexpr`` could increase code size, so check the compilation results if this |
| 30 | is a concern. |
| 31 | |
| 32 | Simple constructors should be marked ``constexpr`` whenever possible. GCC |
| 33 | produces smaller code in some situations when the ``constexpr`` specifier is |
| 34 | present. Do not avoid important initialization in order to make the class |
| 35 | constexpr-constructible unless it actually needs to be used in a constant |
| 36 | expression. |
| 37 | |
| 38 | Constexpr variables |
| 39 | =================== |
| 40 | Constants should be marked ``constexpr`` whenever possible. Constexpr variables |
| 41 | can be used in any constant expression, such as a non-type template argument, |
| 42 | ``static_assert`` statement, or another constexpr variable initialization. |
| 43 | Constexpr variables can be initialized at compile time with values calculated by |
| 44 | constexpr functions. |
| 45 | |
| 46 | ``constexpr`` implies ``const`` for variables, so there is no need to include |
| 47 | the ``const`` qualifier when declaring a constexpr variable. |
| 48 | |
| 49 | Unlike constexpr functions, constexpr variables are **not** implicitly inline. |
| 50 | Constexpr variables in headers must be declared with the ``inline`` specifier. |
| 51 | |
| 52 | .. code-block:: cpp |
| 53 | |
| 54 | namespace pw { |
| 55 | |
| 56 | inline constexpr const char* kStringConstant = "O_o"; |
| 57 | |
| 58 | inline constexpr float kFloatConstant1 = CalculateFloatConstant(1); |
| 59 | inline constexpr float kFloatConstant2 = CalculateFloatConstant(2); |
| 60 | |
| 61 | } // namespace pw |
| 62 | |
| 63 | Function templates |
| 64 | ================== |
| 65 | Function templates facilitate writing code that works with different types. For |
| 66 | example, the following clamps a value within a minimum and maximum: |
| 67 | |
| 68 | .. code-block:: cpp |
| 69 | |
| 70 | template <typename T> |
| 71 | T Clamp(T min, T max, T value) { |
| 72 | if (value < min) { |
| 73 | return min; |
| 74 | } |
| 75 | if (value > max) { |
Ali Zhang | f22f1f1 | 2021-04-02 18:59:28 -0700 | [diff] [blame] | 76 | return max; |
Keir Mierle | 2c1e56b | 2019-11-15 16:32:11 -0800 | [diff] [blame] | 77 | } |
| 78 | return value; |
| 79 | } |
| 80 | |
| 81 | The above code works seamlessly with values of any type -- float, int, or even a |
| 82 | custom type that supports the < and > operators. |
| 83 | |
| 84 | The compiler implements templates by generating a separate version of the |
| 85 | function for each set of types it is instantiated with. This can increase code |
| 86 | size significantly. |
| 87 | |
| 88 | .. tip:: |
| 89 | |
| 90 | Be careful when instantiating non-trivial template functions with multiple |
| 91 | types. |
| 92 | |
| 93 | Virtual functions |
| 94 | ================= |
| 95 | Virtual functions provide for runtime polymorphism. Unless runtime polymorphism |
| 96 | is required, virtual functions should be avoided. Virtual functions require a |
| 97 | virtual table, which increases RAM usage and requires extra instructions at each |
| 98 | call site. Virtual functions can also inhibit compiler optimizations, since the |
| 99 | compiler may not be able to tell which functions will actually be invoked. This |
| 100 | can prevent linker garbage collection, resulting in unused functions being |
| 101 | linked into a binary. |
| 102 | |
| 103 | When runtime polymorphism is required, virtual functions should be considered. |
| 104 | C alternatives, such as a struct of function pointers, could be used instead, |
| 105 | but these approaches may offer no performance advantage while sacrificing |
| 106 | flexibility and ease of use. |
| 107 | |
| 108 | .. tip:: |
| 109 | |
| 110 | Only use virtual functions when runtime polymorphism is needed. |
Wyatt Hepler | 63afc00 | 2021-02-08 13:20:26 -0800 | [diff] [blame] | 111 | |
| 112 | Compiler warnings |
| 113 | ================= |
| 114 | Bugs in embedded systems can be difficult to track down. Compiler warnings are |
| 115 | one tool to help identify and fix bugs early in development. |
| 116 | |
| 117 | Pigweed compiles with a strict set of warnings. The warnings include the |
| 118 | following: |
| 119 | |
| 120 | * ``-Wall`` and ``-Wextra`` -- Standard sets of compilation warnings, which |
| 121 | are recommended for all projects. |
| 122 | * ``-Wimplicit-fallthrough`` -- Requires explicit ``[[fallthrough]]`` |
| 123 | annotations for fallthrough between switch cases. Prevents unintentional |
| 124 | fallthroughs if a ``break`` or ``return`` is forgotten. |
| 125 | * ``-Wundef`` -- Requires macros to be defined before using them. This |
| 126 | disables the standard, problematic behavior that replaces undefined (or |
| 127 | misspelled) macros with ``0``. |
| 128 | |
| 129 | Unused variable and function warnings |
| 130 | ------------------------------------- |
| 131 | The ``-Wall`` and ``-Wextra`` flags enable warnings about unused variables or |
| 132 | functions. Usually, the best way to address these warnings is to remove the |
| 133 | unused items. In some circumstances, these cannot be removed, so the warning |
| 134 | must be silenced. This is done in one of the following ways: |
| 135 | |
| 136 | 1. When possible, delete unused variables, functions, or class definitions. |
| 137 | 2. If an unused entity must remain in the code, avoid giving it a name. A |
| 138 | common situation that triggers unused parameter warnings is implementing a |
| 139 | virtual function or callback. In C++, function parameters may be unnamed. |
| 140 | If desired, the variable name can remain in the code as a comment. |
| 141 | |
| 142 | .. code-block:: cpp |
| 143 | |
| 144 | class BaseCalculator { |
| 145 | public: |
| 146 | virtual int DoMath(int number_1, int number_2, int number_3) = 0; |
| 147 | }; |
| 148 | |
| 149 | class Calculator : public BaseCalculator { |
| 150 | int DoMath(int number_1, int /* number_2 */, int) override { |
| 151 | return number_1 * 100; |
| 152 | } |
| 153 | }; |
| 154 | |
| 155 | 3. In C++, annotate unused entities with `[[maybe_unused]] |
| 156 | <https://en.cppreference.com/w/cpp/language/attributes/maybe_unused>`_ to |
| 157 | silence warnings. |
| 158 | |
| 159 | .. code-block:: cpp |
| 160 | |
| 161 | // This variable is unused in certain circumstances. |
| 162 | [[maybe_unused]] int expected_size = size * 4; |
| 163 | #if OPTION_1 |
| 164 | DoThing1(expected_size); |
| 165 | #elif OPTION_2 |
| 166 | DoThing2(expected_size); |
| 167 | #endif |
| 168 | |
| 169 | 4. As a final option, cast unused variables to ``void`` to silence these |
| 170 | warnings. Use ``static_cast<void>(unused_var)`` in C++ or |
| 171 | ``(void)unused_var`` in C. |
| 172 | |
| 173 | In C, silencing warnings on unused functions may require compiler-specific |
| 174 | attributes (``__attribute__((unused))``). Avoid this by removing the |
| 175 | functions or compiling with C++ and using ``[[maybe_unused]]``. |