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