Top 28 code guidelines for automotive products

8 minute read

Published:

We all love clean, well structured, safe and well performed code. But what makes code a good code?

What makes code a good code?

There are a lot of ways to make build a good code. Like architecture in advance, tests coverage, code review. But there is one way, that sometimes ignored - follow code guidance. Every big company adopts code guidance. There is plenty of C++ code guidance. For example some of very popular code guidelines:

Today I would like to talk about AUTOSAR C++ Coding rules. The rules that should be followed in many car manufacturing companies.

This standard is available to everyone here.

It is a 510 page document. But if you do not have time to read the document, welcome to this post. I will show you some of the most interesting rules, from my perspective, that should be adopted in your code.

What is AUTOSAR C++ Core guidelines

From Wikipedia:

AUTomotive Open System ARchitecture (AUTOSAR) is a global development partnership of automotive interested parties founded in 2003. It pursues the objective to create and establish an open and standardized software architecture for automotive electronic control units (ECUs).

As we can see it is a partnership of care manufacture and suppliers to align on the single architecture and communication protocols.

AUTOSAR C++ Core guidance is the collection of C++ rules, how you should write code and what you should avoid.

Let’s take a look on the most interesting, from my point of view.

28 not obvious rules that you should adopt in your code

Rule A0-4-2: Type long double shall not be used

The type long double is not fixed and can be different depends on the compiler implementation. It can be 64, 80 or 128 bits. It means that with the time, once you change the hardware and compiler, your software might change the behavior. That is not what you want.

Rule A1-4-3 All code should compile free of compiler warnings

Yes, I know it is widely adopted rule, but I regular see warnings when I compile open source projects. Keep in mind, warning is also an error, it means you do something wrong.

Rule A2-11-1 Volatile keyword shall not be used

Volatile prevents compiler from optimization of the code.

Rule A2-13-3 Type wchar_t shall not be used

Width of the type is implementation defined. So usage of it is prohibited.

Rule A3-9-1 Fixed width integer types from , indicating the size and signedness, shell be used in place of the basic numerical types.

You should always explicitly define the width of the variable.

Rule A4-7-1 An integer expression shell not lead to data loss

This is an interesting and not obvious rule. Lets take a look on the code:

std::int8_t add(std::int8_t x, std::int8_t y){
    return x+y;
}

From the first check, you might think that it is absolutely correct code, both types are integers, and we return an integer.

But we risk to have the integer overflow. So if we have an addition of two integers and the sum is greater than max value of the 8 bit integer, we have UB.

So the correct implementation should be:

std::int16_t add(std::int8_t x, std::int8_t y){
    return x+y;
}

So every time when we have a risk of reaching the max value, we should do a check.


std::int8_t increment(std::int8_t x){
	if(x == 127) return 0;
    return ++x;
}

Rule A5-0-1 The value of an expression shall be the same under any order of the evaluation

Sometimes we try to write a complicated code to save 1-2 lines of code.

Let’s take a look on the following code:

std::uint8_t do_something(std::uint8_t (&arr)[10], std::uint8_t idx){
	return arr[idx] + idx++;
}

Depending on what happens first: access the element of the array or increment of the index, we will get different results.

Rule A5-0-3 The declaration of objects shall contain no more than two levels of pointer indirection

Two pointer is commonly used to encode 2 dimensional arrays. But if you have in your code something like

std::uint8_t*** name;

You need to remove it from your code.

Rule A5-1-2 Variables shall not be implicitly captured in a lambda expression

We all love and use lambdas. I saw many cases, when developers where lazy to capture variables and simply captured everything.

std::uint8_t x = 10;
auto lambda = [&](std::uint8_t y){return x+y;}

In this case we catch all variables. That should not happen.

Rule A5-2-1 dynamic_cast should not be used

If you have a real time system, dynamic_cast is not something you want to have. If there is no way to avoid it, you can replace it with polymorphism or consider the usage of the dynamic_cast custom implementation.

Rule A5-2-2 Traditional C-style cast shall not be used

The name of the rule is pretty self explainable. No C-style cast.

Rule A5-2-4 reinterpret_cast shall not be used

As in the previous case, nothing to add.

Rule M6-2-2 Floating-point expressions shall not be directly or indirectly tested for equality or inequality

You might saw it already, that floating points data types are not trivial. Just compile and run the code:

#include <iomanip>
#include <iostream>

int main() {
  std::cout << std::setprecision(17) << 0.1 + 0.2;
}

For more details, check 0.30000000000000004

So if you have any tests, be careful with floating points.

Rule A6-5-3 Do statement should not be used

The problem of the do statements, is that it does something and then verifies the condition. In AUTOSAR it is considered as bug-prone approach and you should not use it.

Rule A6-6-1 Te goto statement shall not be used

Commonly used rule, but I still love it and support. Say no to goto!

Rule A7-1-6 The typedef specifier shall not be used

typedef should be replaced with the using. Nothing to add.

Rule A7-2-2 Enumeration underlying type should be explicitly defined

If you use an enum in the code you should always explicitly define the underlying type.

Rule A7-2-3 Enumerations shall be declared as scoped enum classes

This example shows the implementation of rule A7-2-2 and A7-2-3.

enum class E: std::uint8_t {
	E1 = 1,
	E2 = 2
};

Rule A7-5-2 Functions shall not call themselves, either directly or undirected

Recursion might cause stack overflow, if you run the code on the hardware with the very limited resources.

Rule A8-5-2 Braced-initialization {}, without equals sign, shall be used for variable initialization.

If you want to initialize the variable, you should use {}.

std::uint8_t x{9}; // complient
std::uint8_t x = 9; // non-complient

Rule A9-5-1 Unions shall not be used

Unions are not type safe. So no unions in your code.

Rule A11-3-1 Friend declarations shall not be used

Friend declaration reduces encapsulation. So no friend classes.

Rule A15-0-1 The function shall not exit with an exception if it is able to complete its task

Exception should capture unexpected behavior. But no every unexpected behavior should interrupt the function. If it is possible to continue the task, the function should be continued.

Rule A16-0-1 The pre-processor shall only be used for unconditional and conditional file inclusion and include guards, and using the following directives: (1) #ifndef, (2) #ifdef, (3) #if, (4) if defined, (5) #elif, (6) #else, (7) #define, (8) #endif, (9) #include.

I saw a lot of code, that would violate this rule. If you have something like:

#ifdef WIN32
std::uint8_t fn1() noexcept;
#endif

#define MAX_SIZE = 100U

#define MIN(a,b) (((a) < (b)) ? (a) : (b))

You should remove it. All of the above examples.

Rule A16-7-1 The #pragma directive shall not be used

No #pragma in your code.

Rule A18-1-1 C-style arrays shall not be used

No C-style arrays in the code.

std::uint8_t arr[100]; // non/complient

Rule A18-1-2 The std::vector specialization shall not be used.

The vector ob bools is a specific type, expected to be optimized for the size. As the result, some STL algorithms do not work as expected. Particular, operator does not return a contiguous sequence of elements as it does for the primary template std::vector.

Rule A27-0-1 C-style strings shall not be used

Just keep in mind, that AUTOSAR C++ guidelines encourage you to use C++. So if you need to have a sequence of chars, use std::string.

What to take from this article?

  • There is a lot of standards that would allow you to improve you code quality.
  • Not all rules are obvious, but most of rules are simple and your code is compliend already.
  • If your code compiles and runs, it does not mean that it is a good code.