xref: /openbmc/docs/cpp-style-and-conventions.md (revision 785a64ff6f07c743361cc2fc208fd53a5bbc2e41)
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 * Files should use Unix-style newlines (\n).
136 
137 ### Clang Formatting
138 
139 Individual OpenBMC repositories can use [clang-format](https://clang.llvm.org/docs/ClangFormat.html)
140 if desired. The OpenBMC CI infrastructure will automatically verify the
141 code formatting on code check-in if a .clang_format file is found
142 within the root directory of the repository. This allows for automatic
143 validation of code formatting upon check-in.
144 
145 If a custom configuration is desired, such as using different clang formatting
146 for C and C++ files, a format-code.sh script can be created, which can for
147 example use different .clang* files as input depending on the file type.
148 The format-code.sh script will be executed as part of CI if found in the root
149 directory of the repository, and will check that there are no files that were
150 modified after running it (same check as running clang).
151 
152 OpenBMC requires a clang-format of version 6.0 or greater. An example of
153 how to run clang-format against all code in your repo can be found by
154 referencing the [tool](https://github.com/openbmc/openbmc-build-scripts/blob/master/scripts/format-code.sh) used by CI.
155 
156 [Example .clang-format](https://www.github.com/openbmc/docs/blob/master/style/cpp/.clang-format)
157 
158 ### Bracket style
159 
160 * Utilize 'Allman' style brackets.  Brackets are on their own line at the same
161   indentation level as the statement that creates the scope.
162 
163 ```
164 if (condition)
165 {
166     ...
167 }
168 ```
169 
170 ```
171 void foo()
172 {
173     ...
174 }
175 ```
176 
177 * Even one line conditional and loop statements should have brackets.
178 
179 ```
180 /// Wrong.
181 if (condition)
182     do_something;
183 
184 /// Correct
185 if (condition)
186 {
187     do_something;
188 }
189 ```
190 
191 ### Indentation
192 
193 * Content within a namespace should be at the same indentation level as the
194   namespace itself.
195 
196 ```
197 namespace foo
198 {
199 
200 content
201 
202 }
203 ```
204 
205 * Content within a class / struct should be indented.
206 
207 ```
208 class Foo
209 {
210     public:
211         Foo();
212 }
213 ```
214 
215 * Content within a function / conditional / loop should be indented.
216 
217 ```
218 void foo()
219 {
220     while (1)
221     {
222         if (bar())
223         {
224             ...
225         }
226     }
227 }
228 ```
229 
230 * Switch / case statements should be indented.
231 
232 ```
233 switch (foo)
234 {
235     case bar:
236     {
237         bar();
238         break;
239     }
240 
241     case baz:
242     {
243         baz();
244         break;
245     }
246 }
247 ```
248 
249 * Labels should be indented so they appear at 1 level less than the current
250   indentation, rather than flush to the left.  (This is not to say that goto
251   and labels are preferred or should be regularly used, but simply when they
252   are used, this is how they are to be used.)
253 
254 ```
255 void foo()
256 {
257     if (bar)
258     {
259         do
260         {
261             if (baz)
262             {
263                 goto exit;
264             }
265 
266         } while(1);
267 
268     exit:
269         cleanup();
270     }
271 }
272 ```
273 
274 ### Naming Conventions.
275 
276 * We generally abstain from any prefix or suffix on names.
277 * Acronyms should be same-case throughout and follow the requirements as
278   in their appropriate section.
279 
280 ```
281 /// Correct.
282 SomeBMCType someBMCVariable = bmcFunction();
283 
284 /// Wrong: type and variable are mixed-case, function isn't lowerCamelCase.
285 SomeBmcType someBmcVariable = BMCFunction();
286 ```
287 
288 ### Header Ordering
289 
290 Header inclusion order for a header file:
291 ```
292 local headers (e.g. "daemon_sys.hpp")
293 c-libraries
294 cpp-libraries (including openbmc libraries)
295 ```
296 
297 Header inclusion order for a source file:
298 ```
299 source.hpp (if applicable)
300 local headers
301 c-libraries
302 cpp-libraries
303 ```
304 
305 All in alphabetically sorted order.
306 
307 #### Files
308 
309 * C++ headers should end in ".hpp".  C headers should end in ".h".
310 * C++ files should be named with lower_snake_case.
311 
312 #### Types
313 
314 * Prefer 'using' over 'typedef' for type aliases.
315 * Structs, classes, enums, and typed template parameters should all be in
316   UpperCamelCase.
317 * Prefer namespace scoping rather than long names with prefixes.
318 * A single-word type alias within a struct / class may be lowercase to match
319   STL conventions (`using type = T`) while a multi-word type alias should be
320   UpperCamelCase (`using ArrayOfT = std::array<T, N>`).
321 * Exception: A library API may use lower_snake_case to match conventions of the
322   STL or an underlying C library it is abstracting.  Application APIs should
323   all be UpperCamelCase.
324 * Exception: A for-convenience template type alias of a template class may end
325   in `_t` to match the conventions of the STL.
326 
327 ```
328 template <typename T>
329 class Foo
330 {
331     using type = std::decay_t<T>;
332 };
333 
334 template <typename T> using foo_t = Foo<T>::type;
335 ```
336 
337 #### Variables
338 
339 * Variables should all be lowerCamelCase, including class members, with no
340   underscores.
341 
342 #### Functions
343 
344 * Functions should all be lowerCamelCase.
345 * Exception: A library API may use lower_snake-case to match conventions of
346   the STL or an underlying C library it is abstracting.  Application APIs
347   should all be lowerCamelCase.
348 
349 #### Constants
350 
351 * Constants and enum members should be named like variables in lowerCamelCase.
352 
353 #### Namespaces
354 
355 * Namespaces should be lower_snake_case.
356 * Top-level namespace should be named based on the containing repository.
357 * Favor a namespace called 'details' or 'internal' to indicate the equivalent
358   of a "private" namespace in a header file and anonymous namespaces in a C++
359   file.
360 
361 ### Header Guards
362 
363 Prefer '#pragma once' header guard over '#ifndef'-style.
364 
365 ### Additional Whitespace
366 
367 * Follow NL.18: Use C++-style declarator layout.
368 
369 ```
370 foo(T& bar, const S* baz); /// Correct.
371 foo(T &bar, const S *baz); /// Incorrect.
372 ```
373 
374 * Follow NL.15: Use spaces sparingly.
375 
376 * Insert whitespace after a conditional and before parens.
377 
378 ```
379 if (...)
380 while (...)
381 for (...)
382 ```
383 
384 * Insert whitespace around binary operators for readability.
385 
386 ```
387 foo((a-1)/b,c-2); /// Incorrect.
388 foo((a - 1) / b, c - 2); /// Correct.
389 ```
390 
391 * Do not insert whitespace around unary operators.
392 ```
393 a = * b;  /// Incorrect.
394 a = & b;  /// Incorrect.
395 a = b -> c;  /// Incorrect.
396 if (! a)  /// Incorrect.
397 ```
398 
399 * Do not insert whitespace inside parens or between a function call and
400   parameters.
401 
402 ```
403 foo(x, y); /// Correct.
404 foo ( x , y ); /// Incorrect.
405 
406 do (...)
407 {
408 } while(0); /// 'while' here is structured like a function call.
409 ```
410 
411 * Prefer line-breaks after operators to show continuation.
412 ```
413 if (this1 == that1 &&
414     this2 == that2) /// Correct.
415 
416 if (this1 == that1
417     && this2 == that2) /// Incorrect.
418 ```
419 
420 * Long lines should have continuation start at the same level as the parens or
421   all all items inside the parens should be at a 2-level indent.
422 
423 ```
424 reallyLongFunctionCall(foo,
425                        bar,
426                        baz); // Correct.
427 
428 reallyLongFunctionCall(
429         foo,
430         bar,
431         baz); // Also correct.
432 
433 reallyLongFunctionCall(
434         foo, bar, baz); // Similarly correct.
435 
436 reallyLongFunctionCall(foo,
437         bar,
438         baz); // Incorrect.
439 ```
440 
441 ### Misc Guidelines.
442 
443 * Always use `size_t` or `ssize_t` for things that are sizes, counts, etc.
444   You need a strong rationale for using a sized type (ex. `uint8_t`) when a
445   size_t will do.
446 
447 * Use `uint8_t`, `int16_t`, `uint32_t`, `int64_t`, etc. for types where size
448   is important due to hardware interaction.  Do not use them, without good
449   reason, when hardware interaction is not involved; prefer size_t or int
450   instead.
451 
452 
453