C++ is now widely used in the development of software for embedded systems, even safety-critical and hard-real-time systems. Even if, due to their design, other programming languages may be better suited for the development of safety critical systems, there are other relevant factors in favor of C++. Examples are availability of skilled developers and tool support.
This CRC Press News gives an overview of both the JSF C++ and MISRA C++ standards and also looks in detail at some of their rules and the rationale behind them. An interesting aspect that is covered is also where both standard differ. For example, JSF C++ does not allow the use of C++ exceptions at all, whereas MISRA C++ specifies detailed rules for their use.
It must be stated, though, that C++ (as well as C) is, by its design, not really a suitable language for writing high integrity or safety-critical software. C++ gives a programmer a great deal of freedom. With freedom comes responsibility, though, and, in the case of C++, a whole lot of responsibility. Nevertheless, there are good reasons for using C++ in a safety-critical system:
On the other hand, C++ has a lot of issues that limit its suitability for safety-critical systems:
When using C++ for safety-critical systems, great care must be taken to avoid any language constructs and code that can potentially lead to unintended program behavior. C has most of these issues as well, though, and this hasn’t stopped C becoming one of the most widely used languages in safety-critical systems. This has only been possible through the introduction of coding standards that limit language features to a safe subset that can be used without giving rise to concerns.
C++ Coding Standards
The most popular coding standard for using C in safety-critical systems is MISRA-C: Guidelines for the use of the C language in critical systems. First published in 1998 and revised in 2004, MISRA-C specifies a “safe” subset of the C language in the form of 121 required and 20 advisory rules. MISRA-C has enjoyed great adoption among developers of safety-critical applications, not just in the automotive industry, at which it was initially targeted. MISRA-C has also had a great influence on another coding standard for using C++ in safety-critical systems.
The current trend to move from C to C++ in the development of critical systems has lead to the publication of MISRA-C++ in the summer of 2008. MISRA-C++ takes many rules from MISRA-C: 2004 and adds many more C++ specific rules, bringing it to a total of 228 rules.
Risk with Using C++ in Safety-Critical Systems
An analysis of MISRA-C++ and JSF C++ reveals many issues that must be taken great care of when using C++ in a safety-critical system. Some of these issues are briefly discussed in the following.
Use of the preprocessor is strongly restricted by coding standards. Basically the only allowed use of the preprocessor is to implement include guards, preventing multiple inclusion of the same header file. Some compilers also provide proprietary mechanisms to prevent multiple inclusion of a header file (in the form of #pragma directives), but these are not allowed by MISRA-C and JSF C++. Other uses of macros, e.g. to define constants or to define inline macros are forbidden. Generally, C++ provides better alternatives for most uses of preprocessor macros. For defining constants, C++ supports the const keyword, as well as enumerations. Inline functions are a much better alternative than inline macros, avoiding all of the problems associated with functional macros and macro arguments.
Implementation-defined, unspecified and undefined behavior
Error-prone language constructs
The type system in C++ has many loopholes that make it easy to circumvent it. Coding standards severely restrict the use of implicit or explicit type conversions (casts), as these can easily lead to a loss of information. The C++ standard does not define fixed sizes for built-in types, making it harder to write portable code requiring fixed-size integers, for example. In addition, C++ (as well as C) does all arithmetic operations in either int or long, depending on the original operand types. Prior to an arithmetic operation, integral types (signed/unsigned short and signed/unsigned char) are promoted to int. This can easily lead to non-portable code, as the following example shows:
short i = 10000;
short j = 8;
int32 result = i * j;
On a system where int is 32 bits (and short is 16 bits), the result will be as 80000, as one would expect considering that the multiplication is carried out with both operands promoted to int. However, on a system where int is just 16 bits, the result will be undefined, as the multiplication will be carried out in 16-bit, will thus overflow, and only the (wrong) result will then be converted to int32.
The plain char type is problematic, as it can be either signed or unsigned. In C++, char, unsigned char and signed char are all distinctive types. Coding standards only allow the use of the plain char type for holding character values and forbid the use of the plain char type for arithmetic operations, as this leads to non-portable code.
Use of dynamic or heap memory (new, delete) is generally strongly restricted in safety critical systems.
A Comparison of MISRA-C++ and JSF C++