|
|
These are some rules and guidelines that we decided to adopt in order to write
|
|
|
more readable and safe code: they were chosen after some trials and errors, so you
|
|
|
will see that not all of the code in this repository follows them,
|
|
|
will see that not all of the code in this repository follows them,
|
|
|
yet it is very important that you keep them in mind when producing the software here.
|
|
|
|
|
|
You can find some good rules for writing safety-critical code here: <http://pixelscommander.com/wp-content/uploads/2014/12/P10.pdf>.
|
|
|
|
|
|
The following rules take inspiration from the JSF - C++ Rules, which you can read here: <http://www.stroustrup.com/JSF-AV-rules.pdf>
|
|
|
|
|
|
## **Critical Rules**
|
|
|
|
|
|
## **Style Rules**
|
|
|
|
|
|
Use [clang-format](https://clang.llvm.org/docs/ClangFormat.html). Full Stop. There is an extension for it for nearly every IDE/Text Editor in the world: make it run automatically when saving a file and get the rules from the `.clang-format` file in the top directory.
|
|
|
|
|
|
## **Critical Rules**
|
|
|
|
|
|
These rules shall be followed every single time.
|
|
|
An exception to these rules can be made only after extensive consultation with the majority of the software team. Every deviation from this rules shall be documented in place with the reasoning behind the decision.
|
|
|
|
|
|
- **Do not use dynamic memory allocation (`malloc` or `new`) after initialization**
|
|
|
The use of dynamic allocation can lead to heap fragmentation and out-of-memory errors after the startup of the rocket (eg. In flight)
|
|
|
- **Do not use direct or indirect recursion**
|
|
|
Recursive code is hard to read and debug, and can easy lead to stack overflow errors.
|
|
|
- **Every class with a virtual function shall define a virtual destructor**
|
|
|
Avoid undefined behavior where a derived class is destroyed through a reference to the base class, possibly leaking resources.
|
|
|
- **Every header file shall be guarded using defines containing the full project path of the file, or by using #pragma once**
|
|
|
Avoid conflicts when having files with the same name in multiple directories.
|
|
|
Example: File `src/shared/sensors/ExampleSensor.h`
|
|
|
- **Do not use dynamic memory allocation (`malloc` or `new`) after initialization**
|
|
|
The use of dynamic allocation can lead to heap fragmentation and out-of-memory errors after the startup of the rocket (eg. In flight)
|
|
|
- **Do not use direct or indirect recursion**
|
|
|
Recursive code is hard to read and debug, and can easy lead to stack overflow errors.
|
|
|
- **Every class with a virtual function shall define a virtual destructor**
|
|
|
Avoid undefined behavior where a derived class is destroyed through a reference to the base class, possibly leaking resources.
|
|
|
- **Every header file shall be guarded using defines containing the full project path of the file, or by using #pragma once**
|
|
|
Avoid conflicts when having files with the same name in multiple directories.
|
|
|
Example: File `src/shared/sensors/ExampleSensor.h`
|
|
|
|
|
|
```cpp
|
|
|
#pragma once
|
|
|
|
|
|
//code goes here
|
|
|
```
|
|
|
|
|
|
## **General rules**
|
|
|
|
|
|
## **General rules**
|
|
|
These are not critical rules, but you should follow them in most cases.
|
|
|
- **Check the validity of the input arguments for every function**
|
|
|
Avoid errors caused by out-of-domain or otherwise wrong input arguments.
|
|
|
- **Avoid throwing exceptions**
|
|
|
Exceptions can cause the program to crash if not handled correctly.
|
|
|
- **Every nonlocal name, except main(), should be placed in some namespace**
|
|
|
These are not critical rules, but you should follow them in most cases.
|
|
|
|
|
|
- **Check the validity of the input arguments for every function**
|
|
|
Avoid errors caused by out-of-domain or otherwise wrong input arguments.
|
|
|
- **Avoid throwing exceptions**
|
|
|
Exceptions can cause the program to crash if not handled correctly.
|
|
|
- **Every nonlocal name, except main(), should be placed in some namespace**
|
|
|
- **Data objects must be declared at the smallest possible level of scope**
|
|
|
- **Prefer inline function over macros. Macros should be avoided or kept as simple as possible**
|
|
|
While sometimes useful, macros kill the readability of the code. Avoid them when you can, and if you use them, keep them simple and comment them appropriately.
|
|
|
- **Constant values should be not be defined using the #define directive**
|
|
|
Prefer `static const` instead, to improve readability and avoid type ambiguities.
|
|
|
- **Variables should not be introduced until they can be initialized with meaningful values**
|
|
|
Avoid errors where someone tries to use an uninitialized variable.
|
|
|
- **Prefer inline function over macros. Macros should be avoided or kept as simple as possible**
|
|
|
While sometimes useful, macros kill the readability of the code. Avoid them when you can, and if you use them, keep them simple and comment them appropriately.
|
|
|
- **Constant values should be not be defined using the #define directive**
|
|
|
Prefer `static const` instead, to improve readability and avoid type ambiguities.
|
|
|
- **Variables should not be introduced until they can be initialized with meaningful values**
|
|
|
Avoid errors where someone tries to use an uninitialized variable.
|
|
|
- **Avoid pointers and prefer references, when possible**
|
|
|
- **Prefer enum class over plain, c-style enum**
|
|
|
Enum classes restrict the scope of their members and are strongly-typed.
|
|
|
- **The return value of non-void functions must be checked by each calling**
|
|
|
This is done to check if the function had an error.
|
|
|
- **Prefer enum class over plain, c-style enum**
|
|
|
Enum classes restrict the scope of their members and are strongly-typed.
|
|
|
- **The return value of non-void functions must be checked by each calling**
|
|
|
This is done to check if the function had an error.
|
|
|
- **Use C++ style cast (static_cast, dynamic_cast…) instead of their C counterparts**
|
|
|
- **Initialize objects in their costructors: avoid init() functions when possible**
|
|
|
It's very easy to forget to call an object's init() method, leaving objects in a undefined state.
|
|
|
- **Initialize objects in their costructors: avoid init() functions when possible**
|
|
|
It's very easy to forget to call an object's init() method, leaving objects in a undefined state.
|
|
|
- **Use TRACE() instead of printf()** `printf()` is a very heavy function (surprised?) and it's very unlikely you need it when the rocket is flying. TRACEs are like printfs but they are stripped out when compiling the flight binary.
|
|
|
|
|
|
## **Commenting rules**
|
|
|
|
|
|
- **Every function shall be documented using a doxygen comment describing the purpose of the function, its parameters and the return value.**
|
|
|
- **Write a comment defining pre and post-conditions for most methods.**
|
|
|
This rule is aimed at improving the readability of the code.
|
|
|
- **Write a comment defining pre and post-conditions for most methods.**
|
|
|
This rule is aimed at improving the readability of the code.
|
|
|
- **Write a comment describing every variable when its meaning is not trivial**
|
|
|
- **Write a comment describing the purpose of each class, struct and enum**
|
|
|
|
|
|
|
|
|
For a guide on how to use doxygen to document the code, take a look here: https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html
|
|
|
|
|
|
## **Best practices**
|
|
|
|
|
|
These rules are mostly aimed at keeping the code readable and consistent.
|
|
|
|
|
|
- **Functions generally should return an integer value which tells the caller if there were problems**
|
|
|
- **No function should be longer than about 80 lines**
|
|
|
Split functions in multiple ones to improve readability of the code
|
|
|
- **No function should be longer than about 80 lines**
|
|
|
Split functions in multiple ones to improve readability of the code
|
|
|
|
|
|
## **Naming conventions**
|
|
|
- **Function will be named using camelCaseNotation, starting with a lower case letter**
|
|
|
Example: `float readTemperatureSample()`
|
|
|
Exception: Acronyms in function names will be UPPERCASE. Example: `bool selfTestIMU()`
|
|
|
- **Classes, structs and enums will be named using CamelCaseNotation**
|
|
|
Example: `class TemperatureSensor {...}`
|
|
|
Exception: As for functions, acronyms in class names will be UPPERCASE. Example: `class FSM {...}`
|
|
|
- **Constants and enum members will be named using ALL_CAPS_WITH_UNDERSCORES**
|
|
|
Example 1: `float TEMPERATURE_SAMPLE;`
|
|
|
Example 2:
|
|
|
|
|
|
- **Function will be named using camelCaseNotation, starting with a lower case letter**
|
|
|
Example: `float readTemperatureSample()`
|
|
|
Exception: Acronyms in function names will be UPPERCASE. Example: `bool selfTestIMU()`
|
|
|
- **Classes, structs and enums will be named using CamelCaseNotation**
|
|
|
Example: `class TemperatureSensor {...}`
|
|
|
Exception: As for functions, acronyms in class names will be UPPERCASE. Example: `class FSM {...}`
|
|
|
- **Constants and enum members will be named using ALL_CAPS_WITH_UNDERSCORES**
|
|
|
Example 1: `float TEMPERATURE_SAMPLE;`
|
|
|
Example 2:
|
|
|
|
|
|
```cpp
|
|
|
enum class ExampleEnum {
|
|
|
EXAMPLE_MEMBER_1,
|
|
|
EXAMPLE_MEMBER_2
|
|
|
enum class ExampleEnum {
|
|
|
EXAMPLE_MEMBER_1,
|
|
|
EXAMPLE_MEMBER_2
|
|
|
}
|
|
|
```
|
|
|
- **Local and member variables will be named all_lowercase_with_underscores**
|
|
|
Example 1: `float pressure_sample;`
|
|
|
- **Names of members of c-style enums shall begin with a identifier of the enum, to avoid ambiguities, since they are not scoped**
|
|
|
Example 2:
|
|
|
|
|
|
- **Local and member variables will be named all_lowercase_with_underscores**
|
|
|
Example 1: `float pressure_sample;`
|
|
|
- **Names of members of c-style enums shall begin with a identifier of the enum, to avoid ambiguities, since they are not scoped**
|
|
|
Example 2:
|
|
|
|
|
|
```cpp
|
|
|
enum EventIDs {
|
|
|
enum EventIDs {
|
|
|
EV_ID_1,
|
|
|
EV_ID_2
|
|
|
}
|
|
|
``` |
|
|
\ No newline at end of file |
|
|
``` |