xref: /openbmc/docs/cpp-style-and-conventions.md (revision 146f9098278c0e746e47a011ebf7334196ae2f11)
1 # C++ Coding Style and Conventions
2 
3 ## General Philosophy
4 
5 Being an extensive and complicated language, there are often differences of
6 opinions on "good" and "bad" C++ code.  Bjarne Stroustrup has said "Within C++
7 is a smaller, simpler, safer language struggling to get out."  We are striving
8 to write in this variant of C++ and are therefore following the "C++ Core
9 Guidelines" that Bjarne and Herb Sutter introduced at CppCon 2015.
10 
11 Beyond a set of rules that help codify "good" and "bad" C++, we have general
12 principles that help us align the software we develop with the constraints
13 within the problem domain being solved by OpenBMC.  These are:
14  1. Code should be clear and concise.
15  2. Code should be written with modern practices.
16  3. Code should be performant.
17 
18 ### Code should be clear and concise
19 
20 > Brevity is the soul of wit.
21 
22 It is important that code be optimized for the reviewer and maintainer and not
23 for the writer.  Solutions should avoid tricks that detract from the clarity
24 of reviewing and understanding it.
25 
26 Modern practices allow C++ to be an expressive, but concise, language.  We tend
27 to favor solutions which succinctly represent the problem in as few lines as
28 possible.
29 
30 When there is a conflict between clarity and conciseness, clarity should win
31 out.
32 
33 ### Code should be written with modern practices
34 
35 We strive to keep our code conforming to and utilizing of the latest in C++
36 standards.  Today, that means all C++ code should be compiled using C++20
37 compiler settings.  As the C++23 standard is finalized and compilers support
38 it, we will move to it as well.
39 
40 We also strive to keep the codebase up-to-date with the latest recommended
41 practices by the language designers.  This is reflected by the choice in
42 following the C++ Core Guidelines.
43 
44 [[Not currently implemented]] We finally desire to have computers do our
45 thinking for us wherever possible.  This means having Continuous Integration
46 tests on each repository so that regressions are quickly identified prior to
47 merge. It also means having as much of this document enforced by tools as
48 possible by, for example, clang-format and clang-tidy.
49 
50 For those coming to the project from pre-C++11 environments we strongly
51 recommend the book "Effective Modern C++" as a way to get up to speed on the
52 differences between C++98/03 and C++11/14/17/20.
53 
54 ### Code should be performant.
55 
56 OpenBMC targets embedded processors that typically have 32-64MB of flash and
57 similar processing power of a typical smart-watch available in 2016.  This
58 means that there are times where we must limit library selection and/or coding
59 techniques to compensate for this constraint.  Due to the current technology,
60 performance evaluation is done in order of { code size, cpu utilization, and
61 memory size }.
62 
63 From a macro-optimization perspective, we expect all solutions to have an
64 appropriate algorithmic complexity for the problem at hand.  Therefore, an
65 `O(n^3)` algorithm may be rejected even though it has good clarity when an
66 `O(n*lg(n))` solution exists.
67 
68 ## Global Guidelines and Practices
69 
70 Please follow the guidelines established by the C++ Core Guidelines (CCG).
71 
72 https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
73 
74 [[ Last reviewed revision is 53bc78f ]]
75 
76 Exceptions:
77 
78 ### Guideline Support Library (GSL)
79 
80 We do not currently utilize the Guideline Support Library provided by the CCG.
81 Any recommendation within the CCG of GSL conventions may be ignored at this
82 time.
83 
84 ### Style recommendations
85 
86 The following are not followed:
87  * NL.10 Avoid CamelCase
88  * NL.17 Use K&R-derived layout
89 
90 ## Library and Feature Specifics
91 
92 Additional recommendations within the OpenBMC project on specific language
93 features or libraries.
94 
95 ### Exceptions
96 
97 We do use exceptions as a basis for error handling within OpenBMC.
98 
99 ### Boost
100 
101 Use of boost is allowed, under the following circumstances:
102 1. Use is done as a header-only library.  This allows unused functions and
103    methods to be removed by the compiler at link time, and avoids adding large
104 amounts of overhead and flash usage.
105 2. Implementers should include the lowest level header required to solve the
106    problem at hand.  This allows uses to be found and moved forward when new
107 standards are available, as well as reduce compile times, and decrease the
108 possibility of accidental use.  (ie, #include <boost/container/flat_map.hpp>
109 not #include <boost/container.hpp>)
110 3. The module used should not have an equivalent in the std namespace that meets
111    the same requirements for implementation or clarity:  For example,
112 std::chrono should be preferred over boost::chrono.  std::array over
113 boost::array.
114 4. Use does not conflict with any of the core tenants of this coding standard
115    (clarity, modern practices, or performance).
116 
117 ### iostream
118 
119 The iostream conventions of using 'operator<<' contribute to an increased code
120 size over printf-style operations, due to individual function calls for each
121 appended value.  We therefore do not use iostreams, or iostream-style APIs,
122 for logging.
123 
124 There are cases when using an iostream utility (such as sstream) can result in
125 clearer and similar-sized code.  iostream may be used in those situations.
126 
127 ## Coding Style
128 
129 Indentation, naming practices, etc.
130 
131 ### General
132 
133 * Line length should be limited to 80 characters.
134 * Indentation should be done with 4 space characters.
135 
136 ### Clang Formatting
137 
138 Individual OpenBMC repositories can use [clang-format](https://clang.llvm.org/docs/ClangFormat.html)
139 if desired. The OpenBMC CI infrastructure will automatically verify the
140 code formatting on code check-in if a .clang_format file is found
141 within the root directory of the repository. This allows for automatic
142 validation of code formatting upon check-in.
143 
144 If a custom configuration is desired, such as using different clang formatting
145 for C and C++ files, a format-code.sh script can be created, which can for
146 example use different .clang* files as input depending on the file type.
147 The format-code.sh script will be executed as part of CI if found in the root
148 directory of the repository, and will check that there are no files that were
149 modified after running it (same check as running clang).
150 
151 OpenBMC requires a clang-format of version 6.0 or greater. An example of
152 how to run clang-format against all code in your repo can be found by
153 referencing the [tool](https://github.com/openbmc/openbmc-build-scripts/blob/master/scripts/format-code.sh) used by CI.
154 
155 [Example .clang-format](https://www.github.com/openbmc/docs/blob/master/style/cpp/.clang-format)
156 
157 ### Bracket style
158 
159 * Utilize 'Allman' style brackets.  Brackets are on their own line at the same
160   indentation level as the statement that creates the scope.
161 
162 ```
163 if (condition)
164 {
165     ...
166 }
167 ```
168 
169 ```
170 void foo()
171 {
172     ...
173 }
174 ```
175 
176 * Even one line conditional and loop statements should have brackets.
177 
178 ```
179 /// Wrong.
180 if (condition)
181     do_something;
182 
183 /// Correct
184 if (condition)
185 {
186     do_something;
187 }
188 ```
189 
190 ### Indentation
191 
192 * Content within a namespace should be at the same indentation level as the
193   namespace itself.
194 
195 ```
196 namespace foo
197 {
198 
199 content
200 
201 }
202 ```
203 
204 * Content within a class / struct should be indented.
205 
206 ```
207 class Foo
208 {
209     public:
210         Foo();
211 }
212 ```
213 
214 * Content within a function / conditional / loop should be indented.
215 
216 ```
217 void foo()
218 {
219     while (1)
220     {
221         if (bar())
222         {
223             ...
224         }
225     }
226 }
227 ```
228 
229 * Switch / case statements should be indented.
230 
231 ```
232 switch (foo)
233 {
234     case bar:
235     {
236         bar();
237         break;
238     }
239 
240     case baz:
241     {
242         baz();
243         break;
244     }
245 }
246 ```
247 
248 * Labels should be indented so they appear at 1 level less than the current
249   indentation, rather than flush to the left.  (This is not to say that goto
250   and labels are preferred or should be regularly used, but simply when they
251   are used, this is how they are to be used.)
252 
253 ```
254 void foo()
255 {
256     if (bar)
257     {
258         do
259         {
260             if (baz)
261             {
262                 goto exit;
263             }
264 
265         } while(1);
266 
267     exit:
268         cleanup();
269     }
270 }
271 ```
272 
273 ### Naming Conventions.
274 
275 * We generally abstain from any prefix or suffix on names.
276 * Acronyms should be same-case throughout and follow the requirements as
277   in their appropriate section.
278 
279 ```
280 /// Correct.
281 SomeBMCType someBMCVariable = bmcFunction();
282 
283 /// Wrong: type and variable are mixed-case, function isn't lowerCamelCase.
284 SomeBmcType someBmcVariable = BMCFunction();
285 ```
286 
287 ### Header Ordering
288 
289 Header inclusion order for a header file:
290 ```
291 local headers (e.g. "daemon_sys.hpp")
292 c-libraries
293 cpp-libraries (including openbmc libraries)
294 ```
295 
296 Header inclusion order for a source file:
297 ```
298 source.hpp (if applicable)
299 local headers
300 c-libraries
301 cpp-libraries
302 ```
303 
304 All in alphabetically sorted order.
305 
306 #### Files
307 
308 * C++ headers should end in ".hpp".  C headers should end in ".h".
309 * C++ files should be named with lower_snake_case.
310 
311 #### Types
312 
313 * Prefer 'using' over 'typedef' for type aliases.
314 * Structs, classes, enums, and typed template parameters should all be in
315   UpperCamelCase.
316 * Prefer namespace scoping rather than long names with prefixes.
317 * A single-word type alias within a struct / class may be lowercase to match
318   STL conventions (`using type = T`) while a multi-word type alias should be
319   UpperCamelCase (`using ArrayOfT = std::array<T, N>`).
320 * Exception: A library API may use lower_snake_case to match conventions of the
321   STL or an underlying C library it is abstracting.  Application APIs should
322   all be UpperCamelCase.
323 * Exception: A for-convenience template type alias of a template class may end
324   in `_t` to match the conventions of the STL.
325 
326 ```
327 template <typename T>
328 class Foo
329 {
330     using type = std::decay_t<T>;
331 };
332 
333 template <typename T> using foo_t = Foo<T>::type;
334 ```
335 
336 #### Variables
337 
338 * Variables should all be lowerCamelCase, including class members, with no
339   underscores.
340 
341 #### Functions
342 
343 * Functions should all be lowerCamelCase.
344 * Exception: A library API may use lower_snake-case to match conventions of
345   the STL or an underlying C library it is abstracting.  Application APIs
346   should all be lowerCamelCase.
347 
348 #### Constants
349 
350 * Constants and enum members should be named like variables in lowerCamelCase.
351 
352 #### Namespaces
353 
354 * Namespaces should be lower_snake_case.
355 * Top-level namespace should be named based on the containing repository.
356 * Favor a namespace called 'details' or 'internal' to indicate the equivalent
357   of a "private" namespace in a header file and anonymous namespaces in a C++
358   file.
359 
360 ### Header Guards
361 
362 Prefer '#pragma once' header guard over '#ifndef'-style.
363 
364 ### Additional Whitespace
365 
366 * Follow NL.18: Use C++-style declarator layout.
367 
368 ```
369 foo(T& bar, const S* baz); /// Correct.
370 foo(T &bar, const S *baz); /// Incorrect.
371 ```
372 
373 * Follow NL.15: Use spaces sparingly.
374 
375 * Insert whitespace after a conditional and before parens.
376 
377 ```
378 if (...)
379 while (...)
380 for (...)
381 ```
382 
383 * Insert whitespace around binary operators for readability.
384 
385 ```
386 foo((a-1)/b,c-2); /// Incorrect.
387 foo((a - 1) / b, c - 2); /// Correct.
388 ```
389 
390 * Do not insert whitespace around unary operators.
391 ```
392 a = * b;  /// Incorrect.
393 a = & b;  /// Incorrect.
394 a = b -> c;  /// Incorrect.
395 if (! a)  /// Incorrect.
396 ```
397 
398 * Do not insert whitespace inside parens or between a function call and
399   parameters.
400 
401 ```
402 foo(x, y); /// Correct.
403 foo ( x , y ); /// Incorrect.
404 
405 do (...)
406 {
407 } while(0); /// 'while' here is structured like a function call.
408 ```
409 
410 * Prefer line-breaks after operators to show continuation.
411 ```
412 if (this1 == that1 &&
413     this2 == that2) /// Correct.
414 
415 if (this1 == that1
416     && this2 == that2) /// Incorrect.
417 ```
418 
419 * Long lines should have continuation start at the same level as the parens or
420   all all items inside the parens should be at a 2-level indent.
421 
422 ```
423 reallyLongFunctionCall(foo,
424                        bar,
425                        baz); // Correct.
426 
427 reallyLongFunctionCall(
428         foo,
429         bar,
430         baz); // Also correct.
431 
432 reallyLongFunctionCall(
433         foo, bar, baz); // Similarly correct.
434 
435 reallyLongFunctionCall(foo,
436         bar,
437         baz); // Incorrect.
438 ```
439 
440 ### Misc Guidelines.
441 
442 * Always use `size_t` or `ssize_t` for things that are sizes, counts, etc.
443   You need a strong rationale for using a sized type (ex. `uint8_t`) when a
444   size_t will do.
445 
446 * Use `uint8_t`, `int16_t`, `uint32_t`, `int64_t`, etc. for types where size
447   is important due to hardware interaction.  Do not use them, without good
448   reason, when hardware interaction is not involved; prefer size_t or int
449   instead.
450 
451 
452