...
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 for more details.
Keep the functions small
Do not write functions that are larger than 10 lines of meaningful code. The benefits are:
Creates smaller units of code that are easier to test
Increases code clarity
Decreases code complexity (ISO26262, Table 1: 1A)
Do not use more than 5 brace levels in your code
Refactor code into smaller functions
Re-structure the branch statements to reduce the curly brace nesting
This improves the overall readability of the code
No recursive functions
In Embedded Systems, the stack memory is precious, as there is usually not very much.
Each function should only have one return statement
and that should be at the end of the function. This is to prevent unintentional side-effects where a developer adds some code thinking it will be run, but the function exits out before it gets to that piece of code.
MISRA and safety certified code requires this
Use
(void)
for functions without inputsA function with no input should be labeled as
func(void)
for the parameters. This is because if you omit the void in C, you can pass it infinite number of parameters. C++ safeguards you for this case though.
Prefer at most 10 meaningful lines per function
This forces you to make the code more modular
This helps during unit-testing
Avoid using more than 5
{}
brace levels in your code
Use Yoda notation in equality checks but not anywhere else.
Example: Use
if (5 == value)
instead ofif (value == 5)
Example: Use
if (const_x != value)
instead ofif (value != const_x)
Example: Do not use
if (5 < value)
; see Yoda notationExample: Do not use
if (10 >= value)
; see Yoda notationBecause we don't want to accidentally assign the variable in the condition check.
Even though static analysis should catch this, we do not want to rely on it.
Code Block |
---|
if (CONSTANTconstant == my_variable) { } // Good if (my_variable == CONSTANTconstant) { } // Bad if (CONSTANTconstant != my_variable) { } // Good if (my_variable != CONSTANTconstant) { } // Bad |
Use
const
type qualifier on pointer parameters when applicable (i.e. when the referenced value remains unchanged).Do not use this type qualifier on pass-by-value parameters as it is redundant.
...
Snake Case
After some research, we decided to go with
snake_case
or theunderscore
convention, primarily due to the following reasons:Snake case does not suffer from:
Differentiating Abbreviations
tcpIpOpen
Naming convention inside code can match the file names
Use
myCodeModule.c
creates issues with diverse build systems (Windows vs. Linux)
Therefore, use
snake_case
with no capital letters for all code, file and folder namesDo not use
camelCase
Do not use
PascalCase
Do not use Hungarian notation (like
pMyString
for pointers, 'bSwitch' for booleans)Do not mix dashes with underscores. e.g.
pre_integration_plan
is OK;pre-integration_plan
is not OK.
Names of basic types and code modules
Use
ALLUPPER_CAPSCASE_SNAKE_CASE
forGlobal and static globalConstants(local variables that are const can be lowercase to differentiate between a global const variable vs. local const variable)Upon further discussion, it was decided to use
lower_case_snake_case
for all variables, including constants. This is because we don’t want global const struct instances to beUPPER_CASE_SNAKE_CASE
, and we don’t want to make a confusing exception between global const scalar types beingUPPER_CASE_SNAKE_CASE
and global const aggregate types beinglower_case_snake_case
. The original benefit of usingUPPER_CASE_SNAKE_CASE
for constants was to easily identify which variables are constant. However, all of our global constants are file scope, so const variables can easily be identified as being constant by viewing their declaration in the same file.
Macros
Enumeration values
Code Module
Precede with layer name
Create a header file, source file, and its corresponding unit-test file
If there are private enums, typedefs, or functions, then also create the private module
Example:
app_my_module.h
,app_my_module.c
,app_my_module_private.h
,test_app_my_module.c
All code, file names and folder names in lowercase
All acronyms shall be lowercase, just like all other names, whether they are part of functions, variables, or any other types
The reason for this is to keep naming consistent with the file naming convention, which uses all lowercase
Example: your module for Unified Diagnostic Services shall be abbreviated as
uds
and notUDS
Use American English spelling and not British English spelling.
Example:
behavior
instead ofbehaviour
Because Sibros started in America
No single letter variables, not even in
for
loops.Instead of
i
andj
, useindex
or something else that describes what you are looping overBecause you should have more meaning to the variable
No Hungarian notation for variable names
Because variables change meaning, or the notation doesn't convey any useful information. For example, do not create boolean variables starting with
b
likeb_door_open
and pointers withp
likep_battery_data
.Because this notation existed in the old days with lack of good IDEs
No global variables that can be accessed using an
extern
You should make them
static
in your source code, to forbidextern
accessIf you want to have access to a variable, it should be returned from a public access function
Because we don’t want
peek and poke
code
Use the verb-noun method for naming routines that perform some operation on a given object, such as:
app_battery__calculate_soc()
This allows for the reader of the code to be able to easily understand the action that the function is taking.
...