C++ Core Guidelines: A Short Detour to Contracts in C++20

Contents[Show]

My original plan was it to write in this post about the next rules to error handling. But I changed my plan to write about the future: contracts in C++20.

 Design by contract

By Fabuio - Own work, CC0, Link

Here are the rules I will skip.

Why did I change my plan? I did it for a few reasons.

The consequence of these points is quite simple. Contracts seem necessary for error handling; C++20 will presumably have contracts. Therefore, I am writing this post about contracts in C++20.

In case you want to have more details on contracts. This post is based on proposals P0380R1 and P0542R5.

First of all.

What is a contract?

A contract specifies in a precise and checkable way interfaces for software components. These software components are functions and methods that must fulfill preconditions, postconditions, and invariants. Here are the shortened definitions from the proposals.

  • A precondition: a predicate that is supposed to hold upon entry in a function. It is placed outside the function definition.
  • A postcondition: a predicate that is supposed to hold upon exit from the function. It is placed outside the function definition.
  • An assertion: a predicate that is supposed to hold at its point in the computation. It is placed inside the function definition.

The precondition and the postcondition are in C++20, placed outside the function definition, but the invariant is placed inside the function definition. A predicate is a function that returns a boolean.

 
Here is a first example:
 
int push(queue& q, int val) 
  [[ expects: !q.full() ]]
  [[ ensures !q.empty() ]]{
  ...
  [[assert: q.is_ok() ]]
... }

 

The attribute expects is a precondition, the attribute ensures is a postcondition, and the attribute assert is an assertion.

The contracts for the function push are that the queue is incomplete before adding an element that is not empty after adding, and the assertion q.is_ok() holds.

Preconditions and postconditions are part of the function interface. This means they can't access local members of a function or private or protected members of a class. In contrast, assertions are part of the implementation and can, therefore, access local members of a function of private or protected class members.

 

class X {
public:
    void f(int n)
        [[ expects: n<m ]]  // error; m is private
    {
        [[ assert: n<m ]];  // OK
        // ...
    }
private:
    int m;
};     

 

m is private and can not be part of a precondition.

By default, a violation of a contract terminates the program. This is not the whole story; let me give you more details.

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Be part of my mentoring programs:

 

 

 

 

Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

More Details

Here is the full syntax of the contract attributes: [[contract-attribute modifier: conditional-expression ]]

  • contract-attribute: expects, ensures, and assert
  • modifier: specifies the contract level or the enforcement of the contract; possible values are default, audit, and axiom
    • default: the cost of run-time checking should be small; it is the default modifier
    • audit: the cost of run-time checking is assumed to be large
    • axiom: the predicate is not checked at run-time
  • conditional-expression: the predicate of the contract

For the ensures attribute, there is an additional identifier available. [[ensures modifier identifier: conditional-expression ]]

The identifier lets you refer to the return value of the function.

int mul(int x, int y)
  [[expects: x > 0]]         // implicit default
  [[expects default: y > 0]]
  [[ensures audit res: res > 0]]{
  return x * y;
}

 

res as the identifier is, in this case, an arbitrary name. As shown in the example, you can use more contracts of the same kind.

Let me dive deeper into the modifiers and the handling of the contract violations.

Handling contract violations

A compilation has three assertion build levels:

  • off: no contracts are checked
  • default: default contracts are checked; this is the default
  • audit: default and audit contracts are checked

If a contract violation occurs - the predicate evaluates to false -, the violation handler is invoked. The violation handler is a function of type noexcept which takes a const std::contract_violation and returns a void. Because the function is noexcept, this means that std::terminate is called in case of a contract violation. A user can set a violation handler.

The class std::contract_violation gives information about the violation of the contract.

namespace std{ 
  class contract_violation{
  public:
    uint_least32_t line_number() const noexcept;
    string_view file_name() const noexcept;
    string_view function_name() const noexcept;
    string_view comment() const noexcept;
    string_view assertion_level() const noexcept;
  };
}

 

  • line_number: the line number of the contract violation
  • file_name: the filename of the contract violation
  • function_name: function name of the contract violation
  • comment: the predicate to the contract
  • assertion_level: assertion level to the contract

 There are a few rules to remember if you declare a contract.

Declaration of contracts

A contract can be placed on the declaration of a function. This includes declarations of virtual functions or function templates.

  • The contract declaration of a function must be identical. Any declaration different from the first one can omit the contract.
int f(int x) 
  [[expects: x>0]]
  [[ensures r: r>0]];

int f(int x); // OK. No contract.

int f(int x)
  [[expects: x>=0]]; // Error missing ensures and different expects condition

 

  • A contract can not be modified in an overriding function.

 

struct B{
  virtual void f(int x)[[expects: x > 0]];
  virtual void g(int x);
}

struct D: B{
  void f(int x)[[expects: x >= 0]];   // error
  void g(int x)[[expects: x != 0]];   // error
};

 

Both contract definitions of class D are erroneous. The contract of the method f differs from the one from B::f. The method D::g adds a contract to B::g.

Closing Thoughts

Impressed? Me too! I still can not imagine how fundamentally contracts will change how we write functions and think about interfaces and exception handling. Maybe Herb Sutter's thoughts on Sutter's Mill give you an idea because, for him, "contracts is the most impactful feature of C++20 so far, and arguably the most impactful feature we have added to C++ since C++11."

What's next?

With my next post, I will continue with a step back to the present time and write about the rules for exception handling.

Further Information

 Wow! Almost 200 readers participated in the vote for the next pdf bundle. Here are the winners.

  • German pdf bundle: Embedded: Performanz zählt
  • English pdf bundle: C++ Core Guidelines: Concurrency and Parallelism
Here are the details of the vote:

I need at least a week to proofread and prepare the pdf bundles

 

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.

 

Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

My special thanks to Tipi.build tipi.build logo

 

My special thanks to Take Up Code TakeUpCode 450 60

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

  • C++ - The Core Language
  • C++ - The Standard Library
  • C++ - Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

Comments   

+1 #1 Elmar 2022-11-14 21:49
It would be nice if you put up a disclaimer that contracts did not make it into c++ 20, and this information given here is outdated.
Quote
0 #2 Saifi Khan 2023-02-15 08:46
0. Contracts did not make it into C++20.

1. Eiffel is a great example of Design by Contract. In In C++ most of it would be assert(ions).

2. Herb's pattern has been to bring C#'isms to C++ viz contracts, in .. out .. inout modifiers in parameter passing. When all functions have contracts then 'noexcept' will be a no-op.

3. Enhancing the type system and bringing in dependent types will add value.
Quote

Stay Informed about my Mentoring

 

Mentoring

English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Course: The All-in-One Guide to C++20

Course: Master Software Design Patterns and Architecture in C++

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 3467

Yesterday 5555

Week 33675

Month 55349

All 12133558

Currently are 172 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments