This paper supplies a design and implementation exploration for a more library-oriented approach to contracts.
The goal of the exploration is to look at an alternative model that is less compiler-dependent, more library-oriented, and by being especially the latter, more flexible and more user-extensible.
It may well have been the goal of P2900 and everything that came after it, including P3400, to keep the design(s) rigid and extensible only through committee action, with the goal of maintaining certain prime directives and principles.
This exploration quite intentionally chooses a completely different set of trade-offs, and provides user-extensibility, user-configurability and user-overrideability over all else.
This exploration is heavily based on ideas formulated in Bengt Gustafsson's paper P3968, but also builds on various other papers such as P4005 and P4009, attempting to further refine those papers and address the review feedback given for them. The paper also builds on various analyses and ruminations performed on P3400.
The goal of this approach is to provide a programmatic framework for expressing different needs for assertions, in different domains. There are various such needs that have already been communicated, such as being able to:
The framework also facilitates certain other desires that have been mentioned, such as being able to:
Some of these things are things that have been on the committee's agenda during the development of C++26 contracts. Some of them are possible implementation-defined mechanisms allowed by the standard. Some of them are facilities that may end up being on the committee's agenda to extend the C++26 contracts, or are already on that agenda.
This framework allows Just Doing them, in (3rd party) library code, which could later become standard library code.
Let's first look at what the compiler does for a C++26 contract:
In this approach, it will be:
To go straight to the point, the syntactic keying used here
is similar to P3400: void f(int x) pre<cco>(x >= 0);
and similarly for post and contract_assert.
The cco in pre<cco> stands
for Contract Control Object.
And that's what it is - an object that controls the semantics and the behavior of the contract.
Pay particular attention to "and the behavior". These control objects don't just compute semantics used by language-internal implementations of the standard semantics, they implement the semantics.
Right. Onto the actual building blocks.
We need
In addition, we need
We use a static-property information class:
class assertion_static_info { public: constexpr evaluation_semantic semantic() const noexcept; constexpr assertion_check_side side() const noexcept; constexpr bool is_virtual() const noexcept; constexpr bool overrides_virtual() const noexcept; };
The type of a contract control object has:
static constexpr bool is_ignored(assertion_static_info);static constexpr bool constify(assertion_static_info);These give us steps ii and iii.
For step v, the type of a contract control object also has:
constexpr void operator()(const assertion_context&);and assertion_context is
struct assertion_context { constexpr const char* comment() const noexcept; constexpr std::source_location location() const noexcept; constexpr evaluation_semantic semantic() const noexcept; constexpr assertion_kind kind() const noexcept; constexpr bool check(); private: void* __args; // exposition-only bool (*__check)(void*); // exposition-only };
For invoking the violation handler, there is a function
void invoke_violation_handler(assertion_kind kind, evaluation_semantic semantic, detection_mode mode, const char* comment, std::source_location loc);
This approach has been implemented in a fork of GCC, at https://github.com/villevoutilainen/gcc/tree/p4324.
The compiler is available on godbolt, as "X86-64 gcc (P4324 contracts)".
A pure-library version of quick_enforce; calls abort() on enforce, calls the violation handler on observe, ignores on ignore: Library quick_enforce on godbolt
A pure-library version of noexcept_enforce/noexcept_observe: Library noexcept_enforce/noexcept_observe on godbolt
Same example in the case where the handler throws: Library noexcept_enforce/noexcept_observe on godbolt, with a handler throw
An example of a contract that lets predicate exceptions through: A throwing predicate, exception passed through, on godbolt
Same example, but we catch the exception and call the violation handler: A throwing predicate, exception caught, on godbolt
An example of zero overhead (which can also be achieved with C++26 quick_enforce, but this shows it's attainable here too): Zero-overhead example
A contract that is always checked and always enforced: An always-on/enforced contract on godbolt
A contract that is ignored at run-time based on an environment variable: A run-time-ignored contract on godbolt
A contract that carries a custom diagnostic message: A contract with a custom message on godbolt
The facility integrated to the standard library, implementing the hardened standard library: Library hardening with P4324 on godbolt