Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • 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 inputs

    • A 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 of if (value == 5)

    • Example: Use if (const_x != value) instead of if (value != const_x)

    • Example: Do not use if (5 < value); see Yoda notation

    • Example: Do not use if (10 >= value); see Yoda notation

    • Because 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 the underscore convention, primarily due to the following reasons:

    • Snake case does not suffer from:

    • 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 names

      • Do 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 for

      • Global and static global Constants (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 be UPPER_CASE_SNAKE_CASE, and we don’t want to make a confusing exception between global const scalar types being UPPER_CASE_SNAKE_CASE and global const aggregate types being lower_case_snake_case. The original benefit of using UPPER_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 not UDS

  • Use American English spelling and not British English spelling.

    • Example: behavior instead of behaviour

    • Because Sibros started in America

  • No single letter variables, not even in for loops.

    • Instead of i and j, use index or something else that describes what you are looping over

    • Because 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 like b_door_open and pointers with p like p_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 forbid extern access

    • If 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.

...