## 2. C++ Coding Guidelines ### I. Introduction These are the internal C++ guidelines for the [machines-in-motion](https://wp.nyu.edu/machinesinmotion/) group. The same guidelines are used in the [Open Dynamic Robot Initiative](https://open-dynamic-robot-initiative.github.io/) The following rules present basic guidelines for our C++ code. The goal is to have code that is formatted in a consistent and easily readable way while at the same time not being overly complicated by specifying every detail. For such guidelines to be practical, newcomers should be able to read them within a few minutes and be able to memorize them. So these rules intentionally do not cover every detail but rather aim at specifying only the big, important things. If this is too simple for you and you want more rules, you are encouraged to read the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) on which these rules are based. Note, however, that we have a few small deviations from the Google style. These guidelines may evolve in time so it is first good practice to check them upon creation of a new package or code refactoring. ### II. Folder Structure and File Naming - Header files should be in a folder: `include//*`, e.g. `#include "package_template_cpp/gains_configuration.hpp"` - File extension for header files: `.hpp` - Source files should be in a folder named `src/`. The file should have the same name as the header with extension `.cpp`. - When using templates: If you want to separate declaration and definition, put the declaration to a `.hpp` header file as usual and the definition to a file with extension `.hxx` in the same directory (which is included at the bottom of the `.hpp` file). - Preferably each class should be in a separate file with name *class_name_in_lower_case*. However, this is not a strict rule, if several smaller classes are logically closely related, they may go to the same file. - Executable scripts should be placed in the `scripts/` folder. And should have a CMake **install rule** that makes them executable upon installation. - The C++/pybind11 code for wrapping C++ code to Python must be placed in `srcpy/` ### III. Naming Give as descriptive a name as possible, within reason. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word. Abbreviations that would be familiar to someone outside your project with relevant domain knowledge are OK. As a rule of thumb, an abbreviation is probably OK if it's listed in Wikipedia. Formatting of names should be as follows: - types (classes, structs, ...): *FirstUpperCamelCase* - functions, methods: *lower_case_with_underscores* - variables: *lower_case_with_underscores* - class members: *like_variables_but_with_trailing_underscore_* - constants: *UPPER_CASE_WITH_UNDERSCORES* - global variables: Should generally be avoided but if needed, prefix them with g_, i.e. *g_variable_name*. ### IV. Add Units to Variable Names Variables that hold values of a specific unit should have that unit appended to the name. For example if a variable holds the velocity of a motor in *krpm* it should be called `velocity_krpm` instead of `just velocity`. Some more examples: - duration_us (use "u" instead of "ยต") - voltage_mV - acceleration_mps2 (\f$ \frac{m}{s^2} \f$) ### V. C/C++ Formatting #### V.1. Line Length Limit the length of lines to 80 characters. This may seem hard to follow sometimes but makes it much easier to view two or even three files next to each other (important during code review or when resolving merge conflicts). #### V.2. Indentation - Use spaces instead of tabs. - 4 spaces per "tab". #### V.3. Position of braces - Opening brace always goes to the next line. - **Always** add braces for single-line if/loop/etc. Example: ~~~{.c} namespace bar { class Foo { void my_function(const Foo &foo, int *output_arg) { if (condition) { ... } else if (other_condition) { ... } else { ... } while (condition) { ... } } } } // namespace ~~~ #### V.3. Spaces Add single spaces between if/for/etc., the condition and the brace. add spaces around most binary operators. Exception: No spaces around `::`, `.` and `->`. Also no spaces for unary operators (`i++`, `&x`, `*x`, ...). Example: ~~~{.c} int x = 42; for (int i = 0; i < x; i++) { int y = i * x; if (y == foo.bar->baz) { ... } } ~~~ #### V.4. Formatting of switch blocks - See the following example for proper indentation of switch blocks. - Always add a default case, even when it is technically not needed. - If the default block is empty, it shall contain a comment to indicate that this is intentional. - The default case shall either be the first or the last case, preferably the last. - Non-empty cases shall be terminated by an unconditional break. ~~~{.c} switch (x) { case 1: // ... break; case 2: // ... break; case 3: // multiple cases for one block are okay case 4: // ... break; case 5: // BAD. A non-empty case without break should not be something(); // used case 6: more(); break; default: // no action needed break; } ~~~ #### V.5. Clang-Format Configuration To automatically format your code according to this guidelines, you can use clang-format with the configuration [here](https://github.com/machines-in-motion/mpi_cmake_modules/blob/master/python/mpi_cmake_modules/_clang-format). ### VI. C/C++ Coding Guidelines #### VI.1. Pass objects by const reference In general, non-primitive data types should be passed to functions by const reference instead of by value. ~~~{.c} void foobar(const Foo &foo); // good void foobar(Foo foo); // results in copy of `foo`. Only do this if const // reference is not possible for some reason. ~~~ #### VI.2. `#pragma once` vs Include Guards Prefer `#pragma once` over include guards. `#pragma once` is not part of the official standard but is widely supported by compilers and much simpler to maintain. Note that there are some border cases where `#pragma once` is causing issues (e.g. on Windows or when having a weird build setup with symlinks or copies of files). In such cases use traditional include guards. Make sure they have unique names by composing them from the package name and the path/name of the file (e.g. MY_PACKAGE_PATH_TO_FILE_FILENAME_H). Please **do not** add underscore as prefix nor suffix because this is reserved for the compiler pre-proccesor variables. #### VI.3. Keep scopes small Avoid adding anything to the global namespace if possible. This means - Use a namespace when defining extern symbols. - Use an anonymous namespace or static for symbols that are only used internally. Generally define symbols in the smallest possible scope, i.e. if a variable is only used inside one loop, define it inside this loop (however, do not consider this to be a very strict rule, deviate from it where it seems reasonable). #### VI.4. Use types with explicit sizes The header *stdint* defines primitive types with explicit sizes: *int32_t*, *uint32_t*, *int16_t*, ... They should be preferred over the build-in types int, unsigned, short, ... To use them add the following include: ~~~{.c} #include ~~~