|
These are some rules and guidelines that we decided to adopt in order to write
|
|
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
|
|
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.
|
|
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>.
|
|
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>
|
|
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.
|
|
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.
|
|
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**
|
|
- **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)
|
|
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**
|
|
- **Do not use direct or indirect recursion**
|
|
Recursive code is hard to read and debug, and can easy lead to stack overflow errors.
|
|
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**
|
|
- **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.
|
|
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**
|
|
- **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.
|
|
Avoid conflicts when having files with the same name in multiple directories.
|
|
Example: File `src/shared/sensors/ExampleSensor.h`
|
|
Example: File `src/shared/sensors/ExampleSensor.h`
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
#pragma once
|
|
#pragma once
|
|
|
|
|
|
//code goes here
|
|
//code goes here
|
|
```
|
|
```
|
|
|
|
|
|
|
|
## **General rules**
|
|
|
|
|
|
## **General rules**
|
|
These are not critical rules, but you should follow them in most cases.
|
|
These are not critical rules, but you should follow them in most cases.
|
|
|
|
- **Check the validity of the input arguments for every function**
|
|
- **Check the validity of the input arguments for every function**
|
|
Avoid errors caused by out-of-domain or otherwise wrong input arguments.
|
|
Avoid errors caused by out-of-domain or otherwise wrong input arguments.
|
|
- **Avoid throwing exceptions**
|
|
- **Avoid throwing exceptions**
|
|
Exceptions can cause the program to crash if not handled correctly.
|
|
Exceptions can cause the program to crash if not handled correctly.
|
|
- **Every nonlocal name, except main(), should be placed in some namespace**
|
|
- **Every nonlocal name, except main(), should be placed in some namespace**
|
|
- **Data objects must be declared at the smallest possible level of scope**
|
|
- **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**
|
|
- **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.
|
|
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**
|
|
- **Constant values should be not be defined using the #define directive**
|
|
Prefer `static const` instead, to improve readability and avoid type ambiguities.
|
|
Prefer `static const` instead, to improve readability and avoid type ambiguities.
|
|
- **Variables should not be introduced until they can be initialized with meaningful values**
|
|
- **Variables should not be introduced until they can be initialized with meaningful values**
|
|
Avoid errors where someone tries to use an uninitialized variable.
|
|
Avoid errors where someone tries to use an uninitialized variable.
|
|
- **Avoid pointers and prefer references, when possible**
|
|
- **Avoid pointers and prefer references, when possible**
|
|
- **Prefer enum class over plain, c-style enum**
|
|
- **Prefer enum class over plain, c-style enum**
|
|
Enum classes restrict the scope of their members and are strongly-typed.
|
|
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**
|
|
- **The return value of non-void functions must be checked by each calling**
|
|
This is done to check if the function had an error.
|
|
This is done to check if the function had an error.
|
|
- **Use C++ style cast (static_cast, dynamic_cast…) instead of their C counterparts**
|
|
- **Use C++ style cast (static_cast, dynamic_cast…) instead of their C counterparts**
|
|
- **Initialize objects in their costructors: avoid init() functions when possible**
|
|
- **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.
|
|
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.
|
|
- **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**
|
|
## **Commenting rules**
|
|
|
|
|
|
- **Every function shall be documented using a doxygen comment describing the purpose of the function, its parameters and the return value.**
|
|
- **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.**
|
|
- **Write a comment defining pre and post-conditions for most methods.**
|
|
This rule is aimed at improving the readability of the code.
|
|
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 every variable when its meaning is not trivial**
|
|
- **Write a comment describing the purpose of each class, struct and enum**
|
|
- **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
|
|
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**
|
|
## **Best practices**
|
|
|
|
|
|
These rules are mostly aimed at keeping the code readable and consistent.
|
|
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**
|
|
- **Functions generally should return an integer value which tells the caller if there were problems**
|
|
- **No function should be longer than about 80 lines**
|
|
- **No function should be longer than about 80 lines**
|
|
Split functions in multiple ones to improve readability of the code
|
|
Split functions in multiple ones to improve readability of the code
|
|
|
|
|
|
## **Naming conventions**
|
|
## **Naming conventions**
|
|
- **Function will be named using camelCaseNotation, starting with a lower case letter**
|
|
|
|
Example: `float readTemperatureSample()`
|
|
- **Function will be named using camelCaseNotation, starting with a lower case letter**
|
|
Exception: Acronyms in function names will be UPPERCASE. Example: `bool selfTestIMU()`
|
|
Example: `float readTemperatureSample()`
|
|
- **Classes, structs and enums will be named using CamelCaseNotation**
|
|
Exception: Acronyms in function names will be UPPERCASE. Example: `bool selfTestIMU()`
|
|
Example: `class TemperatureSensor {...}`
|
|
- **Classes, structs and enums will be named using CamelCaseNotation**
|
|
Exception: As for functions, acronyms in class names will be UPPERCASE. Example: `class FSM {...}`
|
|
Example: `class TemperatureSensor {...}`
|
|
- **Constants and enum members will be named using ALL_CAPS_WITH_UNDERSCORES**
|
|
Exception: As for functions, acronyms in class names will be UPPERCASE. Example: `class FSM {...}`
|
|
Example 1: `float TEMPERATURE_SAMPLE;`
|
|
- **Constants and enum members will be named using ALL_CAPS_WITH_UNDERSCORES**
|
|
Example 2:
|
|
Example 1: `float TEMPERATURE_SAMPLE;`
|
|
|
|
Example 2:
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
enum class ExampleEnum {
|
|
enum class ExampleEnum {
|
|
EXAMPLE_MEMBER_1,
|
|
EXAMPLE_MEMBER_1,
|
|
EXAMPLE_MEMBER_2
|
|
EXAMPLE_MEMBER_2
|
|
}
|
|
}
|
|
```
|
|
```
|
|
- **Local and member variables will be named all_lowercase_with_underscores**
|
|
|
|
Example 1: `float pressure_sample;`
|
|
- **Local and member variables will be named all_lowercase_with_underscores**
|
|
- **Names of members of c-style enums shall begin with a identifier of the enum, to avoid ambiguities, since they are not scoped**
|
|
Example 1: `float pressure_sample;`
|
|
Example 2:
|
|
- **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
|
|
```cpp
|
|
enum EventIDs {
|
|
enum EventIDs {
|
|
EV_ID_1,
|
|
EV_ID_1,
|
|
EV_ID_2
|
|
EV_ID_2
|
|
}
|
|
}
|
|
``` |
|
``` |
|
\ No newline at end of file |
|
|