... | ... | @@ -29,15 +29,13 @@ Any code you write, modify or generate should follow the coding standards. If y |
|
|
|
|
|
# Best Practices
|
|
|
|
|
|
## Principles
|
|
|
|
|
|
Principles are guiding rules to be more successful. You need to constantly keep these in mind and have them serve as a guardrail to keep you focused towards writing good, and clean software.
|
|
|
|
|
|
> You should not learn of the following principles once and then forget them. A principle is a kind of rule, belief, or idea that guides you.
|
|
|
>
|
|
|
> [Clean C++]
|
|
|
|
|
|
### KISS
|
|
|
## KISS
|
|
|
----
|
|
|
|
|
|
`Keep It Simple and Stupid` and less obscure. Keep the logic as simple as possible, and oftentimes as un-optimized as possible. **Aim for code readability over creating fancy logic.**
|
... | ... | @@ -70,7 +68,7 @@ Aim for extreme simplicity and write minimal amount of code. And remember my wi |
|
|
>
|
|
|
> -- Lockheed Martin (Preet)
|
|
|
|
|
|
### DRY
|
|
|
## DRY
|
|
|
----
|
|
|
|
|
|
> Copy and paste is a design error.
|
... | ... | @@ -100,7 +98,7 @@ But apart from this silly example, do not repeat your code in multiple places. |
|
|
>
|
|
|
> -- The Pragmatic Programmer
|
|
|
|
|
|
### YAGNI
|
|
|
## YAGNI
|
|
|
----
|
|
|
|
|
|
`You Ain't Gonna Need It` This one is the most occurring theme among developers. We usually develop APIs that we think we will need one day. But the fact of the matter is that we can design the APIs when we really need it.
|
... | ... | @@ -117,7 +115,7 @@ Prefer to build software when you really need it, otherwise it is a waste. Avoi |
|
|
|
|
|
But at the same time, provisioning certain things towards the future is okay. For example, the TCP/IP stack provisioned for some reserved bytes and although the standard is largely unchanged, it did provide the roadway towards IPv6. Another example is that if you are developing a communication standard, it is okay to provision for an extra byte for a `version` that allows you to amend the standard in the future.
|
|
|
|
|
|
### Other Best Practices
|
|
|
## Other Best Practices
|
|
|
----
|
|
|
|
|
|
Here are some general Sibros guidelines for being a good, responsible engineer.
|
... | ... | @@ -160,9 +158,7 @@ If we are creating an HTTP client, do not put all the code into a single module. |
|
|
|
|
|
Reference [this article](https://gitlab.com/sibros_public/public/wikis/c/code_designs#code-modularization) for further inspiration.
|
|
|
|
|
|
## Guidelines
|
|
|
|
|
|
### Functions
|
|
|
## Functions
|
|
|
|
|
|
* **Write Testable Code**
|
|
|
* Write code with the test code alongside. This is hard to capture in a guideline like this coding standards' document, but design code that is modular, and testable. Refer to the [C Unit Test Tutorial](https://gitlab.com/sibros_public/public/-/wikis/c/Unit-Testing-for-C) for more details.
|
... | ... | @@ -210,7 +206,7 @@ if (my_variable != CONSTANT) { } // Bad |
|
|
const char *app__get_log_line(const *module_s); // Good
|
|
|
bool app__is_id_ready(const *module_s, const size_t type_id); // Bad, const type_id is redundant
|
|
|
```
|
|
|
### Validate Pointers
|
|
|
## Validate Pointers
|
|
|
|
|
|
Assuming there is a heavy use of pointers, we want to balance the fundamental checks with those that may actually be useful and create a safer code-base.
|
|
|
|
... | ... | @@ -246,7 +242,7 @@ void my_api(module_s *this_ptr, void *ptr) { |
|
|
}
|
|
|
```
|
|
|
|
|
|
#### Pointer Validation Recommendation
|
|
|
### Pointer Validation Recommendation
|
|
|
|
|
|
Each pointer must be validated, and there are certain rules to follow in order to get past static analysis tools flagging your code, and preventing it from getting merged.
|
|
|
|
... | ... | @@ -283,7 +279,7 @@ void module__do(module_s *module, void *pointer) { |
|
|
}
|
|
|
```
|
|
|
|
|
|
### Variables
|
|
|
## Variables
|
|
|
|
|
|
Do not re-use variables, and avoid changing the variables passed as copy. You can opt to make the parameters `const` to enforce this.
|
|
|
|
... | ... | @@ -298,9 +294,9 @@ sl_buffer_s sl_buffer__init(void *static_memory, size_t memory_size_bytes) { |
|
|
}
|
|
|
```
|
|
|
|
|
|
### Data Initialization
|
|
|
## Data Initialization
|
|
|
|
|
|
#### Globals
|
|
|
### Globals
|
|
|
C standards call for initialization of all global memory to zero, and therefore, there is no need for explicit initialization. For any customer integration that fails to do this, we need to work with them to make sure that their startup code performs this necessary step.
|
|
|
|
|
|
```c
|
... | ... | @@ -308,7 +304,7 @@ int my_global; |
|
|
struct_s my_struct;
|
|
|
```
|
|
|
|
|
|
#### Structs
|
|
|
### Structs
|
|
|
Initialize the members by name. Use colon for C++ (it doesn’t support C dot initializer)
|
|
|
|
|
|
Example
|
... | ... | @@ -349,7 +345,7 @@ Do not confuse the struct initialization with array initialization which can use |
|
|
* `char array[2] = {0}`
|
|
|
|
|
|
|
|
|
#### Arrays/Maps
|
|
|
### Arrays/Maps
|
|
|
For constant arrays that map two sets of values and should contain a certain number of elements, use a static, compile-time assertion macro to validate the array size.
|
|
|
|
|
|
This ensures that the code will only compile when the number of elements in the map/array equals the number that is expected.
|
... | ... | @@ -370,13 +366,11 @@ Consider adding the following compile-time assertion: |
|
|
COMPILE_TIME_ASSERT((size_t)SL_ECU_INFO__IMAGE_TYPE_COUNT == ARRAY_COUNT(image_type_string_map));
|
|
|
```
|
|
|
|
|
|
### Safety and MISRA
|
|
|
|
|
|
TODO: Add more guidelines from MISRA
|
|
|
## Safety and MISRA
|
|
|
|
|
|
* No unreacheable code
|
|
|
* **No unreacheable code**
|
|
|
* You should not be able to do this at Sibros code base because of enforcement of 100% test coverage
|
|
|
* No dead code
|
|
|
* **No dead code**
|
|
|
* Do not leave commented code base that is not used in production
|
|
|
* **No recursive functions**
|
|
|
In Embedded Systems, the stack memory is precious, as there is usually not very much.
|
... | ... | @@ -490,30 +484,23 @@ TODO: Add more guidelines from MISRA |
|
|
- `@param` is only needed for parameter names that are not clear
|
|
|
- If the variable is very clear and explicitly named, there should be no need for explaining the parameter. If someone does still explain the parameter for whatever reason, we don’t need to spend any time pointing it out and/or having them remove the comment or amend their commit (all of which takes more time than just leaving a redundant comment in there).
|
|
|
|
|
|
----
|
|
|
# Thoughts and Decisions
|
|
|
## Relative Includes vs. Explicit Path includes
|
|
|
## Use Relative Includes
|
|
|
|
|
|
There are two ways to design a code module:
|
|
|
```c
|
|
|
// Option 1
|
|
|
#include "sl_data_carrier.h"
|
|
|
```
|
|
|
Here, we do Compiler `-I` path resolution
|
|
|
|
|
|
* `#includes` are easier to maintain
|
|
|
* Immune to code module path changes
|
|
|
|
|
|
```c
|
|
|
// Option 2
|
|
|
#include "../buffers/sl_data_carrier.h"
|
|
|
```
|
|
|
|
|
|
Option 1: Compiler `-I` path resolution
|
|
|
|
|
|
* `#includes` are easier to maintain
|
|
|
* Immune to code module path changes
|
|
|
|
|
|
Option 2: Relative Includes
|
|
|
|
|
|
* Avoids ambiguous includes
|
|
|
* Avoids the customer from having to `-I` all include files
|
|
|
Here, we are doing absolute path Includes. This prevents us from having ambiguous includes and prevents the customer from having to `-I` all include files.
|
|
|
|
|
|
In the first example, we expect that the code integrator (customer) has resolved the paths to be able to build the code module. In the second example, we explicitly rely on a folder structure to pull in appropriate header files.
|
|
|
|
... | ... | |