MISRA C++:2023
Safer C++17 for critical systems
About me
Grew up in Rodos, Greece
Software Engineering at GU & Chalmers
Working with embedded systems
Teaching
DIT113, DAT265, Thesis supervision
C++, Coursera, Udemy
Open source projects
https://platis.solutions
https://github.com/platisd
Email: dimitris@platis.solutions
What is MISRA?
MISRA: Motor Industry Software Reliability Association
Originally: UK government initiative to develop guidelines for
embedded software in road vehicle electronic systems
Purpose: Promote best practices in safety- and security-related
electronic systems and other software-intensive applications
Focus: C and C++ for critical systems
Critical systems: Failure can lead to significant harm, loss of life,
injury, or substantial property damage
MISRA C++
MISRA C++: Guidelines for the use of C++ in critical systems
Latest Version: MISRA C++:2023, based on C++17, 179 guidelines
Vision: "Predictable subset", no undefined & unspecified behaviour
ISO26262: Functional safety standard for automotive systems
Merger: MISRA C++:2008 AUTOSAR C++14 MISRA C++:2023
Liability: Compliance does not guarantee legal immunity
Paywall: Copies of the guidelines available for 15£
Open source tooling: Impossible due to copyright
Free "copy": Mathworks offers a rather complete overview
Different behaviors in C++
Undefined (UB): Anything can happen, no point to reason about it
E.g. Dereferencing nullptr , accessing out-of-bounds memory
Implementation-defined: Implementation documents behavior
E.g. Exact size of int
Unspecified: Implementation doesn't need to document behavior
E.g. Order of evaluation of function arguments
Problems with C++ in critical systems
UB: Relying on undefined or unspecified behavior
Misuse: Misuse of features frequently allowed by the compiler
Misunderstanding: Areas of the language often misunderstood
E.g. Implicit conversions, memory management
Runtime checks: Unavailable (e.g. array bounds, pointer validity)
MISRA Guidelines
Directives: Guidelines harder to implement automated checks for
Rules: Guidelines with concrete description, easier to automate
Categories:
Mandatory: Must be followed, deviations not allowed
Required: Must be followed, deviations allowed
Advisory: Follow to a reasonable extent
Enforcement: Static analysis tools and code reviews
Formal Deviations
Achieving compliance with MISRA Coding Guidelines
Unavoidable: Must be documented and managed systematically
Deviation records
The violated guideline
The reason for the deviation
Background information
Risk assessment
Traceability: Which deviations are present in the codebase
E.g. Comments with machine-readable markers for deviations
Target audience
Developers, architects, testers, and managers
Working with C++ in critical systems
Interested in learning about coding practices in critical systems
Interested in improving code quality regardless of the project
Relying on undefined/unspecified behavior is generally bad
Less interesting for those who already work with AUTOSAR C++14
Will not focus on the differences, they are not that many anyway
Welcome to join the discussion and share experiences
Coming up
An overview of 25 interesting guidelines from MISRA C++:2023
Personal selection, not exhaustive, not in order of importance
Code examples to illustrate the guidelines
Reflections on the implications of adhering to MISRA C++:2023
Should you deviate or not?
"I don't need a babysitter!"
You may never need one, but...
Can you speak for your colleagues or future maintainers?
Critical systems need to prove safety
Proving safety is intricate, when subtle pitfalls can go unnoticed
Proving safety is hard, MISRA helps by limiting the language
Skepticism is healthy
Deviate when justified, but assess the risks carefully
Don't be negative, no large differences to modern C++
Rule x.x.x: Modern C++ practices (mostly) apply
MISRA C++:2023 C++ Core Guidelines
4.1.3: No occurrence of undefined or critical unspecified behaviour
0.1.2: The value returned by a function shall be used
0.2.2: A named function parameter shall be used at least once
6.4.1: A variable declared in an inner scope shall not hide a variable
declared in an outer scope
11.6.2: The value of an object must not be read before it's been set
15.1.3: Conversion operators and constructors that are callable
with a single argument shall be explicit
Rule 21.6.1: Dynamic memory should not be used
No STL class with std::allocator , e.g. std::vector , std::set
No function, e.g. new , delete , malloc , std::make_unique
Error-prone: Dynamic memory may lead to leaks or fragmentation
Out-of-memory: Catastrophic during operation of critical systems
Init-vs-run: Allocate memory in certain phases, e.g. initialization
Alternative: std::array , memory pools, allocators, static_vector
Advisory: Projects in certain domains might treat it as mandatory
int* p = new int(42); // Non-compliant
std::vector<int> v; // Non-compliant
auto f = std::make_shared<int>(42); // Non-compliant
Rule 21.6.2: Dynamic memory shall be managed
automatically
No new or delete in the code (except for placement new )
No malloc , free , realloc , std::unique_ptr::release() , etc.
Manual memory management is error-prone (leaks, double free )
Smart pointers: Avoid deviating from this guideline
Manual management: Minimize deviation with RAII wrappers
auto p = new int(42); // Non-compliant
delete p; // Non-compliant
auto f = std::make_unique<int>(42); // Compliant
f.release(); // Non-compliant
Rule 6.9.2: The names of the standard signed integer types
and standard unsigned integer types should not be used
Not allowed: int , short , unsigned long etc
Use fixed-width types instead: std::int32_t , std::uint16_t , etc
Why: The range of standard types is implementation-defined
Only the minimum range is guaranteed by the standard
May result in portability issues and wrong assumptions
Exception: Return type of main() and its argc argument
Exception: Creating type aliases, e.g. using ticks_t = long;
Advisory: Often enforced as it is easy to conform and check
Rule 8.2.2: C-style casts and functional notation casts shall
not be used
C-style casts: Almost limitless type conversions without checks
Misuse: Syntax can be confused with function or constructor calls
Exception: C-style casting to void is permitted
Alternatives: static_cast , dynamic_cast etc usually sufficient
auto i = (std::int16_t) 42; // Non-compliant
auto z = std::int16_t(42); // Compliant (not a cast, constructor call)
(void) i; // Compliant by exception
Rule 8.2.5: reinterpret_cast shall not be used
Undefined behavior: Casting between unrelated types
Almost equivalent to C-style casts which are not allowed
reinterpret_cast<T*>(ptr) to cast a pointer to T* if:
T is void , char , unsigned char , std::byte
reinterpret_cast<T>(ptr) to convert a pointer to an integer T if:
T is large enough to represent a std::uintptr_t
std::int32_t i = 42;
auto pI = reinterpret_cast<std::uint32_t>(i); // Non-compliant
std::int32_t val = 32;
auto pToVal = reinterpret_cast<std::byte*>(&val); // Compliant by exception
Rule 9.6.1: The goto statement should not be used
Why: Often leads to unmaintainable code, considered bad practice
Advisory: But often treated as mandatory
Required: goto -related rules, e.g. 9.6.3 allows only jumping to
labels declared later in the function body
std::int32_t i = 0;
loop_start:
if (i < 10) {
std::cout << i++ << std::endl;
goto loop_start; // Non-compliant for both 9.6.1 and 9.6.3
}
Rule 7.11.2: An array passed as a function argument shall not
decay to a pointer
Bounds: Arrays as arguments decay to pointers, size info lost
If code must work with different sizes, maintain dimensions
C-style arrays: Bad idea, 11.3.1 (advisory) forbids C-style arrays
Exception: Passing a string literal when expecting a const char*
String literal guaranteed to end with 0 so length is inferred
void processArray1(std::int32_t arr[42]); // Non-compliant, decays to pointer
void processArray2(std::int32_t* arr); // Non-compliant, decays to pointer
void processArray3(std::int32_t (&arr)[42]); // Compliant, enforces arr[42]
template<std::size_t N>
void processArray4(std::int32_t (&arr)[N]); // Compliant, array of N
Rule 24.5.2: The C++ Standard Library functions memcpy ,
memmove and memcmp from <cstring> shall not be used
UB: If the source and destination overlap or not trivially copyable
Incompatible: memcmp may compare objects not logically equal
Floating point numbers, class objects due to padding and
alignment, buffers that may contain uninitialized data
struct Data {
std::byte first{};
// Content of potential padding is unspecified
std::uint64_t payload{};
};
if (memcmp(&data1, &data2, sizeof(Data)) == 0) { /* Non-compliant */ }
Rule 21.2.1: The library functions atof , atoi , atol and
atoll from <cstdlib> shall not be used
UB: If the string cannot be converted to the expected type
Modern alternatives: std::stoi , std::stod , std::from_chars
int i = atoi("42"); // Non-compliant
int j = std::stoi("42"); // Compliant
std::int32_t res = 0;
std::string_view str = "42";
auto [p, errc] = std::from_chars(str.data(), str.data() + str.size(), res);
if (errc == std::errc()) {
std::cout << "Parsed integer: " << res << 'n';
} else {
std::cerr << "Error parsing integern";
}
Rule 21.2.2: The string handling functions from <cstring> ,
<cstdlib> , <cwchar> and <cinttypes> shall not be used
strcat , strcmp , strcpy , strlen , strtol etc
UB: Out of bounds access and null-termination issues
Modern & safer alternatives, e.g. std::string , std::string_view
void populate(char* message, std::size_t messageSize) {
const char* payload = "a message";
// Non-compliant calls to strlen and strcpy
if ((strlen(payload) + 1U) < messageSize) { strcpy(message, payload); }
}
std::string_view payload = "a message"; // (Modern) Alternative to char*
payload.size(); // Compliant alternative to strlen
payload.copy(message, payload.size()); // Compliant alternative to strcpy
Rule 21.2.3: The library function system from <cstdlib> shall
not be used
Security: Can execute arbitrary commands, might be exploited
Portability: Implementation-defined behavior may vary
Undefined behavior: Guidelines do not elaborate further
Alternatives: Use APIs, formally deviate, posix_spawn or similar
E.g. use std::filesystem for file operations
std::system("ls ."); // Non-compliant
for (const auto& entry: std::filesystem::directory_iterator(".")) {
std::cout << entry.path() << std::endl; // Compliant
}
Rule 30.0.1: The C Library input/output functions shall not be
used
gets , fopen , fclose , fread , fflush , printf , scanf etc
Behavior: Undefined, unspecified and implementation-defined
Alternatives: std::cin , std::ifstream , std::ofstream etc
<fstream> compliant despite maybe using offending functions
printf("Hello, World!n"); // Non-compliant
std::cout << "Hello, World!" << std::endl; // Compliant
Rule 21.10.1: The features of <cstdarg> shall not be used
va_list , va_start , va_end , va_arg etc
Why: Passing arguments via ... omits compile-time type checking
UB: va_end not called after va_start being used
UB: va_arg in multiple functions on the same va_list
Alternatives: Variadic templates, functions with fixed arguments
void doSomething(va_list args) { /* ... */ } // Non-compliant
template<typename T, typename... Args>
void doSomething(const T& first, const Args&... args) { /* ... */ } // OK
Rule 24.5.1: The character handling functions from <cctype>
and <cwctype> shall not be used
isalpha , isdigit , isspace , toupper , tolower etc
UB: When the character cannot be represented as unsigned char
Alternatives: Use equivalents from <locale>
Same as above but with an extra std::locale parameter
std::isalpha('a'); // Non-compliant
std::isdigit('1', std::locale{}); // Compliant
Rule 6.7.2: Global variables shall not be used
Non-constant variables in some global scope
Non-constant static member variables
UB: Due to data races when concurrent access is possible
Unspecified: Order of (dynamic) initialization not guaranteed
Unpredictable: Unexpected side effects, hard to track down
// Some global scope
std::int32_t currentSpeed{ getCurrentSpeed() }; // Non-compliant, non-const
const std::int32_t prevSpeed{ currentSpeed }; // Non-compliant, dynamic init
struct ComplexCar { ComplexCar(/* Some intricate initialization */); };
const ComplexCar car{}; // Non-compliant, dynamic initialization
Rule 6.7.1: Local variables shall not have static storage
duration
Temporal coupling: Data races that may lead to UB
Global state: Hard to understand, maintain and test
Destruction: Reverse order of creation, avoid dangling references
Deviations: Occasionally straightforward, but otherwise tricky
template<typename T>
T& getSingleton() {
static T instance{}; // Non-compliant, static storage duration
return instance;
}
Rule 19.6.1: The #pragma directive and the _Pragma operator
should not be used
Why: Non-portable, implementation-specific behavior
Advisory: Often treated as mandatory since it's easy to enforce
// Foo.h
#pragma once // Non-compliant
#ifndef FOO_H // Compliant, portable alternative
// ...
#endif
Rule 28.3.1: Predicates shall not have persistent side effects
Predicate: A function that (usually) returns a boolean value
Compare , Predicate etc in STL
Implementation-defined: Predicate may be copied
Unspecified: The order of predicate invocations
Unexpected: Side effects due to mutable internal state
using Samples = std::array<int32_t, 10U>;
int64_t countBadSamples(const Samples& s, int32_t& superBad) {
return std::count_if(s.begin(), s.end(), [&superBad](int32_t sample) {
if (sample < 100) { superBad++; } // Non-compliant
return sample < 0;
});
}
Rule 18.5.2: Program-terminating functions should not be
used
std::abort , std::exit , std::quick_exit , std::terminate etc
Why: Destructors not called, environment left in bad state
E.g. 3rd-party resources not released, files locked, etc
Exception: assert is OK since it is a debugging tool
Advisory: Often convenient to deviate if risks are understood
void handleError() {
std::cerr << "Fatal error occurred, terminating programn";
std::exit(EXIT_FAILURE); // Non-compliant
}
Rule 18.3.1: There should be at least one exception handler
to catch all otherwise unhandled exceptions
Catch-all: catch(...) to handle all unhandled exceptions in main
Why: Program terminates in an implementation-defined manner
Advisory: Personally find it annoying, but easy to comply
int main() {
try { cool_company::Car car{}; } // Application code
catch (const cool_company::NoFuelException& e) { return 1; } // Specific
catch (...) { return 99; } // Catch-all for all other exceptions
return 0;
}
Rule 5.13.5: The lowercase form of L shall not be used as the
first character in a literal suffix
Why: l and 1 are visually similar, can lead to confusion
How: Use uppercase L for long literals, e.g. 42L
Literals: Unsigned literal numbers need suffixes (see 5.13.4 )
int64_t i = 42l; // Non-compliant
int64_t j = 42L; // Compliant
int64_t k = 42ul; // Compliant because `l` not the first character
int64_t m = 42Ul; // Compliant but why not `UL`?
Rule 8.2.10: Functions shall not call themselves, either
directly or indirectly
Recursion: Not allowed, can lead to stack overflow
Why: Hard to predict worst-case operation count
Deviations: If you really must, document how risk is mitigated
Exception: Generally OK for constexpr ("core constant") functions
int32_t factorial(int32_t n) {
if (n <= 1) return 1; // Base case
return n * factorial(n - 1); // Non-compliant, recursion
}
Rule 9.4.1: All if ... else if constructs shall be terminated
with an else statement
Why: Defensive programming, ensures all cases handled
Empty else clause: May be used to indicate intentional handling
Default case: 9.4.2 requires a default case in switch statements
if (car.getSpeed() > 100) { /* OK */ }
else if (car.getSpeed() < 50) { /* There must be an else clause */ }
else { /* Compliant, handles all cases */ }
Rule 10.2.1: An enumeration shall be defined with an explicit
underlying type
Unscoped enums: The underlying type is implementation-defined
Scoped enums: The underlying type is int by default
Why: No implicit conversions, promotes predictability, portability
Exception: When all enumerators use default values
enum class Month { Jan = 1, Feb, Mar, Apr, May, Jun }; // Non-compliant
enum class Day : int8_t { Sun = 0, Mon, Tue, Wed, Thu, Fri }; // Compliant
enum class Color { R, G, B }; // Compliant by exception
Rule 14.1.1: Non-static data members should be either all
private or all public
Why: Member functions allow better access control
Robustness: Never accidentally break class invariants
Advisory: Common practice, but rather "too opinionated" as a rule
class Car {
public:
int32_t id{}; // Non-compliant, mixed access
int32_t getSpeed() const { return speed; }
private:
int32_t speed{};
};
Takeaways
MISRA rules define a "safer subset" of C++ for critical systems
BjarneStroustrup/profiles
C++ safety, in context
Avoids UB, unspecified or implementation-defined behavior
Avoids common pitfalls and bad practices
Overall a big improvement over MISRA C++:2008
Mostly in alignment with C++ Core Guidelines
Deviations possible, but must be documented and justified
General-purpose C++ projects may benefit from some rules

