Contract assertions (since C++26)
Contract assertions allow the programmer to specify properties of the state of the program that are expected to hold at certain points during execution.
Contents |
[edit] Explanation
Contract assertions are introduced by function contract specifiers and contract_assert
statements. Each contract assertion has a predicate , which is an expression of type bool.
[edit] Evaluating contract assertions
An evaluation of a contract assertion uses one of the following evaluation semantics:
Evaluation semantic | Is a checking semantic | Is a terminating semantic |
---|---|---|
ignore | ||
observe | Yes | |
enforce | Yes | Yes |
quick-enforce | Yes | Yes |
It is implementation-defined which evaluation semantic is used for any given evaluation of a contract assertion. The evaluation semantics can differ for different evaluations of the same contract assertion, including evaluations during constant evaluation.
If the “ignore” semantic is used, the evaluation of a contract assertion has no effect.
If a checking semantic is used, the evaluation E
of a contract assertion determines the value of the predicate. It is unspecified whether the predicate is evaluated. If any of the following conditions is satisfied, a contract violation occurs:
- The value that would result from evaluating the predicate is false.
- The evaluation of the predicate exits via an exception.
- The evaluation of the predicate is performed in a context that is manifestly constant-evaluated and the predicate is not a core constant expression.
There is an observable checkpoint CP
that happens before E
such that
any other operation OP
that happens before A
also happens before CP
.
int num = 0; void f() pre((num++, false)); f(); // Increment of “num” might not occur, even if a checking semantic is used
[edit] Handling contract violations
If a contract violation occurs in a context that is manifestly constant-evaluated:
- If the evaluation semantic is “observe”, a diagnostic is produced.
- If the evaluation semantic is a terminating semantic, the program is ill-formed.
If a contract violation occurs in a context that is not manifestly constant-evaluated:
- If the evaluation semantic is “quick-enforce”, the program is contract-terminated.
- If the evaluation semantic is “enforce” or “observe”, the contract-violation handler is invoked with an lvalue referring to an object obj of type const std::contracts::contract_violation containing information about the contract violation.
- Storage for obj is allocated in an unspecified manner, but no global allocation function will be invoked.
- The lifetime of obj persists for the duration of the invocation of the contract-violation handler.
[edit] Contract-terminated programs
When the program is contract-terminated , it is implementation-defined (depending on context) whether
- std::terminate is called,
- std::abort is called, or
- execution is terminated (no further execution steps occur).
[edit] Contract-violation handler
The contract-violation handler of a program is a function named ::handle_contract_violation:
void handle_contract_violation( std::contracts::contract_violation ); |
(since C++26) (optionally noexcept) |
|
A definition of the contract-violation handler, called the default contract-violation handler , is provided by the implemenation (instead of a standard library header).
It is implementation-defined whether the contract-violation handler is replaceable. If the contract-violation handler is not replaceable, a declaration of a replacement function for the contract-violation handler is ill-formed, no diagnostic required.
When the contract-violation handler returns normally:
- If the evaluation semantic is “observe”, control flow continues normally after the point of evaluation of the contract assertion.
- If the evaluation semantic is “enforce”, the program is contract-terminated.
There is an observable checkpoint CP
that happens after the contract-violation handler returns normally such that any other operation OP
that happens after
the contract-violation handler returns also happens after CP
.
[edit] Handling exceptions from assertions
If the contract violation occurred because the evaluation of the predicate exited via an exception and the evaluation semantic is “observe” or “enforce”, the contract-violation handler is invoked from within an active implicit handler for that exception.
When the contract-violation handler returns normally:
- If the evaluation semantic is “observe”, the implicit handler is no longer considered active.
- If the evaluation semantic is “enforce”, the implicit handler remains active when contract termination occurs.
The current exception can be inspected or rethrown within the contract-violation handler using std::current_exception().
[edit] Evaluate in sequence
To evaluate in sequence a list R
of contract assertions:
S
such that all following conditions are satisfied:
- All elements of
R
are inS
. - Each element of
R
may be repeated an implementation-defined number of times withinS
. - If a contract assertion
A
precedes another contract assertionB
inR
, then the first occurrence ofA
precedes the first occurrence ofB
inS
.
S
such that, if a contract assertion A
precedes a contract assertion B
in S
, then the evaluation of A
is sequenced before the evaluation of B
.void f(int i) { contract_assert(i > 0); // #1 contract_assert(i < 10); // #2 // valid sequence of evaluations: #1 #2 (no repeat) // valid sequence of evaluations: #1 #1 #2 #2 (repeat in sequence) // valid sequence of evaluations: #1 #2 #1 #2 (repeat alternatively) // valid sequence of evaluations: #1 #2 #2 #1 (second occurences can switch order) // invalid sequence of evaluations: #2 #1 (first occurences cannot switch) }
[edit] Notes
The range and flexibility of available choices of evaluation semantics depends on the implementation, and need not allow all four evaluation semantics as possibilities.
Different evaluation semantics chosen for the same contract assertion in different translation units can result in violations of the one-definition rule when a contract assertion has side effects that alter the value produced by a constant expression:
constexpr int f(int i) { contract_assert((++const_cast<int&>(i), true)); return i; } inline void g() { int a[f(1)]; // size dependent on the evaluation semantic of contract_assert above }
If the value that would result from evaluating the predicate is true, no contract violation occurs and control flow continues normally after the point of evaluation of the contract assertion.
If the evaluation of the predicate exits by means of non-local jumps or terminating the program, no contract violation occurs either.
It is recommended by the C++ standard that the default contract-violation handler should produce diagnostic output that suitably formats the most relevant contents of the argument (rate-limited for potentially repeated violations of observed contract assertions), and then return normally.
Feature-test macro | Value | Std | Feature |
---|---|---|---|
__cpp_contracts |
202502L |
(C++26) | Contracts |
[edit] Keywords
[edit] Support status
C++26 feature |
Paper(s) |
GCC |
Clang |
MSVC |
Apple Clang |
EDG eccp |
Intel C++ |
Nvidia HPC C++ (ex PGI)* |
Nvidia nvcc |
Cray
|
---|---|---|---|---|---|---|---|---|---|---|
Contracts (FTM)* | P2900R14 |
[edit] See also
contract_assert statement (C++26)
|
verifies an internal condition during execution |
function contract specifiers (C++26) | specifies preconditions (pre) and postconditions (post) |