Document no: P2032R0
Date: 2020-03-02
Authors:
Joshua Berne
Reply-to: jberne4@bloomberg.net
Audience: SG21
Prior to the formation of SG21 there were a number of different potential solutions proposed for contract based programming in the language. With the formation of SG21, a large number of use cases that any future contract solution might need to satisfy have been presented and polled on (see P1995 for the use cases and the initial poll results).
In order to better understand these use cases, a number of people expressed questions about how those use cases might be met with standard C++20 with no language based contracts, and how each of the prior language proposals might satisfy these use cases. We hope this exposition helps in understanding the use cases better, and helps to leverage the work that has gone before into determining our future direction for language based contracts.
Differences from first draft:
There are 4 primary language variations that we wish to evaluate for their merits at satisfying the SG21 use cases:
default
, audit
, and axiom
- with all levels either checked at
runtime or introducing undefined behavior if not checked and violated.default
, audit
, and axiom
) to specific behaviors (in lieu of
the combination of build level and continuation mode available in N4820).Rather than enumerate all possible variations that could be chosen, we will focus on comparing P1429 with no literal semantics and P1607 with no contract levels.
For each of these variations, we will assign a value in the range 0-100 for each use case with roughly the following meanings:
The goal of these numbers is to give a rough feeling for worth. In the future, we expect to see use cases given some weight (based partly on importance to the community and partly on distinctness from other use cases), and then combined with these numbers to give each solution a total value that can be used to understand how effecitvely is satisfies the needs of the community.
Note that the long term goal here is not to give a number to say "you must vote for this since it has a higher number" but rather to provide a value that can be used to see "this solution measurably improves support for use cases that I personally might not consider important but that others do see as a priority".
Additionally, a number of features could conceivably be built in a compatible way on top of any of these proposals using a system built with preprocessor macros (and such macros are the only viable way to build a system with no language support). Because of this, some scores are split into two numbers, the first being a judgement on how well the proposal satisfies the use case "out of the box", the second being how the proposal can be used with a macro facility above it to meet the specified use case.
# | Code | As A | In Order To | I Want To | N4842 C++20 |
N4820 Pre-Cologne Draft |
P1429 Semantic Level Control |
P1607 Literal Semantics |
---|---|---|---|---|---|---|---|---|
1 | dev.reason.knowl | Developer | Reason explicitly | Annotate my program anywhere in the code with my current understanding of its structure or execution | 25 | 90 | 90 | 90 |
Without a specific contract checking facility there are still ways to annotate
a program with information about program contracts, but those are limited to
either comments (with no validation or normative effect), the classic C
All of the contract proposals that were considered introduced the same syntax for expressing an expectation that a certain boolean predicate was true at specified points in a program. These satisfy most common use cases in the wild for contract predicates, although being only boolean predicates makes them fall short of being able to capture many more involved or not runtime checkable aspects of a function contract. These include the ability to express this within a function body as assertions like this:
More notably, annotations of expectations about execution could be added on function declarations as well -- something not possible with a solely language based solution -- like this: |
||||||||
2 | dev.reason.confidence | Developer | Reason explicitly | Express a spectrum of confidence in my annotations, from "unsure" and asking for validation, to "sure" and asking for some effect to be applied (eg. "maybe", "definitely", "assume" 'something') | 40 | 0 | 0 | 0 /40 |
3 | dev.reason.importance | Developer | Reason explicitly | Express a spectrum of importance of my annotations, from "critical" (eg. bring the system down) to "minor" (eg. lead to a slower fallback) | 40 | 0 | 0 | 0 /40 |
While BDE's bsls_assert does not provide confidence as something that can be used to control contract behavior, it is not hard to envision that such a thing could be built into a macro-based solution. Build-level-only solutions do not capture this at all (where N4820 and P1429
presented P1607's literal semantics do not provide this directly, but here again one could envision combining a macro-based solution to choose literal semantics that would capture confidence or importance and compute behavior based on that. |
||||||||
4 | dev.reason.cost | Developer | Reason explicitly | Express a spectrum of expected cost at compile or runtime of my annotations, from "unrunnable" to "expensive" to "cheap" | 40 | 80 | 80 | 0 /50 |
A macro-based solution would again be able to be built to use an expression of the cost of checking to determine how a contract will be treated, and BDE does exactly this with the following variations on how a contract can be checked:
The
Similar to the case for confidence and importance, any system that was built on top of literal semantics for P1607 with macros would be able to choose to capture cost as part of that control of contract behavior. |
||||||||
5 | dev.reason.behavior | Developer | Reason about executions | Have annotations affect the execution of my program in accordance with my expectations | 25 | 75 | 90 | 90 |
N4820 contracts allowed for contract checks to either introduce undefined behavior or be checked at runtime. P1429 and P1607 provide more flexibility by exposing the choice of 4 different semantics for contracts, with the primary difference being the granularity of that control. |
||||||||
6 | dev.reason.sideeffects | Developer | Reason about executions | Ensure annotations do not substantially change the meaning of my program whether enabled or disabled | 100 | 0 | 50 | 50 |
The primary meaning of this use case is that there should be a way to use the contract facility (on or off) without undefined behavior. This does not preclude the ability to introduce undefined behavior - but it needs to be optional. N4820 fails to provide this. A macro-based facility, barring bugs, should be able to easily provide this. P1429 and P1607 provide the ignore semantic with no violation-introduced undefiend behavior. Both P1429 and P1607 did not fix the fact that any side effects in a predicate are treated as undefined behavior. Note that P1670R0 was scheduled for Cologne which had one proposal for an attempt to address that. |
||||||||
7 | dev.reason.behaviorcontrol | Developer | Reason about executions | Have the effect of annotations on execution be user controllable (based on whatever aspects, if any, are available). | 65 | 50 | 55 | 25 /70 |
A macro-based solution can be built to use command line and in-code annotations to determine contract behavior. P1607 and a macro-only facility would both be able to provide any form of behavior control that users might wish to build, with more consistency to the behaviors that different options might give with P1607. N4820 and P1429 contract control is much more limited, primarily being only doable at the build level of granularity. |
||||||||
8 | dev.adapt | Developer | Adapt and progress with my project | Be able to easily change my confidence, importance, or other properties of my annotations over time | 40 | 25 | 50 | 25 /75 |
The build-level supporting options (N4820 and P1429) allow changing the one attribute they expose (cost). The macro-based solutions would have to build support for this, but can clearly put that in the sou rce code and thus make editing the attributes of an annotation easy to edit. An important thing to note is that safely changing annotation properties in released software is equivalent to introducing that contract freshly in any deployments that ignored the old 'level' and check the new 'level'. The ability to introduce the check at the new 'level' with a continuing semantic is essential to safely adding any checks to already deployed software. N4820 and P1429 both only allow the control of continuation at a very high level, thus forcing a user to turn continuation on for all existing checks in order to safely add checks that continue. P1607 provides the granularity to change this decision on a per-contract basis. |
||||||||
9 | dev.readable.syntax | Developer | Have readable annotations | Have annotations with a succinct and elegant syntax | 40 | 80 | 80 | 70 |
10 | dev.parsable | Developer | Interoperate with tools or persons | A syntax that can both be parsed and can be reasoned about semantically | 40 | 75 | 75 | 70 |
11 | cppdev.syntax.familiar | C++ Developer | Get up to speed | Have annotations use familiar syntax | 50 | 75 | 75 | 75 |
12 | cppdev.syntax.cpp | C++ Developer | Get up to speed | Have annotations use C++ syntax | 50 | 75 | 75 | 75 |
13 | cppdev.syntax.reuse | C++ Developer | Reuse code | Have annotations use my custom types or functions | 75 | 75 | 75 | 75 |
14 | cppdev.location | C++ Developer | Have a single source of truth | Use same source file for both code and annotations | 100 | 100 | 100 | 100 |
Obviously N4820 provides no explicit syntax and is limited to macros, but that still allows for using readable C++ in the annotations. P1607 likely requires macros to facilitate any global controls over behavior, so is a slightly worse syntactic option than what N4820 and P1429 provide (which have identical syntax). |
||||||||
15 | dev.readable.keywords | Developer | Have readable annotations | Have annotation keywords or names with intuitive, clear, and unambiguous meanings | 10 | 50 | 50 | 60 |
Given that it provides no new keywords for contracts, readability is going to be entirely dependent on what macro based facility is being used with N4842. For N4820 and P1429, the clarity of 'default' is fairly opaque, and a huge amount of reflector discussion and contention has revolved around the meaning and use of axiom, so they fair poorly here. P1607 removes the contentious keywords, but introduces new bespoke keywords that while clear are not necessarily intuitive to all, along with needing to build any other controls on top of those with macros, so it seems only slightly better in this regard. |
||||||||
16 | dev.readable.priority | Developer | Have readable annotations | Have my contract specification to be visually primary, and secondary information (syntax, hints, roles, levels, etc.) to not be distracting | 15 | 10 | 10 | 10 |
All of the attribute-based solutions put the meta-information first before the expression, counter to what this use case is asking for. A macro-based solution could choose to prioritize information differently, but would also be restricted in how the information gets passed into the macro. |
||||||||
17 | dev.tooling | Developer | Interoperate with tools or persons | Expose annotations to tools that might leverage them (eg. code linter, static analyzer, semantic prover, compiler sanitizer, binary analyzer, code reviewer, etc.) | 0 | 75 | 75 | 75 |
Entirely macro-based facilities would need to be ubiquitous enough for tooling vendors to choose to support them explicitly, along with those tooling vendors needing to work around the difficulties of managing pre-preprocessor based analysis. |
||||||||
18 | cppdev.syntax.macros | C++ Developer | Support modern features | Minimize use of macros | 0 | 50 | 60 | 50 /0 |
Obviously not very much can be done with the base language without macros. P1607 would require macros to support any form of global controls, so it gets the lowest rating for this use case, while P4820 would be usable for basic applications without macros, and P1429 would be slightly more flexible without needing to resort to macros. |
||||||||
19 | cppdev.modules | C++ Developer | Support modern features | Be interoperable with modules | 10 | 25 | 25 | 25 |
A purely macro based solution would have the worst interaction with modules. The language-based solutions were expected to have basic support for modules, since all would have landed in C++20, but no specific modules-related features were initially planned. |
||||||||
20 | cppdev.coroutines | C++ Developer | Support modern features | Be interoperable with coroutines | 25 | 25 | 25 | 25 |
21 | api.coroutines | C++ API Developer | Use coroutines | Define and check pre and post conditions as I would a regular function | 0 | 0 | 0 | 0 |
22 | api.coroutines.invariants | C++ API Developer | Use coroutines | Define and check invariants over all entry and exit points from a coroutine (to its awaiter or promise) | 0 | 0 | 0 | 0 |
All of the solutions would work perfectly fine within the body of a coroutine. Coroutine handling of contracts on inputs, outputs, and states when resuming a coroutine had not had any discussions or support planned in the proposals as they existed, so none of the proposals support coroutines any better than a purely macro-based solution. |
||||||||
23 | cppdev.concepts | C++ Developer | Support modern features | Be interoperable with concepts | 25 | 25 | 25 | 25 |
Nothing about the prior facilities seems to benefit or hinder the use of concepts. |
||||||||
24 | cppdev.existing.std | C++ Developer | Use the standard library in-contract | Codify existing exposition-only standard library requirements | 50 | 50 | 50 | 50 |
Many of the preconditions and postconditions of the standard library can be expressed as boolean expressions, and so are encodable using the facilities that were provided. |
||||||||
25 | cppdev.debugger | C++ Developer | Use Debugger | Have runtime able to launch a debugger from an annotation if necessary | 25 | 40 | 40 | 40 |
As much as any C++ program is able to do something like signal that a debugger
should be opened, a macro based solution could invoke the platform-specific
functions that would enable that (such as This is still not a standardized facility, so no solution completely satisfies this use case within the standard itself. |
||||||||
26 | cppdev.build.legacy | C++ Developer | Use existing build modes | Have annotations affect executions depending on my existing build modes (eg. Debug or Release modes in VS) | 25 | 0 | 0 | 0 /10 |
Those solutions that would use macros to do global control (N4842 and P1607) would be able to reference _NDEBUG or similar macros usually associated with "debug" and "release" builds as input to how they configure contracts. The standard, and the build levels specified in N4820 and P1429, are however completely disconnected from one another. |
||||||||
27 | cdev.contracts | C Developer | Write contracts on my functions | Specify contracts in a way standardizable as part of the C language | 10 | 0 | 0 | 0 |
28 | cdev.identifiers | C Developer | Write contracts on my functions | Use contracts with macro-safe keywords that are reserved C names (i.e., _Pre, _Post, _Assert, etc.) | 10 | 0 | 0 | 0 |
None of the contract specifications as a language feature would have been palatable to the C standards committee, since they all require being able to attach meaning to an attribute, as well as make use of new identifiers with special meaning (which are unfriendly to standardize in the macro-heavy C world.) A pure C++ macro based contract solution would, however, possibly be implementable in a fashion that is equally usable from C code. |
||||||||
29 | cdev.violationhandler | C Developer | Write contracts on my functions | Have a common violation handler for both violated C and C++ contracts | 50 | 25 | 25 | 25 |
With no C interoperability easily possible, and the violation handler specified
in terms of an opaque class type ( It is, however, likely that any C-based contract solution would be able to bridge to and from the C++ contract violation handler, since there is no particularly complex functionality in the violation handler or the violation object itself. |
||||||||
30 | cdev.ignorable | C Developer | Write contracts on my functions | Make all contract semantics optional (so as not to change WG14-N2385 6.7.11 p2) | 50 | 25 | 25 | 0 |
P1607's explicit semantics require that they behave in a specific way when
asked for. Similarly, there is an expectation with N4820 and P1429 that build
levels can be set to something other than Obviously a macro-based solution would be able to provide this kind of feature. |
||||||||
31 | ccppdev.interop | Mixed C/C++ Developer | Maintain mixed code base | Not lose contracts when crossing languages | 0 | 0 | 0 | 0 |
32 | cdev.cppinterop | Mixed C/C++ Developer | Write contracts on my functions | Expose my contracts to C++ developers through 'extern "C"' declarations of my functions | 0 | 0 | 0 | 0 |
A pure macro-based facility exposes no contracts to callers, regardless of what language the callers are calling from. The language-based facilities would not have readily supported standardizing by WG14. |
||||||||
33 | api.communicate.inputsoutputs | API Developer | Communicate my interface to users | Document the expected inputs and expected outputs on my interface | 25 | 75 | 75 | 75 |
34 | api.establish.check | API Developer | Establish a contract | Have validation inform me which output values are unexpected or invalid | 25 | 75 | 75 | 75 |
35 | api.establish.values | API Developer | Establish a contract | Have validation inform user which input values are unexpected or invalid | 25 | 75 | 75 | 75 |
36 | api.establish.preconditions | API Developer | Establish a contract | Have contracts specify their pre-conditions as logical predicates | 25 | 75 | 75 | 75 |
37 | api.establish.postconditions | API Developer | Establish a contract | Have contracts specify their post-conditions as logical predicates | 25 | 75 | 75 | 75 |
Precondition and postcondition checks within a function do something to document inputs and outputs, as do comments that are readily available without a language feature. The language proposals all included the common feature of |
||||||||
38 | api.establish.validate_invariants | API Developer | Establish a contract | Have validation inform me which class invariants are violated | 5 | 5 | 5 | 5 |
39 | api.establish.invariants | API Developer | Establish a contract | Have contracts specify their class invariants as logical predicates | 5 | 5 | 5 | 5 |
40 | cppapi.invariants | C++ API Developer | Write classes | Declare class invariants that all of my public functions need to maintain | 0 | 0 | 0 | 0 |
Neither a macro based facility or the language proposals allowed for specifying at a class level invariants that would be automatically checked (barring manually adding class invariants as pre and post conditions on every single function.) |
||||||||
41 | api.express.values | API Developer | Express predicates | Make reference to either the values of my inputs, or other in-scope identifiers | 75 | 75 | 75 | 75 |
42 | api.establish.changedvalues | API Developer | Establish a contract | Make reference to the before and after values of in-out variables (ie. passed by pointer or reference) in post-conditions | 40 | 0 | 0 | 0 |
43 | api.establish.changedmembers | API Developer | Establish a contract | Make reference to the before and after values of mutable class members (eg. new_size = old_size+1 after push_back) in post-conditions | 40 | 0 | 0 | 0 |
44 | api.establish.changedstate | API Developer | Establish a contract | Make reference to the before and after values of global state (eg., global >= old(global) + 1) in post-conditions | 40 | 0 | 0 | 0 |
A macro based facility can similarly expose macros to control the enablement of arbitrary code at arbitrary scopes, and so enables storing values between calling time and return time manually. The language-based facilities allow for referencing inputs and outputs, with the caveat that inputs were only referencable in outputs if they were not changed. The language-based facilities explicitly prevented any form of making additional code execute if contracts were enabled, and thus removed the ability to store data between the start and end of a function call. |
||||||||
45 | api.extend.exceptionsafety | API Developer | Extend contractual aspects | Annotate operations as being exception safe | 0 | 0 | 0 | 0 |
46 | api.extend.threadsafety | API Developer | Extend contractual aspects | Annotate operations as being thread safe | 0 | 0 | 0 | 0 |
47 | api.extend.atomicity | API Developer | Extend contractual aspects | Annotate operations as being atomic (ie. all or no changes become visible) | 0 | 0 | 0 | 0 |
48 | api.extend.realtime | API Developer | Extend contractual aspects | Annotate operations as real-time (ie. guaranteed to complete within a time frame) | 0 | 0 | 0 | 0 |
49 | api.extend.determinism | API Developer | Extend contractual aspects | Annotate operations as being deterministic (ie. same outputs for same inputs) | 0 | 0 | 0 | 0 |
50 | api.extend.purity | API Developer | Extend contractual aspects | Annotate operations as functionally pure (ie. no side effects) | 0 | 0 | 0 | 0 |
51 | api.extend.sideeffects | API Developer | Extend contractual aspects | Annotate operations as having global side effects (ie. write to singleton, file, network, or database) | 0 | 0 | 0 | 0 |
52 | api.extend.complexity | API Developer | Extend contractual aspects | Annotate algorithmic complexity | 0 | 0 | 0 | 0 |
No codification of these features is baked into the language itself or added by the previous proposals. |
||||||||
53 | api.express.runnability | API Developer | Express unrunnable contracts | Be able to use a predicate that is not evaluated at runtime, because it might be unsafe to run or have stateful side effects | 0 | 0 | 75 | 75 |
54 | api.express.undefined | API Developer | Express unrunnable contracts | Be able to use a predicate that doesn't have a definition, because it hasn't been written yet, or is infeasible to run | 0 | 0 | 75 | 75 |
55 | api.express.uncheckable | API Developer | Express uncheckable contracts | Be able to use a predicate that is not evaluated, because it is simply a semantic placeholder for a tool | 0 | 0 | 75 | 75 |
56 | api.express.unimplementable | API Developer | Express uncheckable contracts | Be able to use a predicate that cannot have a complete definition, because it is inexpressible in the language | 0 | 0 | 75 | 75 |
There is no current in-language way to express predicates that will not be executed at runtime. N4820 had unchecked contracts leave their evaluation as unspecified, thus making any predicate that has UB or has side effects potentially evaluated (specifically, the wording said "it is unspecified if the predicate of an unchecked contract is evaluated".) P1429 and P1607's assume semantic, however, attempted to capture the original intent of the contract proposals and make unchecked contracts not be evaluated, thus enabling the use of predicates that are not themselves safe to ever evaluate. |
||||||||
57 | api.establish.responsibility | API Developer | Establish responsibility boundaries | Inform users which errors are the responsibility of the caller, and which are the callee | 0 | 50 | 50 | 50 |
58 | api.resp.preassert | API Developer | Establish responsibility boundaries | Annotate assertions inside function bodies that indirectly test preconditions (such as malformed data discovered while performing the algorithm) should be reported to the caller as precondition failures | 0 | 0 | 0 | 0 |
Macros within a function are unable to readily gather information about what called the function, thus making it infeasible to identify calling code in a precondition violation in a wholely macro-based facility. N4820 made it possible to change that for |
||||||||
59 | api.contract.interface | API Developer | Have contract as part of my interface | Declare contract when I declare the function | 0 | 100 | 100 | 100 |
This is not doable with macros, but was inherent in the proposed contract solutions. |
||||||||
60 | api.contract.private | API Developer | Keep my user interfaces clean and narrow | Be able to access private implementation details of the class so I don't have to widen public interface to declare predicates | 100 | 100 | 100 | 100 |
A macro within a function has full access to private data members of that function's class, as do contracts in all of the language contract proposals. |
||||||||
61 | api.contract.redeclaration | API Developer | Keep my public interfaces clean and concise | Place function contract conditions on any declaration (e.g., on redeclarations at the bottom of the header, or on the definition in an implementation file, where they are less distracting). | 0 | 0 | 0 | 0 |
A macro-based facility obviously has no contracts on any declarations. The language based proposals did not allow for contracts on any declaration except the first one. Note that P1320R1 was going to be presented in cologne and sought to alter this state. |
||||||||
62 | api.contract.errorhandling | API Developer | Move contract violation out of error handling | Replace uses of error handling to express contract violation (eg. operator[](size_t n) noexcept [[pre: n < size()]] instead of throwing) | 50 | 50 | 50 | 50 |
This is a design decision when defining an API that could be readily accomplished with any contract facility. |
||||||||
63 | cppapi.class.preconditions | C++ API Developer | Maintain a class hierarchy | Ensure overriding methods have same or wider preconditions (see: Liskov substitution principle) | 0 | 40 | 40 | 40 |
64 | cppapi.class.postconditions | C++ API Developer | Maintain a class hierarchy | Ensure overriding functions meet their base class postconditions when their base class preconditions are met (see: Liskov substitution principle) | 0 | 40 | 40 | 40 |
65 | cppapi.class.variability | C++ API Developer | Maintain a class hierarchy. | Allow overriding functions to have narrower preconditions/wider postconditions if I want to | 0 | 0 | 0 | 0 |
A wholely macro based facility has no way to automatically integrate with function overrides. The language proposals all required that virtual functions have exactly the same preconditions and postconditions, and so allowed for a subset of what would be Liskov substitutable. |
||||||||
66 | api.class.publicinterface | C++ API Developer | Express public class invariants | Express a restriction on the public interface of a type that all callers of the type can depend upon: can mention only public members, and is checked on entry and exit from this type's code | 0 | 0 | 0 | 0 |
67 | api.class.publicinvariants | C++ API Developer | Express public class invariants | Check invariants before and after every public method (when called from outside the type, not when one member function calls another) | 0 | 0 | 0 | 0 |
68 | api.class.publiccalls | C++ API Developer | Express public class invariants | Check invariants before and after calling functions that are not part of this type (including virtual calls) | 0 | 0 | 0 | 0 |
69 | api.class.baseinterface | C++ API Developer | Express base class invariants | Express a restriction on the protected interface of a type that derived types can depend upon: can mention only protected and public members, and is checked on entry and exit from this type's code | 0 | 0 | 0 | 0 |
70 | api.class.baseinvariants | C++ API Developer | Express base class invariants | Check invariants on entry and exit of every protected method (when called from the derived type, not when one base member function calls another) | 0 | 0 | 0 | 0 |
71 | api.class.basecalls | C++ API Developer | Express base class invariants | Check invariants before and after every call to a virtual function (when calling to the derived type) | 0 | 0 | 0 | 0 |
72 | api.class.privateinterface | C++ API Developer | Express private class invariants | Express an internal restriction on the private implementation of a type, can mention any member, and is checked on entry and exit from this type's code | 0 | 0 | 0 | 0 |
73 | api.class.privateinvariants | C++ API Developer | Express private class invariants | Check invariants on entry and exit of every public method (when called from outside the type, not when one member function calls another) | 0 | 0 | 0 | 0 |
74 | api.class.privatecalls | C++ API Developer | Express private class invariants | Check invariants before and after calling functions that are not part of this type (including virtual calls) | 0 | 0 | 0 | 0 |
A macro-based facility is not part of the declared interface to any class, but only part of the implementation. The language proposals were limited to functions but not types, and always had private access. |
||||||||
75 | api.class.testing | C++ API Developer | Test my classes | For every member or friend function in my class, run my unit test framework with checking enabled for every assertion at the point where it is written, and check every postcondition at every non-exceptional exit, and test my class invariants on entry and exit from this type's code | 0 | 0 | 0 | 0 |
This level of control is not providable for macros or the language proposals. |
||||||||
76 | cppapi.contracts.async | C++ API Developer | Enforce contracts in async code | Express contracts on callbacks such as std::function, function pointers, or references to functions, lambdas, or function objects | 0 | 0 | 0 | 0 |
This would require contracts be part of a function type. Macros are not even part of a function declaration, let alone its type, and the previous language proposals did not choose to make contracts part of a function type either. |
||||||||
77 | cppapi.contracts.exception | C++ API Developer | Enforce contracts in exception safe code | Express contracts on exceptional exit | 0 | 0 | 0 | 0 |
78 | cppapi.variadic | C++ API Developer | Use contracts with variadic templates | Allow predicate (fold) expansion | 0 | 0 | 0 | 0 |
Neither of these are viable on the. |
||||||||
79 | int.conform.violation | Integration Developer | Conform to a contract | Be informed any time an interface's contract is violated | 25 | 50 | 50 | 50 |
80 | int.conform.postconditions | Integration Developer | Conform to a contract | Verify results from a call are expected output values | 25 | 50 | 50 | 50 |
This is the purpose of a checked contract facility, though the granularity of control to limit to or at least include a particular (or all) interface is not there for all proposals. |
||||||||
81 | int.build.headeronly | Integration Developer | Build multiple libraries | Use contract-enabled header-only libraries | 50 | 100 | 100 | 100 |
82 | cpplib.headeronly | C++ Library Developer | Use templates | Be able to ship header only library | 50 | 100 | 100 | 100 |
A macro based facility is likely to depend on having a supporting library exist to make customization and violation handling available. Nothing about the proposed solutions required this (or, as a language facility, a library got the supporting functionality needed for free from the compiler's runtime environment). |
||||||||
83 | int.build.binaries | Integration Developer | Build multiple libraries | Use contract-enabled binary libraries | 100 | 100 | 100 | 100 |
84 | int.build.binarycounts | Integration Developer | Build multiple libraries | Only be required to manage a small, common set of build/link configurations | 75 | 40 | 25 | 75 |
Policies on how many different builds are managed and deployed are entirely up to users when the facility's controls are all done through macros (either entirely in N4842 or on top of literal semantics in P1607). N4820 includes 5 distinct build configurations that might be desired by clients of a library, and P1429 increases that number to 32, though most are only interesting in special cases and it is likely that individual vendors would find a small set that are of actual interest to their clients. |
||||||||
85 | int.build.control | Integration Developer | Debug multiple libraries | Enable checks only within a selected library | 50 | 35 | 35 | 50 |
86 | int.build.control2 | Integration Developer | Debug multiple libraries | Enable checks on multiple libraries simultaneously | 50 | 35 | 35 | 50 |
The build levels are global, with conditional (and potentially no) support for using mixed build levels across different libraries. Macro based solutions are viable as long as any individual contract always has the same meaning in different translation units (without ODR violations). |
||||||||
87 | int.debug.callsites | Integration Developer | Debug multiple call sites | Enable checks only on selected call sites | 0 | 25 | 25 | 0 |
None of the solutions allowed for a function to be called with different checking levels without violating the ODR. |
||||||||
88 | int.violations.information | Integration Developer | Correct failed checks | Be informed what check failed, when, where, and how | 75 | 100 | 100 | 100 |
89 | int.violations.transmit | Integration Developer | Correct failed checks | Transmit check failure information in environment-specific ways (logs, email, special hardware traps, popup windows, blazing sirens, etc). | 50 | 90 | 90 | 90 |
90 | int.violations.custom | Integration Developer | Correct failed checks | Install custom violation handler where I can inject custom logic to trap errors | 50 | 90 | 90 | 90 |
91 | int.violations.common | Integration Developer | Unify violation handling | Be able to override how library violations are handled in the combined software to point into my handling code | 50 | 90 | 90 | 90 |
All of the facilities support checking contracts at runtime and being informed of details of the violation. The language proposals included a pluggable violation handler, although compiler vendors were allowed to not make that customizable. A pure macro-based facility suffers from needing to do this for all of the contract facilities that might be defined and used within a given fully assembled program. |
||||||||
92 | int.violations.override | Integration Developer | Be independent of build environment | Be able to define and override violation handler via source code | 15 | 0 | 0 | 0 |
This might be doable with a fair bit of complexity in a macro-based facility, and there was no support for this in the language proposals. |
||||||||
93 | int.build.minimize | Integration Developer | Minimize checking overhead | Disable library postconditions, asserts, and invariants, without disabling library preconditions (assuming the library is tested and stable and my code is not) | 25 | 0 | 0 | 25 |
94 | int.control.build | Integrated Software Provider | Ensure the combined software is correct | At build time, turn on and off what checking happens. | 25 | 0 | 0 | 25 |
95 | int.control.runtime | Integrated Software Provider | At runtime, control what checking happens. | Turn checks on at run time | 25 | 0 | 0 | 0 |
96 | int.conrol.subsets.build | Integrated Software Provider | Ensure the combined software is correct | Turn on any subset of individual (call site) checks on at build time | 25 | 0 | 0 | 25 |
97 | int.control.subsets.runtime | Integrated Software Provider | Ensure the combined software is correct | Turn on any subset of individual (call site) checks on at run time | 25 | 0 | 0 | 25 |
98 | int.control.subsets | Integrated Software Provider | Ensure individual features are correct | Have a way to audit (named or semantic) subsets of checks for various deployments | 25 | 0 | 0 | 25 |
99 | int.testing.control | Integrated Software Provider | Define "Code Under Test" | Selectively enable checking for a set of functions which could name either an individual function or an overload set | 15 | 0 | 0 | 15 |
100 | int.testing.controltypes | Integrated Software Provider | Define "Code Under Test" | Selectively enable checking for a set of types and all their members | 15 | 0 | 0 | 15 |
101 | int.testing.transitivity | Integrated Software Provider | Define "Code Under Test" | Selectively enable checking for a set of types and all their transitively nested types and members | 5 | 0 | 0 | 5 |
102 | int.testing.modules | Integrated Software Provider | Define "Code Under Test" | Selectively enable checking for a translation unit or module and all (non transitive) types and functions within | 5 | 0 | 0 | 5 |
Different forms of build and runtime control could be built into the macro-based facilities with varying levels of difficulty. N4820 and P1429 provided only a global level of control, limiting greatly the ability to control contract enablement at finer granularities. Implementing some of these forms of granularity in the preprocessor might, however, be very complicated. |
||||||||
103 | int.consistency | Integrated Software Provider | Ensure the combined software is correct | Verify all annotations are globally consistent when integrated | 0 | 0 | 0 | 0 |
There is no support for this form of checking on a macro-based facility, or from the language proposals. |
||||||||
104 | int.build.common | Integrated Software Provider | Manage binary delivery | Be able to use the same executable regardless of contract enforcement mode | 15 | 0 | 0 | 0 |
A C++20 based facility could determine checking based on runtime configuration instead of only compile-time configuration. The language proposals did not allow for that. |
||||||||
105 | int.build.unchecked | Integrated Software Provider | Test final deliverable | Turn off build time checking to remove checking overhead | 40 | 50 | 50 | 40 |
106 | int.runtime.unchecked | Integrated Software Provider | Test final deliverable | Turn off run time checking to remove checking overhead | 50 | 50 | 50 | 50 |
Macros and the language proposals had minimal concepts of "build time checking", but they do all enable a global ability to turn off such checking. That same global ability can be used to turn off runtime checking. |
||||||||
107 | int.build.optimize | Integrated Software Provider | Test final deliverable | Turn on run time optimization to leverage annotation assumptions | 25 | 50 | 75 | 75 |
108 | pdev.speed | Performance Sensitive Developer | Enable better performance | Annotate my code with assumptions, likelihoods, or reachability information that a tool might not be able to deduce, but that I would be confident of | 25 | 50 | 75 | 75 |
109 | pdev.morespeed | Performance Sensitive Developer | Enable better performance | Be able to give statically-unprovable facts to current and novel optimizers in terms of semantics my program does not depend-on but optimizers can't figure out | 25 | 50 | 75 | 75 |
110 | pdev.footgun | Performance Sensitive Developer | Enable better performance | Accept responsibility for a malformed program that might result from eventually false information given by my annotations | 25 | 50 | 75 | 75 |
A macro based facility would be able to enable optimizations, but that is likely
going to have limited direct support from most compilers. (Generally, this would
leverage either N4820 made optimizations enabled for ALL unchecked contracts, while P1429 and P1607 gave mechanisms to opt into that in varying ways. |
||||||||
111 | cpplib.insulation | C++ Library Developer | Control the tradeoff between need for client recompilation and contract condition visibility | Insulate contract conditions with the function definition, or insulate only the definition while putting contract conditions on a redeclaration - visible to static analysis tools in all TUs. | 25 | 50 | 50 | 50 |
Macro based contract checks will always be in the function body and thus fully insulated from clients. The contract annotations of the language proposals could be equivalently put
in function bodies with |
||||||||
112 | lib.maintenance.noconfig | Library Provider | Simplify maintenance | Not require extra build steps to be documented | 25 | 25 | 25 | 25 |
113 | lib.integration.noconfig | Library Provider | Support successful integration | Not require extra build steps to be learned or performed | 25 | 25 | 25 | 25 |
Any contract facility that allows control without source code manipulation will require some build time control, either through compiler flags or macros, though all should have been usable with some default behavior if no explicit choices were made at build time. |
||||||||
114 | lib.maintenance.nowhining | Library Provider | Simplify maintenance | Not have users complain about my product due to modifications of annotations resulting from their build configuration | 75 | 0 | 0 | 75 |
115 | lib.integration.nowhining | Library Provider | Support successful integration | Not have my users accidentally modify my careful annotations | 75 | 0 | 0 | 75 |
A macro based facility could provide concrete semantics similar to P1607, thus making contracts that cannot have their behavior changed from the command line. N4820 and P1429 contracts are always subject to build modes, leaving no way to enforce that a particular behavior is applied to a given contract annotation. |
||||||||
116 | arch.nomacros | Technical Architect | Maintain quality of code base | Express assertions in a way that does not rely on C macros (i.e., there is no valid technical reason for a programmer not to use the new way, including space, time, tooling, and usability/complexity reasons, compared to C's assert macro) | 0 | 50 | 50 | 50 |
Contract checks without a language facility are not feasibly doable without macros. All of the language proposals require macros in some form or other to satisfy many of the use cases in this document, but still provide a basic contract checking facility with no macros used at all. |
||||||||
117 | arch.complete | Technical Architect | Have a consistent and holistic contracts facility | Specify preconditions/postconditions/assertions/invariants that express my expectations about the expected valid state of my program in the form of compilable boolean expressions, that can be checked statically or dynamically (as opposed to disjointed state where these features are factored into bits) | 0 | 75 | 75 | 75 |
Obviously the language does not have this now, and the language proposals all attempted to provide this. |
||||||||
118 | hardware.performance | Hardware Architect | Improve system-level performance | Be able to design new hardware + optimizations, carefully dovetailed into one another, that depend on statically-unprovable facts being annotated in the code | 0 | 25 | 75 | 75 |
N4820's inability to have contracts that are never executed prevents this from being leveraged for many novel features. P1429 and P1607's assume semantic fixed this. |
||||||||
119 | sdev.bestpractices | Senior Developer | Set an example | Demonstrate best practice in defensive programming | 50 | 25 | 50 | 50 |
Any basic contract checking facility can be used to implement defensive programming. N4820's implicit assumption of any unchecked contract, however, is unlikely to ever be viewable as a best practice to use by anyone. |
||||||||
120 | sdev.quality | Senior Developer | Enforce code quality | Discourage reliance on observable out-of-contract behavior by causing check failure to hard stop program or build | 50 | 50 | 50 | 50 |
This behavior is available in any of the facilities, though not as strongly enforced when a user has control over the violation handler. |
||||||||
121 | sdev.maturity | Senior Developer | Enforce mature, finalized contracts | Disable continuation on violation of stable and correct individual contracts | 50 | 25 | 25 | 75 |
122 | sdev.control | Senior Developer | Enforce mature, finalized contracts | Disable remapping of semantics on stable and correct individual contracts | 50 | 25 | 25 | 75 |
The global controls of N4820 and P1429 do not allow for control over an individual contract's behavior (continuing or not, checked or not) based on the maturity of that specific contract. |
||||||||
123 | jdev.understand.contracts | Junior Developer | Understand the API | A uniform, fluent description of expected input values, expected output values, side effects, and all logical pre and post conditions | 0 | 75 | 75 | 75 |
124 | jdev.understand.violations | Junior Developer | Understand the API | Be informed when my usage is out of contract | 50 | 75 | 75 | 75 |
125 | jdev.understand.buildfailures | Junior Developer | Understand the program | Know why my software is not building | 50 | 75 | 75 | 75 |
126 | jdev.understand.aborting | Junior Developer | Understand the program | Know why my software is aborting | 50 | 75 | 75 | 75 |
127 | jdev.understand.omniscience | Junior Developer | Understand the program | Know why my software is out of contract | 50 | 75 | 75 | 75 |
128 | jdev.understand.buildviolation | Junior Developer | Understand the program | Know that my program or build was halted due to contract violation | 50 | 75 | 75 | 75 |
129 | jdev.bestpractices | Junior Developer | Improve my code | Learn about software best practices by example | 50 | 75 | 75 | 75 |
Without a language feature contract descriptions are library-specific and not uniform. With it, the only non-uniformity comes in when libraries build extra infrastructure on top of the language-provided facility. A library and a language-based facility will, however, be able to provide a user understandable details of why a contract violation might have made a program abort or fail to compile. |
||||||||
130 | jdev.understand.all | Junior Developer | Understand the facility | Be able to build a program with contracts after reasonably short tutorial | 50 | 75 | 75 | 75 |
Whether with a library or any of the proposed language features, simple contract use remains simple. |
||||||||
131 | jdev.understand.keywords | Junior Developer | Understand the facility | Have keywords with precise and unambiguous meanings | 0 | 25 | 25 | 75 |
Without a language feature there are no new keywords. N4820 and P1429 include
The keywords provided by P1607 are all very precisely defined. |
||||||||
132 | adev.fast | Agile Developer | Iterate quickly | Be able to write and modify contracts quickly without heavy boiler plate or up front cost | 0 | 75 | 75 | 75 |
The general use of contracts once they are a language feature is quick to get started on. Without a language feature, getting started requires acquiring or implementing a library to provide the feature. |
||||||||
133 | adev.evolve | Agile Developer | Safeguard evolving code | Assert against conditions I am aware of but not finished handling fully | 20 | 0 | 0 | 75 |
Without a language-based facility the only way to enter a not-yet-implemented contract is as a comment, which is of limited utility. P1607's ignore and assume semantics both allow for referencing undefined functions, and thus enable writing a planned contract while preserving writing the implementation of that check for a future sprint. N4820 provides no way to get that semantic for a contract annotation, and while P1429's assume semantic would, there is no way in code to write such a contract. |
||||||||
134 | bdev.confidentiality | Business Developer | Maintain confidentiality | Not expose diagnostic information (source location, expressions, etc.) in the software I deliver to clients, even when I choose to have contracts enforced in the software I deliver | 25 | 0 | 0 | 0 |
A macro-based library could choose to forgo including this information, or provide flags to control that. None of the language features proposed include that ability. |
||||||||
135 | pdev.safety.isolation | Performance Sensitive Developer | Have safety critical paths | Isolate safety checks from performance annotations | 25 | 0 | 0 | 0 |
A macro-based library could provide this distinction. None of the language proposals include this, and importantly all of them allow for a contract to be assumed and thus subvert any future "safety checks" that attempt to handle out-of-contract behavior more elegantly. |
||||||||
136 | pdev.safety.critical | Performance Sensitive Developer | Have safety critical paths | Retain checking even when optimizing with performance annotations | 25 | 0 | 15 | 20 |
N4820 provides no way to turn checking off locally without bringing in assumption. P1429 at least provides a way to build an application without assumption of checks, and macro-based solutions on top of P1607 or a fully-macro based solution would be able to allow for this kind of distinction with some effort. |
||||||||
137 | qdev.checkall | Quality Sensitive Developer | Enable full checking | Ensure all checks (pre, post, assert, invariant) are enabled | 25 | 100 | 100 | 75 |
138 | qdev.fuzz.testing | Quality Sensitive Developer | Catch unexpected failure modes | Log all predicate failure during fuzz testing | 25 | 100 | 100 | 75 |
A disparate set of macro-based libraries makes it hard to turn on all checking. N4820 and P1429 allow for this to be done very easily with build modes. P1607 allows for code to subvert this kind of control by providing explicit semantics for a particular contract that cannot be externally altered. |
||||||||
139 | qdev.correctness | Quality Sensitive Developer | Validate correctness | Signify the predicates that should be verified by an analysis tool | 25 | 50 | 50 | 25 |
140 | qdev.tooling | Quality Sensitive Developer | Manage multiple tools | Signify subset of individual annotations to be consumed by a specific kind of verification tool | 25 | 50 | 50 | 25 |
141 | qdev.tooling.control | Quality Sensitive Developer | Manage multiple tools | Signify subset of individual annotations to be consumed by a specific instance of verification tool | 25 | 0 | 0 | 25 |
It can be argued that the primary intention of A macro-based facility could integrate with a static analysis tool for this purpose, but would require tools to choose to support it. |
||||||||
142 | qdev.tooling.undefined | Quality Sensitive Developer | Manage multiple tools | Use predicates that may not be understood by all instances of verification | 0 | 0 | 25 | 25 |
143 | qdev.tooling.undefinedkinds | Quality Sensitive Developer | Manage multiple tools | Use predicates that may not be understood by all kinds of verification | 0 | 0 | 25 | 25 |
Only P1429 and P1607 allow predicates to remain undefined if not referenced and not checked at runtime. |
||||||||
144 | qdev.tooling.behavior | Quality Sensitive Developer | Manage multiple tools | Integrate the results of that static checker into how my program behaves in different ways: assume proven predicates, make unprovable predicates ill- formed, etc. | 25 | 0 | 0 | 25 |
P1607 or a wholely macro-based facility provide the only direct way to integrate the results of analysis into specific behaviors for contract checks. |
||||||||
145 | qdev.testing | Quality Sensitive Developer | Unit test predicates | Override failure handler to trigger test failure instead of termination | 25 | 50 | 50 | 50 |
All of the proposals included a conditionally supported custom violation handler which could be used to test that checks are actually checked at runtime in a unit test (at least, for noexcept functions). A macro-based facility can accomplish this as well (and this is the foundation of all negative testing in BDE, see bsls_asserttest.h for an example of how that might be implemented). Importantly, the lack of by-default runtime changing of violation handler behavior means that a custom violation handler must be written to get the full functionality needed - when testing that a check is violated, you want the violation handler to throw so you can recover to do more tests, while when testing anything else you want a hard error because a bug has been found by your testing. |
||||||||
146 | qdev.handler.testing | Quality Sensitive Developer | Unit test violation handlers | Have a way to run handler on all combinations of available build modes | 25 | 0 | 0 | 0 |
The proposal in N4820 and all of its descendants prohibited any way to
directly invoke the violation handler. A macro-based facility can expose this
with relative ease (and, for example, BDE does with the macro |
||||||||
147 | crit.control | Critical Software Developer | Have a verifiable release system | Be able to control the configuration of contracts from a central point | 25 | 75 | 75 | 50 |
A macro based facility, or macros built on top of P1607, could provide this - with the major limitation that there might be multiple such facilities to configure within a single application. N4820 and P1429 only provide global controls over how contracts behave. |
||||||||
148 | crit.noundef | Critical Software Developer | Avoid undefined behavior | Have contract violation at run-time always have well-defined behavior | 25 | 0 | 75 | 50 |
Only P1429 allows removing any use of an P1607 Allows for this, but also allows for the explicit use of the |
||||||||
149 | crit.recovery | Critical Software Developer | Not have a faulty program lead to catastrophic failure | Have access to a recovery path after contract violation | 25 | 0 | 75 | 50 |
150 | crit.redundancy | Critical Software Developer | Not have a faulty program lead to catastrophic failure | Be able to express error handling that may be redundant with contract checking | 25 | 0 | 75 | 50 |
151 | crit.interaction | Critical Software Developer | Not have a faulty program lead to catastrophic failure | Not have contract build or run modes possibly be able to change or disable related error handling in any way | 25 | 0 | 75 | 50 |
152 | crit.testing | Critical Software Developer | Meet code coverage requirements | Be able to run both success and failure branches in my test environment | 25 | 0 | 75 | 50 |
With the right build or diligently avoiding the use of the |
||||||||
153 | crit.locality | Critical Software Developer | Be assured a critical violation uses a critical recovery path | Couple recovery path to a specific contract within the source | 25 | 10 | 10 | 10 |
The only viable option with the language proposals would be a custom violation handler that access a diligently updated thread-local recovery path when there is a violation. There is no innate support for this built into the facilities. |
||||||||
154 | crit.production.checking | Critical Software Developer | Have redundant layering | Be able to continue to run checks in a production environment (even after formal testing is complete) | 25 | 50 | 75 | 75 |
P1429 gives complete flexibility about what levels are checked or not in the builds you choose to deploy to production. P1607 and a macro-based facility allow building that same kind of functionality. |
||||||||
155 | crit.more.coverage | Critical Software Developer | Maximize coverage | Be able to run checks in a production environment that are considered "cheap" compared to the expected cost of entering an invalid state | 25 | 75 | 75 | 50 |
N4820 and P1429 both explicitly consider cost as the primary metadata that can
be put on a contract annotation (via a level of |
||||||||
156 | crit.noassume | Critical Software Developer | Avoid unexpected or undefined behavior | Ensure checks will never be __assume'd/__builtin_assume'd by the compiler as if they were facts injected into the program (otherwise, if such an assumption ever failed, I would be running a different program that is not equivalent to the one I wrote; assumptions can expand the set of possible executions by injecting facts not otherwise knowable to the compiler) | 0 | 0 | 50 | 0 |
Only P1429 provides the a way to configure contract annotations to never be assumed. |
||||||||
157 | sec.noattacks | Security Sensitive Developer | Limit attack vectors | Be unable to insert code paths (eg. violation handlers) at run time (eg. build time only) | 0 | 90 | 90 | 90 |
None of the contract facilities allow any runtime alteration of the violation handler. Note importantly that an application may choose to install a custom violation handler that delegates to something that is runtime controllable, so this restriction is circumventable if the compiler allows for setting a custom violation handler. |
||||||||
158 | sec.certify | Security Sensitive Developer | Deliver a certified product | Have build tool only link to a preapproved violation handler | 0 | 90 | 90 | 90 |
The wording for N4820 and its derivatives allows for a compiler to choose to not allow for changing the violation handler to something user defined. |
||||||||
159 | analysis.runtime | User of Analysis Tools | Improve runtime correctness | Have runtime checks generated by the tool | 0 | 0 | 0 | 50 |
160 | analysis.optimization | User of Analysis Tools | Improve runtime performance | Have runtime optimizations generated by the tool | 0 | 0 | 0 | 50 |
None of the proposals or the language itself support direct integration with a static analysis tool. Explicit literal semantics, however, could be injected into code by such a tool to generate computed contract behavior - runtime checking, optimizations, or otherwise. |
||||||||
161 | analysis.symbolic | User of Analysis Tools | Allow symbolic analysis | Have symbolic proofs for soundness and consistency performed before compile time | 25 | 50 | 75 | 75 |
162 | analysis.compiletime | User of Analysis Tools | Allow code analysis | Have code source, AST, or instruction inspection during compile time | 25 | 50 | 75 | 75 |
Proofs of soundness are certainly aided by stating contract annotations and asking for validation of those annotations. Tooling will need to catch up to leverage this and do such proving. Many such proofs rely on being able to state additional facts that are not easy to codify as boolean checks, and those are often needed to thoroughly prove even much simpler predicates, so none of the proposed solutions are complete for this purpose. |
||||||||
163 | analysis.binaries | User of Analysis Tools | Allow binary analysis | Have binary inspection after compile time | 0 | 50 | 50 | 50 |
In principle contract checks could be carried forwarded and recorded in binaries to allow for post-compile verification. It seems unlikely that a non-builtin facility would be standardized in binary files in such a way. |
||||||||
164 | analysis.information | User of Analysis Tools | Improve the quality of analysis | Be able to hint to the analyzer information it may be unable to deduce from source code alone (eg. 5 / opaque(); [[ opaque() != 0]]) | 0 | 0 | 0 | 75 |
None of the proposals allow for hints that are exclusively for the static
analyzer to use, but the P1607 |
||||||||
165 | analysis.legacy | Provider of Analysis Tools | Extend my existing engine | Be able to map pre-existing contract features in tools to a standardized language syntax | 0 | 25 | 25 | 75 |
166 | large.modernize | Large Codebase Owner | Modernize my code base | Introduce standardized contracts to replace my macro-based contracts | 0 | 25 | 25 | 75 |
Only a very limited legacy framework (such as the C P1607 sought to provide more flexibility for reimplementing most legacy frameworks in terms of common semantics provided by the language. |
||||||||
167 | teach.bestpractices | Teacher | Demonstrate best practice | Be able to express defensive programming, programming by contract, and test driven development to introductory students | 25 | 100 | 100 | 100 |
168 | teach.standardized | Teacher | Demonstrate best practice | Not rely on custom libraries or proprietary extensions | 0 | 100 | 100 | 100 /50 |
169 | teach.lifecycle | Teacher | Demonstrate best practice | Demonstrate mock lifecycle by switching simple compiler flags to control which checks are enabled | 0 | 50 | 50 | 25 /75 |
170 | teach.portable | Teacher | Manage many students | Have examples compilable by a standard compiler on any system | 0 | 50 | 50 | 50 /40 |
Basic defensive programming is directly expressable through any of the proposed contract facilities. Without a language feature, custom libraries must be used and that is not portable. Advanced features of contract lifecycle are more difficult to teach when not directly supported, and none of the proposals provide a complete solution for that behavior (see P1332 for a broad discussion of what sorts of lifecycle considerations impact contract use), while only P1607 provides a way to do so at all at a non-global granularity. |
||||||||
171 | teach.dumbstudents | Teacher | Manage many students | Have examples that are easy to build without digression into build systems | 0 | 50 | 40 | 30 |
More advanced usages with P1429 or P1607 are clearly dependent on more complicated configuration. N4820 provides a simple set of flags (build modes) that would arguably be easiest to teach and use. Note that nothing in P1429 prevented the support for the same set of build modes in addition to the more specific semantic per level setting that it required. |
||||||||
172 | teach.teachable | Teacher | Build layers of understanding | Have simple explanation of assertions and their use to support simple programming tasks, including debugging erroneous programs. | 0 | 50 | 50 | 75 |
173 | teach.layering | Teacher | Build layers of understanding | Support the ability for advanced uses of contracts to be distributed across many different courses in a C++-focused computer science curriculum. | 0 | 50 | 50 | 75 /50 |
N4820 and P1429 provide the least advanced features, but all of the proposals
allow for basic contract use to be done ( |
||||||||
174 | compiler.benice | Compiler Developer | Deliver best experience to my customers | Maximize implementation freedom by limiting what is strictly required by the standard | 0 | 40 | 40 | 75 |
The explicit specification of build modes seems to be seen as highly restrictive by compiler vendors, and the less specific the standard is about that aspect of the facility the more this user base seems to be satisfied. |
||||||||
175 | compiler.best | Compiler Developer | Deliver the best implementation | Have a clear and simple specification that meets clear need | 0 | 15 | 75 | 75 |
N4820 had many open questions about its specification where it had diverged from the original contract proposals. The semantic presentation in P1429 and P1607 sought to be very precise about what was expected of program behavior for any given contract. |
||||||||
176 | large.complex | Large Codebase Developer | Debug complex issues | Have composable and fine grained control over which checks are run, without requiring source code changes. Specifically the checks for only one function or some grouping of functions | 25 | 0 | 0 | 25 |
177 | large.critical | Large Codebase Developer | Enable/Disable checking on critical/hot paths | Control whether checks are run based on where they are being called from | 0 | 0 | 0 | 0 |
A macro based facility could build this form of subsetting with a great deal of effort, though callsite based checking does not seem feasible in any macro based facility. N4820 and P1429 provide no fine grained control of checks. |
||||||||
178 | large.stillmacros | Large Codebase Owner | Modernize my code base | Have my existing macro-based facilities interoperate smoothly with standardized contracts so I can do the migration gradually | 0 | 0 | 0 | 75 |
Only by mapping to literal semantics could a more advanced contract facility be able to maintain behaviors while still using the same underlying facility that the language provides. |
||||||||
179 | large.observation | Large Codebase Owner | Introduce new contracts into an existing system | Have failed individual checks from existing code optionally warn instead of hard stop | 25 | 25 | 25 | 75 |
180 | large.introduction | Large Codebase Owner | Introduce new contracts into an existing system | Have failed checks from a new library optionally warn instead of hard stop | 25 | 25 | 50 | 75 |
181 | large.newenvironment | Large Codebase Owner | Introduce new elements into a contracts based system | Have failed checks caused by a change in environment optionally warn instead of hard stop | 25 | 25 | 50 | 75 |
182 | large.newcompiler | Large Codebase Owner | Introduce new elements into a contracts based system | Have failed checks caused by a change in compiler optionally warn instead of hard stop | 25 | 25 | 50 | 75 |
N4820 and P1429 provide a continuation mode to be able to make this decision at a global (or translation unit) scope. P1607 provides the ability to do this per-contract. |
||||||||
183 | large.separability | Large Codebase Owner | Introduce new parameters or invariants into a contracts based system | Be able to include distinct clauses for each parameter or invariant with their own individual failure or build controls | 25 | 75 | 75 | 75 |
All of the language proposals allowed for multiple distinct annotations on a single function. |
||||||||
184 | large.nogoingback | Large Codebase Owner | Prevent regressions | Have trusted contracts fail fast and hard stop | 25 | 0 | 10 | 75 |
The global controls of N4820 and P1429 limit the ability to enforce only those
checks that are trusted, and provide no natural way to mix some continuing contracts with
some non-continuing contracts. (P1429 would allow this by assigning different
semantics to |
||||||||
185 | large.scalability | Large Codebase Owner | Scale violation handling | Be able to log violations in my organization specific format | 25 | 90 | 90 | 90 |
All of the proposals with a global pluggable violation handler allow this form of customization, though it is not required by any that a compiler actually allow the customization of the violation handler. |
||||||||
186 | large.simulation.disable | Large Codebase Owner | Allow simulation or post-mortem testing of known failure modes | Optionally disable checking on a subset of individual annotations | 25 | 0 | 0 | 25 |
187 | large.simulation.enable | Large Codebase Owner | Allow simulation or post-mortem testing of known failure modes | Optionally allow checking of a subset of individual annotations to fail and access its recovery path | 25 | 0 | 0 | 25 |
188 | large.simulation.ignore | Large Codebase Owner | Allow simulation or post-mortem testing of known failure modes | Optionally allow checking of a subset of individual annotations to fail and continue failing | 25 | 0 | 0 | 25 |
Subsets of annotations can be called out with macro-based solutions, but not with the global controls of N4820 or P1429. |
||||||||
189 | large.perfcontrol.build | Large Codebase Owner | Manage performance cost | Constrain the set of built time checks according to their performance overhead | 0 | 25 | 25 | 20 |
190 | large.perfcontrol.runtime | Large Codebase Owner | Manage performance cost | Constrain the set of runtime checks according to their performance overhead | 0 | 50 | 50 | 40 |
The language proposals had minimal control over build-time checking, but were focused on doing any constraining based on the cost of the check. |
||||||||
191 | large.narrowing | Large Codebase Owner | Tune contract width in complex system | Be able to narrow individual contract so it fails in testing not in production | 25 | 0 | 0 | 50 |
A P1607 contract can have a macro to control individual semantics and go through a lifecycle where it is checked or enforced in testing but left ignored in production. Without global control, this cannot be done with N4820 or P1429. |
||||||||
192 | embedded.nochecking | Small Machine Developer | Minimize executable footprint | Remove all checking and diagnostic (eg. source location) overhead entirely from the final binary | 25 | 50 | 50 | 50 |
The language based proposals allow for the removal of checking without |
||||||||
193 | embedded.nologging | Small Machine Developer | Minimize executable footprint | Remove all logging and diagnostic (but not checking) overhead from the final binary | 25 | 10 | 10 | 10 |
194 | embedded.minimize | Small Machine Developer | Minimize executable footprint | Remove all but the most important diagnostic overhead from the final binary | 25 | 0 | 0 | 0 |
None of the proposals allow for total removal of logging information from the generated code, while a macro based facility could support that option. It would conceivable be possible for link time optimization to recognize that a violation handler made no use of source information/did no logging and then it would be able to remove that source information as well, but this would have significant compile time overhead on a system. |
||||||||
195 | wg21.everythingelse | Language Developer | Interoperate with Contracts | Have a clear way to understand how contracts will interact with the standard library | 0 | 0 | 0 | 0 |
196 | wg21.otherfeatures | Language Developer | Extend contracts beyond pre/post conditions on functions | Be able to use contract-like syntax on past or present runtime checkable language features such as switches, pattern matching, etc. or what might happen on signed integer overflow, etc. This might allow configuration of trapping, logging, or assuming in other areas of language UB. | 0 | 0 | 0 | 0 |
None of the proposed solutions began integration with the rest of the standard, though they would have facilitated it in various ways. |
||||||||
This is a lot of information to digest, so it might help to be able to get some overview numbers on how the different proposals relate to one another. There are endless ways to do this, and we make no attempt to be complete in this analysis, but we will present some approaches that might be useful.
Proposal | Score | Score W/Macros |
---|---|---|
N4842 | 0.000% | 22.041% |
N4820 | 31.454% | 31.454% |
P1429 | 37.908% | 37.908% |
P1607 | 42.321% | 43.087% |
Proposal | Score | Score W/Macros |
---|---|---|
N4842 | 0.000% | 24.683% |
N4820 | 38.306% | 38.306% |
P1429 | 44.703% | 44.703% |
P1607 | 48.847% | 49.527% |
Proposal | Score | Score W/Macros |
---|---|---|
N4842 | 0.000% | 28.419% |
N4820 | 48.156% | 48.156% |
P1429 | 54.359% | 54.359% |
P1607 | 57.940% | 58.634% |
Note that these are not intended to produce a complete measure of those proposals, but simply as an example of how they might be compared. These totals are not a good measure of which proposal is intrinsicly better, but the changes to these totals from any individual proposal should be considered relevant when trying to understand how all users might benefit from any perticular change.