[Koln C++] Safer C++: 25 MISRA Rules Explored

  • 1.
    MISRA C++:2023 Safer C++17for critical systems
  • 2.
    About me Grew upin Rodos, Greece Software Engineering at GU & Chalmers Working with embedded systems Teaching DIT113, DAT265, Thesis supervision C++, Coursera, Udemy Open source projects https://platis.solutions https://github.com/platisd Email: dimitris@platis.solutions
  • 3.
    What is MISRA? MISRA:Motor Industry Software Reliability Association Originally: UK government initiative to develop guidelines for embedded software in road vehicle electronic systems Purpose: Promote best practices in safety- and security-related electronic systems and other software-intensive applications Focus: C and C++ for critical systems Critical systems: Failure can lead to significant harm, loss of life, injury, or substantial property damage
  • 4.
    MISRA C++ MISRA C++:Guidelines for the use of C++ in critical systems Latest Version: MISRA C++:2023, based on C++17, 179 guidelines Vision: "Predictable subset", no undefined & unspecified behaviour ISO26262: Functional safety standard for automotive systems Merger: MISRA C++:2008 AUTOSAR C++14 MISRA C++:2023 Liability: Compliance does not guarantee legal immunity Paywall: Copies of the guidelines available for 15£ Open source tooling: Impossible due to copyright Free "copy": Mathworks offers a rather complete overview
  • 5.
    Different behaviors inC++ Undefined (UB): Anything can happen, no point to reason about it E.g. Dereferencing nullptr , accessing out-of-bounds memory Implementation-defined: Implementation documents behavior E.g. Exact size of int Unspecified: Implementation doesn't need to document behavior E.g. Order of evaluation of function arguments
  • 6.
    Problems with C++in critical systems UB: Relying on undefined or unspecified behavior Misuse: Misuse of features frequently allowed by the compiler Misunderstanding: Areas of the language often misunderstood E.g. Implicit conversions, memory management Runtime checks: Unavailable (e.g. array bounds, pointer validity)
  • 7.
    MISRA Guidelines Directives: Guidelinesharder to implement automated checks for Rules: Guidelines with concrete description, easier to automate Categories: Mandatory: Must be followed, deviations not allowed Required: Must be followed, deviations allowed Advisory: Follow to a reasonable extent Enforcement: Static analysis tools and code reviews
  • 8.
    Formal Deviations Achieving compliancewith MISRA Coding Guidelines Unavoidable: Must be documented and managed systematically Deviation records The violated guideline The reason for the deviation Background information Risk assessment Traceability: Which deviations are present in the codebase E.g. Comments with machine-readable markers for deviations
  • 9.
    Target audience Developers, architects,testers, and managers Working with C++ in critical systems Interested in learning about coding practices in critical systems Interested in improving code quality regardless of the project Relying on undefined/unspecified behavior is generally bad Less interesting for those who already work with AUTOSAR C++14 Will not focus on the differences, they are not that many anyway Welcome to join the discussion and share experiences
  • 10.
    Coming up An overviewof 25 interesting guidelines from MISRA C++:2023 Personal selection, not exhaustive, not in order of importance Code examples to illustrate the guidelines Reflections on the implications of adhering to MISRA C++:2023 Should you deviate or not?
  • 11.
    "I don't needa babysitter!" You may never need one, but... Can you speak for your colleagues or future maintainers? Critical systems need to prove safety Proving safety is intricate, when subtle pitfalls can go unnoticed Proving safety is hard, MISRA helps by limiting the language Skepticism is healthy Deviate when justified, but assess the risks carefully Don't be negative, no large differences to modern C++
  • 12.
    Rule x.x.x: ModernC++ practices (mostly) apply MISRA C++:2023 C++ Core Guidelines 4.1.3: No occurrence of undefined or critical unspecified behaviour 0.1.2: The value returned by a function shall be used 0.2.2: A named function parameter shall be used at least once 6.4.1: A variable declared in an inner scope shall not hide a variable declared in an outer scope 11.6.2: The value of an object must not be read before it's been set 15.1.3: Conversion operators and constructors that are callable with a single argument shall be explicit
  • 13.
    Rule 21.6.1: Dynamicmemory should not be used No STL class with std::allocator , e.g. std::vector , std::set No function, e.g. new , delete , malloc , std::make_unique Error-prone: Dynamic memory may lead to leaks or fragmentation Out-of-memory: Catastrophic during operation of critical systems Init-vs-run: Allocate memory in certain phases, e.g. initialization Alternative: std::array , memory pools, allocators, static_vector Advisory: Projects in certain domains might treat it as mandatory int* p = new int(42); // Non-compliant std::vector<int> v; // Non-compliant auto f = std::make_shared<int>(42); // Non-compliant
  • 14.
    Rule 21.6.2: Dynamicmemory shall be managed automatically No new or delete in the code (except for placement new ) No malloc , free , realloc , std::unique_ptr::release() , etc. Manual memory management is error-prone (leaks, double free ) Smart pointers: Avoid deviating from this guideline Manual management: Minimize deviation with RAII wrappers auto p = new int(42); // Non-compliant delete p; // Non-compliant auto f = std::make_unique<int>(42); // Compliant f.release(); // Non-compliant
  • 15.
    Rule 6.9.2: Thenames of the standard signed integer types and standard unsigned integer types should not be used Not allowed: int , short , unsigned long etc Use fixed-width types instead: std::int32_t , std::uint16_t , etc Why: The range of standard types is implementation-defined Only the minimum range is guaranteed by the standard May result in portability issues and wrong assumptions Exception: Return type of main() and its argc argument Exception: Creating type aliases, e.g. using ticks_t = long; Advisory: Often enforced as it is easy to conform and check
  • 16.
    Rule 8.2.2: C-stylecasts and functional notation casts shall not be used C-style casts: Almost limitless type conversions without checks Misuse: Syntax can be confused with function or constructor calls Exception: C-style casting to void is permitted Alternatives: static_cast , dynamic_cast etc usually sufficient auto i = (std::int16_t) 42; // Non-compliant auto z = std::int16_t(42); // Compliant (not a cast, constructor call) (void) i; // Compliant by exception
  • 17.
    Rule 8.2.5: reinterpret_castshall not be used Undefined behavior: Casting between unrelated types Almost equivalent to C-style casts which are not allowed reinterpret_cast<T*>(ptr) to cast a pointer to T* if: T is void , char , unsigned char , std::byte reinterpret_cast<T>(ptr) to convert a pointer to an integer T if: T is large enough to represent a std::uintptr_t std::int32_t i = 42; auto pI = reinterpret_cast<std::uint32_t>(i); // Non-compliant std::int32_t val = 32; auto pToVal = reinterpret_cast<std::byte*>(&val); // Compliant by exception
  • 18.
    Rule 9.6.1: Thegoto statement should not be used Why: Often leads to unmaintainable code, considered bad practice Advisory: But often treated as mandatory Required: goto -related rules, e.g. 9.6.3 allows only jumping to labels declared later in the function body std::int32_t i = 0; loop_start: if (i < 10) { std::cout << i++ << std::endl; goto loop_start; // Non-compliant for both 9.6.1 and 9.6.3 }
  • 19.
    Rule 7.11.2: Anarray passed as a function argument shall not decay to a pointer Bounds: Arrays as arguments decay to pointers, size info lost If code must work with different sizes, maintain dimensions C-style arrays: Bad idea, 11.3.1 (advisory) forbids C-style arrays Exception: Passing a string literal when expecting a const char* String literal guaranteed to end with 0 so length is inferred void processArray1(std::int32_t arr[42]); // Non-compliant, decays to pointer void processArray2(std::int32_t* arr); // Non-compliant, decays to pointer void processArray3(std::int32_t (&arr)[42]); // Compliant, enforces arr[42] template<std::size_t N> void processArray4(std::int32_t (&arr)[N]); // Compliant, array of N
  • 20.
    Rule 24.5.2: TheC++ Standard Library functions memcpy , memmove and memcmp from <cstring> shall not be used UB: If the source and destination overlap or not trivially copyable Incompatible: memcmp may compare objects not logically equal Floating point numbers, class objects due to padding and alignment, buffers that may contain uninitialized data struct Data { std::byte first{}; // Content of potential padding is unspecified std::uint64_t payload{}; }; if (memcmp(&data1, &data2, sizeof(Data)) == 0) { /* Non-compliant */ }
  • 21.
    Rule 21.2.1: Thelibrary functions atof , atoi , atol and atoll from <cstdlib> shall not be used UB: If the string cannot be converted to the expected type Modern alternatives: std::stoi , std::stod , std::from_chars int i = atoi("42"); // Non-compliant int j = std::stoi("42"); // Compliant std::int32_t res = 0; std::string_view str = "42"; auto [p, errc] = std::from_chars(str.data(), str.data() + str.size(), res); if (errc == std::errc()) { std::cout << "Parsed integer: " << res << 'n'; } else { std::cerr << "Error parsing integern"; }
  • 22.
    Rule 21.2.2: Thestring handling functions from <cstring> , <cstdlib> , <cwchar> and <cinttypes> shall not be used strcat , strcmp , strcpy , strlen , strtol etc UB: Out of bounds access and null-termination issues Modern & safer alternatives, e.g. std::string , std::string_view void populate(char* message, std::size_t messageSize) { const char* payload = "a message"; // Non-compliant calls to strlen and strcpy if ((strlen(payload) + 1U) < messageSize) { strcpy(message, payload); } } std::string_view payload = "a message"; // (Modern) Alternative to char* payload.size(); // Compliant alternative to strlen payload.copy(message, payload.size()); // Compliant alternative to strcpy
  • 23.
    Rule 21.2.3: Thelibrary function system from <cstdlib> shall not be used Security: Can execute arbitrary commands, might be exploited Portability: Implementation-defined behavior may vary Undefined behavior: Guidelines do not elaborate further Alternatives: Use APIs, formally deviate, posix_spawn or similar E.g. use std::filesystem for file operations std::system("ls ."); // Non-compliant for (const auto& entry: std::filesystem::directory_iterator(".")) { std::cout << entry.path() << std::endl; // Compliant }
  • 24.
    Rule 30.0.1: TheC Library input/output functions shall not be used gets , fopen , fclose , fread , fflush , printf , scanf etc Behavior: Undefined, unspecified and implementation-defined Alternatives: std::cin , std::ifstream , std::ofstream etc <fstream> compliant despite maybe using offending functions printf("Hello, World!n"); // Non-compliant std::cout << "Hello, World!" << std::endl; // Compliant
  • 25.
    Rule 21.10.1: Thefeatures of <cstdarg> shall not be used va_list , va_start , va_end , va_arg etc Why: Passing arguments via ... omits compile-time type checking UB: va_end not called after va_start being used UB: va_arg in multiple functions on the same va_list Alternatives: Variadic templates, functions with fixed arguments void doSomething(va_list args) { /* ... */ } // Non-compliant template<typename T, typename... Args> void doSomething(const T& first, const Args&... args) { /* ... */ } // OK
  • 26.
    Rule 24.5.1: Thecharacter handling functions from <cctype> and <cwctype> shall not be used isalpha , isdigit , isspace , toupper , tolower etc UB: When the character cannot be represented as unsigned char Alternatives: Use equivalents from <locale> Same as above but with an extra std::locale parameter std::isalpha('a'); // Non-compliant std::isdigit('1', std::locale{}); // Compliant
  • 27.
    Rule 6.7.2: Globalvariables shall not be used Non-constant variables in some global scope Non-constant static member variables UB: Due to data races when concurrent access is possible Unspecified: Order of (dynamic) initialization not guaranteed Unpredictable: Unexpected side effects, hard to track down // Some global scope std::int32_t currentSpeed{ getCurrentSpeed() }; // Non-compliant, non-const const std::int32_t prevSpeed{ currentSpeed }; // Non-compliant, dynamic init struct ComplexCar { ComplexCar(/* Some intricate initialization */); }; const ComplexCar car{}; // Non-compliant, dynamic initialization
  • 28.
    Rule 6.7.1: Localvariables shall not have static storage duration Temporal coupling: Data races that may lead to UB Global state: Hard to understand, maintain and test Destruction: Reverse order of creation, avoid dangling references Deviations: Occasionally straightforward, but otherwise tricky template<typename T> T& getSingleton() { static T instance{}; // Non-compliant, static storage duration return instance; }
  • 29.
    Rule 19.6.1: The#pragma directive and the _Pragma operator should not be used Why: Non-portable, implementation-specific behavior Advisory: Often treated as mandatory since it's easy to enforce // Foo.h #pragma once // Non-compliant #ifndef FOO_H // Compliant, portable alternative // ... #endif
  • 30.
    Rule 28.3.1: Predicatesshall not have persistent side effects Predicate: A function that (usually) returns a boolean value Compare , Predicate etc in STL Implementation-defined: Predicate may be copied Unspecified: The order of predicate invocations Unexpected: Side effects due to mutable internal state using Samples = std::array<int32_t, 10U>; int64_t countBadSamples(const Samples& s, int32_t& superBad) { return std::count_if(s.begin(), s.end(), [&superBad](int32_t sample) { if (sample < 100) { superBad++; } // Non-compliant return sample < 0; }); }
  • 31.
    Rule 18.5.2: Program-terminatingfunctions should not be used std::abort , std::exit , std::quick_exit , std::terminate etc Why: Destructors not called, environment left in bad state E.g. 3rd-party resources not released, files locked, etc Exception: assert is OK since it is a debugging tool Advisory: Often convenient to deviate if risks are understood void handleError() { std::cerr << "Fatal error occurred, terminating programn"; std::exit(EXIT_FAILURE); // Non-compliant }
  • 32.
    Rule 18.3.1: Thereshould be at least one exception handler to catch all otherwise unhandled exceptions Catch-all: catch(...) to handle all unhandled exceptions in main Why: Program terminates in an implementation-defined manner Advisory: Personally find it annoying, but easy to comply int main() { try { cool_company::Car car{}; } // Application code catch (const cool_company::NoFuelException& e) { return 1; } // Specific catch (...) { return 99; } // Catch-all for all other exceptions return 0; }
  • 33.
    Rule 5.13.5: Thelowercase form of L shall not be used as the first character in a literal suffix Why: l and 1 are visually similar, can lead to confusion How: Use uppercase L for long literals, e.g. 42L Literals: Unsigned literal numbers need suffixes (see 5.13.4 ) int64_t i = 42l; // Non-compliant int64_t j = 42L; // Compliant int64_t k = 42ul; // Compliant because `l` not the first character int64_t m = 42Ul; // Compliant but why not `UL`?
  • 34.
    Rule 8.2.10: Functionsshall not call themselves, either directly or indirectly Recursion: Not allowed, can lead to stack overflow Why: Hard to predict worst-case operation count Deviations: If you really must, document how risk is mitigated Exception: Generally OK for constexpr ("core constant") functions int32_t factorial(int32_t n) { if (n <= 1) return 1; // Base case return n * factorial(n - 1); // Non-compliant, recursion }
  • 35.
    Rule 9.4.1: Allif ... else if constructs shall be terminated with an else statement Why: Defensive programming, ensures all cases handled Empty else clause: May be used to indicate intentional handling Default case: 9.4.2 requires a default case in switch statements if (car.getSpeed() > 100) { /* OK */ } else if (car.getSpeed() < 50) { /* There must be an else clause */ } else { /* Compliant, handles all cases */ }
  • 36.
    Rule 10.2.1: Anenumeration shall be defined with an explicit underlying type Unscoped enums: The underlying type is implementation-defined Scoped enums: The underlying type is int by default Why: No implicit conversions, promotes predictability, portability Exception: When all enumerators use default values enum class Month { Jan = 1, Feb, Mar, Apr, May, Jun }; // Non-compliant enum class Day : int8_t { Sun = 0, Mon, Tue, Wed, Thu, Fri }; // Compliant enum class Color { R, G, B }; // Compliant by exception
  • 37.
    Rule 14.1.1: Non-staticdata members should be either all private or all public Why: Member functions allow better access control Robustness: Never accidentally break class invariants Advisory: Common practice, but rather "too opinionated" as a rule class Car { public: int32_t id{}; // Non-compliant, mixed access int32_t getSpeed() const { return speed; } private: int32_t speed{}; };
  • 38.
    Takeaways MISRA rules definea "safer subset" of C++ for critical systems BjarneStroustrup/profiles C++ safety, in context Avoids UB, unspecified or implementation-defined behavior Avoids common pitfalls and bad practices Overall a big improvement over MISRA C++:2008 Mostly in alignment with C++ Core Guidelines Deviations possible, but must be documented and justified General-purpose C++ projects may benefit from some rules