Defensive programming with new C++ standards

Defensive programming is a form of defensive design intended to ensure the continuing function of a piece of software under unforeseen circumstances. Defensive programming practices are often used where high availability, safety or security is needed.

Defensive programming is an approach to improve software and source code, in terms of:

  • General quality – reducing the number of software bugs and problems.
  • Making the source code comprehensible – the source code should be readable and understandable so it is approved in a code audit.
  • Making the software behave in a predictable manner despite unexpected inputs or user actions.

The usual way to adopt  a defensive programming approach in C++  is the use of assert facility:

    void test( int *p ) {
        assert( p != 0 );
        if (p == 0)
            return;
        // use p.
    }

An assertion is code that’s used during development—usually a routine or macro—that allows a program to check itself as it runs. When an assertion is true, that means everything is operating as expected. When it’s false, that means it has detected an unexpected error in the code.

Assertions are especially useful in large, complicated programs and in high-reliability programs. They enable programmers to more quickly flush out mismatched interface assumptions, errors that creep in when code is modified, and so on.

An assertion usually takes two arguments: a boolean expression that describes the assumption that’s supposed to be true and a message to display if it isn’t.

What do the new C++ standards provide as mechanisms to do a defensive programming?

C++11 and static assert

C++11 introduces a new way to test assertions at compile-time, using the new keyword static_assert, this feature is very useful to add conditions to the template parameters, as shown in this template class from Folly source code:

c3

 

C++17 and the [[nodiscard]] attribute

Functions declared with [[nodiscard]] should not have their return values ignored by the caller. This can be useful if you want to ensure that callers check a return value.You can enforce the code contract for a function so that the caller won’t skip the returned value.

For example, if the return value of the function do_something is not used, the compiler will emit a warning.

[[nodiscard]] error do_something (thing&);


do_something(my_thing); // Warning: ignored return value

C++20 and contracts

This is the big improvements to the defensive programming from the new standards. It will provide many facilities and features to design by contract.

you can discover the proposal here.

Let’s first discover the terminology from the proposal of the contracts feature :

1. A precondition is a predicate that should hold upon entry into a function. It expresses a function's expectation on its arguments and/or the state of objects that may be used by the function. Preconditions are expressed by expectsattributes (7.6.10).

2. A postcondition is a predicate that should hold upon exit from a function. It expresses the conditions that a function should ensure for the return value and/or the state of objects that may be used by the function. Postconditions are expressed by ensures attributes (7.6.11).

3. An assertion is a predicate that should hold at its point in a function body. It expresses the conditions, on objects that accessible at its point in a body, that must be satisfied. Assertions are expressed by assert attributes (7.6.12).

4. Preconditions, postoconditions, and assertions are collectively called contracts. A contract shall have no observable effect in a correct program (a program where all contracts would be satisified, if they were evaluated).

5. Contract attributes are followed by a conditional-expression, which is a potentially evaluated expression (3.2). 

And here’s an example of the use of this new feature:


void push(int x, queue & q)
  [[expects: !q.full()]]
  [[ensures: !q.empty()]]
{
  //...
  [[assert: q.is_valid()]];
  //...
}

The C++20 standard will bring the missing features for the defensive programming adopters, no need to use an external library or framework to design by contract.