This section continues the discussion on Contract Programming started in Contract Programming Overview.
The following table compares features between this library, the proposal for adding Contract Programming to the C++ standard [Crowl2006] (see also [Crowl2005], [Abrahams2005], [Ottosen2004b], and [Ottosen2004]) [19] , the Eiffel programming language [Meyer1997], and the D programming language [Bright2004].
Table 1. Contract Programming Feature Comparison
|
Feature |
This Library |
C++ Standard Proposal |
ISE Eiffel 5.4 |
D |
|---|---|---|---|---|
|
Keywords |
In contract macros preprocessor sequence: |
|
|
|
|
On contract failure |
Default to |
Default to |
Throw exception |
Throw exception |
|
Return value in postconditions |
Yes, |
Yes, |
Yes, |
No |
|
Old values in postconditions |
Yes, |
Yes, |
Yes, |
No |
|
Subcontracting |
Yes, also support multiple base contracts for multiple inheritance |
Yes, also support multiple base contracts but only base classes can specify preconditions |
Yes |
Yes |
|
Contracts for abstract functions |
Yes |
Yes |
Yes |
No (planned) |
|
Arbitrary code in contracts |
Yes (but recommended to limit contracts to a list of assertions |
No, assertions only |
No, assertions only plus preconditions can only access public members |
Yes |
|
Constant-correct |
Yes |
Yes |
Yes |
No |
|
Function code ordering |
In contract macros preprocessor sequence: Preconditions -> postconditions -> body |
Preconditions, postconditions, body |
Preconditions, body, postconditions |
Preconditions, postconditions, body |
|
Static assertions |
No (but Boost.MPL can be used within contracts) |
No |
No |
Yes |
|
Block invariants |
Yes, |
Yes, |
No, but support loop invariants (loops are special code blocks that iterate) |
No |
|
Loop variants |
Yes, |
No |
Yes |
No |
|
Disable assertion checking within assertions checking (policy) |
Yes (to prevent infinite recursion) |
Yes, but not in preconditions |
Yes |
No |
|
Nested function calls (policy) |
Disable checking of class invariants (static and non) |
Disable nothing |
Disable all checks |
Disable nothing |
|
Non-static class invariants checking (policy) |
At constructor exit, around any non-static function, at destructor entry, and at function exit due to exception -- but only if programmers specifies contracts for those (e.g., if no contract specified for a private function then no class invariant and no contract is checked for that function) |
At constructor exit, around public functions, at destructor entry, and at function exit due to exception |
At constructor exit, and around public functions |
At constructor exit, around public functions, and at destructor entry |
|
Static class invariants checking (policy) |
At entry and exit of any (also static) member function, constructor, and destructor |
No static class invariants |
No static class invariants |
No static class invariants |
|
Removable from object code |
Yes, any combinations of |
Yes |
Yes (but predefined combinations only) |
Yes |
The design of this library was largely based on the requirements identified by the different revisions of the proposal for adding Contract Programming to the C++ standard [Crowl2006, etc] and by the Eiffel programming language [Meyer1997].
This is a list of some of the specific requirements considered for the library design:
In addition, library early implementations were somewhat inspired by the work of [Maley1999].
The main use of Contract Programming is to improve software quality. [Meyer1997] discusses how Contract Programming can be used as the basic tool to write "correct" software. The following is a short summary of the benefits associated with Contract Programming mainly taken from [Ottosen2004]. See also [Wilson2006] for a discussion of Contract Programming applied to the C++ programming language. Furhtermore, [Stroustrup1997] discusses the key importance of class invariants plus advantages and disadvantages of using preconditions and postconditions.
Contract Programming benefits come to the cost of performance as discussed in detail by both [Stroustrup1997] and [Meyer1997].
The run-time performances are negatively impacted by Contract Programming mainly because of the following:
To alleviate some of these run-time performance impacts, you can selectively turn off some of the contract compilation and the related run-time checking. In reality, you will have to decide based on the performance trade-offs required by your system but a reasonable approach might be to [21] :
Compile-time performances are also impacted by this library as compilation time and compiler memory usage increase mainly because:
In addition, Contract Programming might induce a false sense of security on the correctness of the software. However, Contract Programming is proposed here as a tool to complement (and not to substitute) testing.
In general, Contract Programming is an essential approach to improve software quality even if it comes at a performance cost. While performance trade-offs should be carefully considered depending on the specific application domain, software quality cannot be sacrificed -- it is difficult to see the value of a system that quickly and efficiently provides the incorrect output.
Contract Programming is also supported by the following tools (this is not a complete list):
Typically, preprocessing tools external to the language work by transforming specially formatted code comments into contract code that is then checked at run-time.
[19] These are all revisions of the same proposal for adding Contract Programming to the C++ standard.
[20] Of course, if the contract is ill written then Contract Programming is of little use. However, it is less likely to have a bug in both the function body and the contract than in the function body only. For example, consider the validation of a result in postconditions. Validating the return value might seem redundant, but in this case we actually want that redundancy. When programmers write a function, there is a certain probability that they make a mistake in implementing the function body. When they specify the result of the function in the postconditions, there is also a certain probability that they make a mistake in writing the contract. However, the probability that they make a mistake twice (in the body and in the contract) is lower than the probability that the mistake is made just once (in the body).
[21] This approach is generally reasonable because in well tested production code, validating the function body implementation via postconditions and class invariants is rarely needed since the function has shown itself to be "correct" during testing. On the other hand, checking arguments has continuing need because of the evolution of the callers.