This section gives an overview of Contract Programming and explains how this library can be used to write contracts. At the end of the section there is a fully working example that can be compiled and executed.
Contract Programming is characterized at least by the following assertion mechanisms (see [Meyer1997], [Ottosen2004], or [Crowl2006]):
Based on these definitions of invariants, preconditions, postconditions, and subcontracting it is possible to specify the semantics of the invocation of a function for which a contract has been specified.
A non-member function call should execute the following steps:
A member function call should execute the following steps:
Where AND and OR are the logical "and" and "or" operators evaluated in short-circuit (i.e., A AND B evaluates B only if A is evaluated to be true, A OR B evaluates B only if A is evaluated to be false).
When a member function overrides more than one virtual function due to multiple inheritance:
A constructor call should execute the following steps:
Before constructor body execution, there is no object therefore:
A destructor call should execute the following steps:
Note that:
After programmers specify contracts, this library provides a mechanism to automatically check if class invariants, preconditions, postconditions, block invariants, and loop variants hold true at run-time or not.
If a class invariant, precondition, postcondition, block invariant, or loop
variant asserted via CONTRACT_ASSERT(), CONTRACT_ASSERT_BLOCK_INVARIANT(), or CONTRACT_ASSERT_LOOP_VARIANT() is checked to be false, the library invokes
the contract::class_invariant_failed(), contract::precondition_failed(), contract::postcondition_failed(), contract::block_invariant_failed() (for both block invariant and loop variant
failures) function respectively. These functions invoke std::terminate() by default but programmers can redefine
them to take a different action using contract::set_class_invariant_failed(), contract::set_precondition_failed(), contract::set_postcondition_failed(), and contract::set_block_invariant_failed().
This mechanism is similarly to the one of C++ std::set_terminate() (see Throw
on Failure for an example).
Contracts are only supposed to check the object state in order to ensure its compliance with the software specifications. Therefore, contract checking should not be allowed to modify the object state and exclusively "read-only" operations (or queries) should be used to specify contracts.
This library enforces
[4]
this constraint at compile-time using the C++ const
qualifier. Contracts only have access to the object, function arguments,
and return value via constant references thus only constant members can be
accessed when checking contracts. Furthermore, pointers are passed as const T* so the pointed object is constants and cannot
be changed (note that the pointer value itself is not constant, because it
is not passed as const T* const, and
it could be changed by the contract but such a change will be local to the
contract function therefore still ensuring the const-correctness of the contract).
The following overview of the contract macros and their usages should be enough to understand the working example at the end of this section, most of the examples in the Examples annex, and to start writing contracts on your own. Consult the library Reference documentation for more information. [5] .
CONTRACT_INVARIANT(
(static)({ ... }) ({ ... }) )
This macro is used to program class invariants. It should appear within a private section in the class declaration. It takes one parameter which is a Boost.Preprocessor sequence of the following elements:
(static) ({ ...
}): The code block { ... }[6]
asserting static class invariants. This is optional and it can be omitted.
({ ...
}): A secondary code block { ... }
asserting non-static class invariants. This is mandatory but an empty code
block ({}) can be specified
if there are no class invariants (using CONTRACT_INVARIANT() instead of CONTRACT_INVARIANT( ({}) ) will generate a preprocessor error).
If contract compilation is turned off, this macro expands to nothing (and no contract overhead is added). Otherwise, it expands to code that checks the class invariants (see Without the Macros for details).
CONTRACT_FUNCTION(
signature-sequence (precondition)({ ... }) (postcondition)(result-name)({
... }) (body)({ ... }) )
CONTRACT_CONSTRUCTOR(
signature-sequence (precondition)({ ... }) (postcondition)({
... }) (body)({ ... }) )
These macros are used to program preconditions and postconditions for functions (members and non) and constructors respectively. They should appear right after the function and constructor declaration, and after the constructor members initialization list when such a list is specified. They both take the following parameters:
(precondition) ({ ...
}): An optional code block { ... }
asserting the preconditions.
(postcondition) (result-name) ({ ...
}): An optional code block { ... }
asserting the postconditions. The (result-name) element is only specified for non-void
functions (so never for constructors) and it names the variable used to
access the return value within postconditions (e.g., result).
(body)({ ... }): A mandatory code block { ... }
implementing the function or constructor body.
If contract compilation is turned off, these macros simply expand to the body code block (and no contract overhead is added). Otherwise, they expand to code that implements the Member Function Call Semantics and Constructor Call Semantics respectively thus checking class invariants, preconditions, and postconditions (see Without the Macros for details).
CONTRACT_DESTRUCTOR(
signature-sequence (body)({ ... }) )
This macro is used to program destructor contracts. It should appear right after the destructor declaration. It takes the following parameters:
(body) ({ ...
}): A mandatory code block { ... }
implementing the destructor body.
If contract compilation is turned off, this macro simply expands to the body code block (and no contract overhead is added). Otherwise, it expands to code that implements the Destructor Call Semantics checking the class invariants (see Without the Macros for details).
CONTRACT_OLDOF(
variable_name )
This macro is used to access the old value for the variable with the specified name in postconditions. It takes the following parameter:
name: The name of the variable.
As usual, this can be used
as the variable name for the object so CONTRACT_OLDOF(this)
is used to access the object old value. Otherwise, any of the function
argument variable names can be specified. The variable type (class or argument
type) must be tagged copyable in the signature sequence for the variable
old value to be available (see below). The library generates a compile-time
error (containing the text contract::noold)
if this macro is used on a variable name of a type which was not tagged
copyable.
![]() |
Note |
|---|---|
This library does not support old values for any expression that can be evaluated in postcondition as supported by Eiffel and required [Crowl2006] instead. The library supports old values for the object and all the function argument types, and this is a subset of the old values supported by Eiffel and required by [Crowl2006]. However, the old values supported by the library are usually enough to program the postconditions (e.g., all Eiffel examples from [Meyer1997], [Mitchell2002], and all C++ examples from [Crowl2006] were successfully programmed using just these old values, see the Examples section). |
The old value variable are only declared locally within postconditions therefore they cannot be mistakenly accessed outside the related postcondition code block (see Without the Macros for details).
![]() |
Note |
|---|---|
|
If pointer types are tagged copyable, then the pointer value, and not
the pointed value, is copied. Therefore the old pointer value will be available
in postconditions via
The notable exception is the object |
CONTRACT_ASSERT(
boolean_condition )
This macro is used to assert conditions within the class invariant, precondition, and postcondition code blocks (see also CONTRACT_ASSERT_MSG()). It takes the following parameter:
boolean_condition: The
boolean condition being asserted.
If contract compilation is turned off, this macro expands to nothing (and
no contract overhead is added). Otherwise, this macro expands to code that
triggers the invocation of contract::class_invariant_failed(), contract::precondition_failed(), or contract::postcondition_failed() in case the asserted boolean condition
is evaluated to be false at run-time from within invariants, preconditions,
or postconditions respectively (see Without
the Macros for details).
CONTRACT_ASSERT_BLOCK_INVARIANT(
boolean_condition )
This macro is used to assert invariant within a generic code block (see also CONTRACT_ASSERT_BLOCK_INVARIANT_MSG()). This macro can also be used within loops to assert loop invariants. It takes the following parameter:
boolean_condition: The
boolean condition being asserted.
If contract compilation is turned off, this macro expands to nothing (and
no contract overhead is added). Otherwise, this macro expands to code that
triggers the invocation of contract::block_invariant_failed() in case the asserted boolean condition
is evaluated to be false at run-time (see Without
the Macros for details).
![]() |
Note |
|---|---|
This macro is similar to the C |
CONTRACT_ASSERT_LOOP_VARIANT(
integer_expression )
This macro is used to assert loop variants (see also CONTRACT_ASSERT_LOOP_VARIANT_MSG()).
It must be used after CONTRACT_INIT_LOOP_VARIANT
has been called once within the same code block scope (see below).
integer_expression: An
integer expression which is evaluated at each loop iteration as the loop
variant. It must be always positive (not negative and not zero) and it
must strictly decrease at each subsequent loop iteration (of 1 or more).
If contract compilation is turned off, this macro expands to nothing (and
no contract overhead is added). Otherwise, this macro expands to code that
triggers the invocation of contract::block_invariant_failed() at run-time in case the specified variant
expression is either not positive or it does not decrease from one loop iteration
to the next (see Without the
Macros for details).
Let's assume we want to write a myvector
class template that wraps the C++ STL vector template std::vector
adding contracts to it. Here we show, step-by-step, how to program a contract
for the push_back()
function.
Step 1) First, we implement myvector::push_back()
to call std::vector::push_back()
and we can sketch the contract using comments.
template<typename T> class myvector { // Invariant: (size() == 0) == empty() public: // Precondition: size() < max_size() // Postcondition: size() == (oldof size() + 1) void push_back(const T& element) { vector_.push_back(element); } ... // Rest of the class. private: std::vector<T> vector_; };
Where "oldof size()" indicates the value size()
has before the push_back() function is executed.
To keep this example simple, we intentionally did not write the full contract
but we only wrote one invariant, one precondition, and one postcondition
(see the STL Vector Example for
the complete push_back()
contract). These contract conditions specify the followings:
push_back() is called (so there is available space
to add one extra vector element).
push_back()
has been executed (to reflect the newly added vector element).
Step 2) The library provides the CONTRACT_FUNCTION()
macro to write member function contracts. This macro must be used right
after the push_back() function declaration. The push_back()
function definition { vector_.push_back(); } is moved
within the macro becoming the last element of the Boost.Preprocessor
sequence passed as the macro parameter:
template<typename T> class myvector { // Invariant: (size() == 0) == empty() public: // Precondition: size() < max_size() // Postcondition: size() == (old size()) + 1 void push_back(const T& element) CONTRACT_FUNCTION( signature-sequence precondition-sequence postcondition-sequence (body) ({ vector_.push_back(element); }) ) // No need for ";" after the macro closing parenthesis ")". ... // Rest of the class. private: std::vector<T> vector_; };
There is no need for ";"
after the macro closing parenthesis ")".
Step 3) We now program the postconditions
using CONTRACT_ASSERT() inside a code block {
... }
specifying the postcondition-sequence elements:
template<typename T> class myvector { // Invariant: (size() == 0) == empty() public: // Precondition: size() < max_size() void push_back(const T& element) CONTRACT_FUNCTION( signature-sequence precondition-sequence (postcondition) ({ CONTRACT_ASSERT( size() == (CONTRACT_OLDOF(this)->size() + 1) ); }) (body) ({ vector_.push_back(element); }) ) ... // Rest of the class. private: std::vector<T> vector_; };
Note how CONTRACT_OLDOF(this) is used
to access the old object value as it was before body execution.
Step 4) Similarly, we program the preconditions
in a code block { ...
} specifying the precondition-sequence
elements:
template<typename T> class myvector { // Invariant: (size() == 0) == empty() public: void push_back(const T& element) CONTRACT_FUNCTION( signature-sequence (precondition) ({ CONTRACT_ASSERT( size() < max_size() ); }) (postcondition) ({ CONTRACT_ASSERT( size() == (CONTRACT_OLDOF(this)->size() + 1) ); }) (body) ({ vector_.push_back(element); }) ) ... // Rest of the class. private: std::vector<T> vector_; };
Step 5) The signature-sequence elements are discussed separately (see below) and for now we will leave them unresolved.
Step 6) Similarly to what we did with for
preconditions and postconditions, we program the class invariants in a code
block { ...
} specifying sequence elements passed
to the CONTRACT_INVARIANT() macro:
template<typename T> class myvector { CONTRACT_INVARIANT( ({ CONTRACT_ASSERT( (size() == 0) == empty() ); }) ) // Again, no need for ";" after the macro closing parenthesis ")". public: void push_back(const T& element) CONTRACT_FUNCTION( signature-sequence (precondition) ({ CONTRACT_ASSERT( size() < max_size() ); }) (postcondition) ({ CONTRACT_ASSERT( size() == (CONTRACT_OLDOF(this)->size() + 1) ); }) (body) ({ vector_.push_back(element); }) ) ... // Rest of the class. private: std::vector<T> vector_; };
Again, there is no need for ";"
after the macro closing parenthesis ")".
Step 7) The same step-by-step process can
be applied to understand the usage of CONTRACT_CONSTRUCTOR() and CONTRACT_DESTRUCTOR() (see the example at the very end of this
section).
This library allows for arbitrary constant-correct code within the class
invariant, precondition, and postcondition code blocks ({
... }).
In other words, the class invariant, precondition, and postcondition code blocks can contain any legal C++ code and they can access any constant class member (private, protected, or public). However, writing complex code in the contracts will increase the probability of introducing bugs in the contracts themselves so it is better to limit the contract code to a simple list of assertions with occasional if-statements to guard them. Furthermore, if non-public members are used in preconditions then the callers will not be able to fully check the preconditions to make sure the contract is satisfied before calling the function so it is best to only use public members at least in preconditions [8] .
In addition, this library checks class invariants (as well as preconditions and postconditions) only for functions that have a contract. For example, if a private member function is allowed to temporarily brake the class invariants, you can omit the contract for that one private function so no invariants (as well as no preconditions and no postconditions) will be checked when that function is called. However, it should never be the case that public member functions can brake class invariants. It is recommended to write contracts for all functions (public and non) with the rare exceptions of a private member function that cannot be programmed without allowing for it to temporarily brake the class invariants.
![]() |
Important |
|---|---|
|
Recommended Practices 1. Limit the contract code to a list of assertions with occasional if-statements to guard them. 2. Only access public class members when asserting preconditions. 3. Write contracts at least for all public member functions (even if they have no preconditions and no postconditions) so they check the class invariants. |
These practices are followed by all the examples of this documentation.
The very first elements of the Boost.Preprocessor
sequence passed to the macros CONTRACT_CONSTRUCTOR(), CONTRACT_DESTRUCTOR(), and CONTRACT_FUNCTION() are the elements of the signature-sequence
[9]
. The signature-sequence repeats the syntactic elements
of the function declaration.
[10]
The syntax of signature-sequence is somewhat unusual (even if it is all legal ISO standard C++). However, there is a convenient rule for remembering this syntax:
![]() |
Important |
|---|---|
|
Signature Sequence Mnemonic The tokens in the signature sequence appear in the exact same order as they appear in the relative function declaration. |
Let's consider the following example (with respect to the myvector
example above, we have added the base class pushable
to show how to subcontract):
template<typename T> class myvector: public pushable<T> { public: void push_back(const T& element) CONTRACT_FUNCTION( signature-sequence ... ) ... };
Applying the mnemonic rule, we read the declaration of push_back() from top to bottom and we find the following
tokens in the listed order (we have to start all the way up to the top of
the myvector class declaration):
class keyword for
myvector (this indicates
that push_back is a member
function).
myvector class
type (this indicates that push_back
is a member function of myvector).
pushable<T>
base class which is repeated in signature-sequence
as (inherit)(pushable<T>) but only when the function is subcontracting
from the specified base class (because ":"
is not a valid preprocessor token (inherit)
is used instead; also the inheritance access level, the public
keyword in this case, is not relevant and it is not repeated in signature-sequence).
public access level
of the push_back()
member function.
void return type.
push_back function
name.
("
to open the function argument list.
const T& function
argument type.
element function
argument name.
)"
to close the function argument list.
Wrapping all these tokens within parenthesis ()
and listing them in the exact order as they appear in the function declaration,
we obtain the signature-sequence elements for this example.
The parenthesis () around the
tokens are mandatory because they are the ones actually creating the Boost.Preprocessor
sequence. New lines and spaces do not matter within preprocessor sequences.
(class) (copyable)(myvector) (inherit)(vector_interface<T>) (public) (void) (push_back)( (const T&)(element) )
The myvector class type is
preceded by (copyable) because, for this example, we wanted to
access the old object value CONTRACT_OLDOF(this)
(as the object was before the function body execution) in the postconditions.
Also any function argument type can be tagged copyable if the old value of
the related argument is needed in the postconditions. In this example, (const T&)(element) could have been tagged copyable and specified
in signature-sequence as (copyable)(const T&)(element) if the old value of the argument CONTRACT_OLDOF(element)
(as the argument was before the body execution) was needed in the postconditions.
Completing the example presented in the previous section, with the addition
of subcontracting myvector::push_back() from pushable::push_back(), we have:
template<typename T> class myvector: public pushable<T> { CONTRACT_INVARIANT( ({ CONTRACT_ASSERT( (size() == 0) == empty() ); }) ) public: void push_back(const T& element) CONTRACT_FUNCTION( (class) (copyable)(myvector) (inherit)(pushable<T>) (public) (void) (push_back)( (const T&)(element) ) (precondition) ({ CONTRACT_ASSERT( size() < max_size() ); }) (postcondition) ({ CONTRACT_ASSERT( size() == (CONTRACT_OLDOF(this)->size() + 1) ); }) (body) ({ vector_.push_back(element); }) ) ... // Rest of the class. private: std::vector<T> vector_; };
This is a fully working example assuming that:
pushable::push_back()
also has a contract specified using this library since it is used for subcontracting
(this library allows to specify contracts also for pure virtual functions,
see below).
myvector has an accessible
constant-correct
[11]
copy constructor since it was tagged (copyable)
(see contract::copy):
// Must be accessible and construct from a const& parameter. myvector::myvector(const myvector& source) { ... }
If either one of these conditions is not true, this library will generate a compile-time errors when attempting to compile the code above.
Generalizing this example to include all possible syntactic elements that can be found in a C++ function declaration, we obtain the full syntax for signature-sequence.
signature-sequence syntax for
CONTRACT_FUNCTION():
For member functions:
{(class) | (struct)} [(copyable)](class-type) {(inherit)(base-class-type)}* {(public) | (protected) | (private)} [(template)( {(function-template-parameter-type)(function-template-parameter-name)}+ )] [(inline)] [(static)] [(virtual)] (result-type) (function-name)( {(void) | {[(copyable)](argument-type)(argument-name)}+} ) [(const)] [(volatile)]
For non-member functions:
[[(export)] (template)( {(function-template-parameter-type)(function-template-parameter-name)}+ )] [(inline)] [(extern [extern-string])] (result-type) (function-name)( {(void) || {[(copyable)](argument-type)(argument-name)}+} )
Note that (static) is used as usual for static member function
but it cannot be specified for non-member functions because its use for non-members
was deprecated from C to C++.
We have used the following conventions in writing the signature-sequence syntax:
item] indicates
an optional item.
item* indicates an item repeated
zero of more times.
item+ indicates an item repeated
one of more times.
item1 operation item2 operation ...} indicates
the item resulting after evaluating the expression within the curly parenthesis.
item1| item2
indicates that one of the two items, but not both, must be specified.
For example:
(copyable)] indicates that
(copyable) is optional (it will only be specified
for variables with old values in postconditions).
(inherit)(base-class-type)}* indicates that
(inherit)(base-class-type) can be repeated zero or more times (it
can be omitted, it will only be specified when subcontracting, and when
specified it can be repeated multiple times to support subcontracting from
multiple base classes in case of multiple inheritance).
(copyable)](argument-type)(argument-name)}+
indicates that the function arguments (when specified instead of (void))
must be repeated one or more times (together with their optional (copyable) tag).
(public)| (protected)| (private)] indicates that the
access level is optional but when specified it must be either (public), (protected),
or (private).
The syntaxes of signature-sequence used by CONTRACT_CONSTRUCTOR()
and CONTRACT_DESTRUCTOR() are somewhat different because C++ uses
different syntactic elements for constructor and destructor declarations
than for member functions (constructors cannot be virtual, destructors cannot
have function arguments, etc). However, the syntax for these signature-sequences
are still obtained applying the basic rule of listing the constructor and
destructor signature tokens in the exact same order as they appear in the
constructor and destructor declarations.
signature-sequence syntax for
CONTRACT_CONSTRUCTOR():
{(class) | (struct)} (class-type) {(public) | (protected) | (private)} [(template)( {(function-template-parameter-type)(function-template-parameter-name)}+ )] [(explicit)] [(inline)] (class-name)( {(void) | {[(copyable)](argument-type)(argument-name)}+} )
As usual, class-name is used as the function-name
for constructors and there is no result-type. Furthermore,
the library does not currently error if (explicit)
is not specified in signature-sequence even if the contracted
constructor is explicit.
[12]
signature-sequence syntax for
CONTRACT_DESTRUCTOR():
{(class) | (struct)} (class-type) {(public) | (protected) | (private)} [(inline)] [(virtual)] (class-name)( (void) )
Note that class-name and not ~class-name is used as
function-name for destructors ("~"
cannot be used because it is not a valid preprocessor token). Furthermore,
as usual destructors have no result-type and no function
arguments (the argument list is always specified (void)).
![]() |
Important |
|---|---|
|
Not in Signature Sequence
The keywords |
The usual C++ syntax constraints apply. For example, (static) cannot
be specified together with (virtual).
Furthermore, the tokens must be specified in the order listed above even
if the C++ syntax allows for different ordering. For example, (const)
(volatile) must be specified in this order, and not
as (volatile) (const), even if C++ accepts both of orderings.
Fixing one specific ordering does not alter the semantics of the code.
[13]
The library will generate compile-time errors if signature-sequence contains tokens combinations which are not valid respect to the C++ syntax.
![]() |
Warning |
|---|---|
|
Cryptic Preprocessing Errors There is only a limited amount of compile-time error checking which this library can do on signature-sequence. In some cases, and depending on the compiler used, an error in programming the signature-sequence syntax will result in cryptic compiler errors.
These compiler errors might involve library internal templates and macros
as well as Boost.Preprocessor
internal macros ( The best way to resolve these errors is usually to inspect the signature-sequence by eye instead of trying to make sense of the compiler error messages. Also, try to compile with contracts turned off to make sure that the errors are actually in the contract code. Rarely, it might be useful to look at the code generated by the contract macro expansion after preprocessing using your compiler related options ("-E -P" on GCC, "/EP" on Microsoft Visual C++, etc). While the C++ preprocessor imposes limits on the amount of error checking that can be performed on signature-sequence, the current library implementation does not implement the best possible preprocessor error detection and reporting strategy. This is an area of improvement for the library that is currently being worked on. Please report on the library help website the criptic preprocessing errors you experience in order to facilitate this process. |
Note that within signature-sequence, multiple function arguments (as well as function template parameters) are not separated by commas (you can use a space or a new line instead):
(function-name)( (argument-type1)(argument-name1) /* no comma (a space or new line can be used instead) */ (argument-type2)(arguments-name2) /* no comma (a space or new line can be used instead) */ (argument-type3)(argument-name3) ... )
Furthermore, if the function has no arguments, the argument list must be
specified void:
(function-name)( (void) )
Note that (function-name)( ) cannot be used instead and it will generate a cryptic
compile-time error.
![]() |
Note |
|---|---|
|
An empty function argument list cannot be represented simply by empty parenthesis
The C99 standard instead allows for empty preprocessor sequence elements
This library supports both syntaxes
The same limitation applies to the |
The library supports subcontracting from multiple base classes in case of multiple inheritance. Contracts are for pure virtual functions are also supported as usual when body definition is deferred to the derived classes (see below).
However, note that constructors and destructors do not directly subcontract
(their signature-sequences do not accept any (inherit)(base-class-type) token). This is because the C++ object construction
and destruction mechanism will automatically execute the code that checks
the base class constructor and destructor contracts if present.
In case of multiple inheritance, it is possible to subcontract from multiple
base classes by repeating (inherit)(base-class-type) multiple times in signature-sequence.
For example, assuming myvector
is inheriting and subcontracting from both the boundable and the basic_begin
base classes:
template<typename T> class myvector: public boundable<typename std::vector<T>::const_iterator>, private basic_begin<typename std::vector<T>::const_iterator> { ... // Class invariants. public: typedef std::vector<T>::const_iterator const_iterator; const_iterator begin(void) const CONTRACT_FUNCTION( (class) (myvector) // Multiple inheritance. (inherit)(boundable<const_iterator>) (inherit)(basic_begin<const_iterator>) (public) (const_iterator) (begin)( (void) ) (const) // No preconditions (omitted). (postcondition) (result) ({ // Return value `result` in postconditions. if (empty()) CONTRACT_ASSERT( result == end() ); }) (body) ({ // Must use CONTRACT_BODY() when calling the base function. if (basic_begin<const_iterator>::CONTRACT_BODY(begin) == const_iterator()) { return const_iterator(); } return vector_.begin(); }) ) ... // Rest of the class. private: std::vector<T> vector_; };
The subcontracted contracts are checked in the order in which their (inherit) tokens are listed in signature-sequence
and the derived class contracts are checked last.
![]() |
Note |
|---|---|
|
The above implies that if a derived class relaxes the precondition of an overridden function, all the base contract preconditions will have be checked first to fails before the relaxed preconditions of the overridden function are checked to pass. In this case, it would appear more efficient to check the derived class contract first. However, the derived class invariants could be written assuming that the base class invariants hold true (for example the base class invariants could assert a pointer to be not null and thus the derived class invariants could deference the pointer without checking for nullity). In doing so, the derived class invariants assume that they are checked last in AND with all base classes invariants. The same argument can be made for postconditions. Therefore, if derived class invariants (and postconditions) should be checked last in AND with all base classes invariants (and postconditions). It is natural to follow the same policy and check derived class function preconditions last in the OR with all base class function preconditions (even if this introduces the inefficiency to have to check and fail all the base class function preconditions when a derived class function if relaxing the base class function preconditions). |
When the derived class invokes the overridden function in the base class
the CONTRACT_BODY()
macro must be used:
base-class::CONTRACT_BODY(function-name)(...)
as illustrated by the example above. The overriding function should not call the overridden function directly without using the macro:
base-class::function-name)(...)
because this call will cause the contracts to executed infinite recursive
calls (due to the dynamic binding of the contracted virtual base function
base-class::function-name).
[14]
![]() |
Warning |
|---|---|
|
Base Calls via CONTRACT_BODY()
Overriding functions must use base-class |
The library supports contracts for operators.
For operators, the (function-name) token in signature-sequence
must contain both the operator symbol and the operator name
spelled out in words without using any special symbol (this is necessary
because, in general, the operator special symbols ([],
==, <<,
etc) are not valid preprocessor tokens). For operators, (function-name) takes the following form:
(operator(symbol, name))
Note the necessary double closing parenthesis "))"
at the end. For example:
template<typename T> class myvector { ... // Class invariants. public: typedef typename std::vector<T>::size_type size_type; typedef typename std::vector<T>::const_reference const_reference; const_reference operator[](size_type index) const CONTRACT_FUNCTION( (class) (myvector) (public) (const_reference) (operator([], at))( (size_type)(index) ) (const) (precondition) ({ CONTRACT_ASSERT( index < size() ); }) // No postconditions (omitted). (body) ({ return vector_[index]; }) ) ... // Rest of the class. private: std::vector<T> vector_; };
The operator name, in this example "at",
is arbitrary and any name without special symbols can been used.
The library supports contracts for overloaded functions.
![]() |
Note |
|---|---|
|
However, the library uses the argument names
to distinguish the overloaded function signatures whereas C++ uses the
argument types (this is because, in general, the argument types, for example
If two functions share the same name, the same number of arguments, and
the same |
For example:
class number { public: // Overloaded functions distinguished using argument names (not types) and const. number add(const int& n) // (1) CONTRACT_FUNCTION(...) // OK -- different from (1) because this is `const`. number& add(const int& n) const CONTRACT_FUNCTION(...) // OK -- different from (1) because argument named "d" instead of "n". number add(const double& d) CONTRACT_FUNCTION(...) // Error -- same as (1) because both non-const and with argument named "n" (even if different argument type). number add(const double& n) CONTRACT_FUNCTION(...) // Same as (1), error! ... };
The (template) token is used in signature-sequence
to specify a function template listing the relative template parameters.
The template parameters are specified using a syntax similar to the one of
the function arguments.
For example:
template<typename T> class myvector { ... // Class invariants. private: template<class Iter> static bool all_equals(Iter first, Iter last, const T& element) CONTRACT_FUNCTION( (class) (myvector) (private) (template)( (class)(Iter) ) (static) (bool) (all_equals)( (Iter)(first) (Iter)(last) (const T&)(element) ) (precondition) ({ CONTRACT_ASSERT( first < last ); }) (body) ({ for (Iter i = first; i < last; ++i) { if (*i != element) return false; } return true; }) ) ... // Rest of the class. };
In this example, the function also happens to be a static member but any function (non-member, member, static member, constructor, etc) can be be declared a template function as usual in C++.
In principle, the library also supports contracts for export
class and function templates. For non-member function templates (export)
can be specified within the signature-sequence as usual.
However, most compilers do not support export
templates and this library feature has not being tested.
Class invariants, preconditions, and postconditions are part of the function specifications (indeed they assert the function specifications). Therefore, this library requires contracts to be defined together with the function declarations. However, the function body (i.e., the function implementation) can be defined either together with the function declaration or separately.
In other words, the usual C++ feature that allows to separate a function definition from its declaration is retained by this library.
When the function body is defined separately from the function declaration:
(body)(;) tokens are used to define the body
in the macro parameter passed to CONTRACT_FUNCTION(). The ";"
symbol is the usual C++ symbol used to separate the function definition
from its declaration.
CONTRACT_BODY(function-name) macro is used to name the function where
it is defined. Again, for operators function-name
must be specified as operator(symbol, name).
CONTRACT_CONSTRUCTOR_BODY(class-type, class-name) and CONTRACT_DESTRUCTOR_BODY(class-type, class-name) should be used when the constructor and
destructor are defined respectively (note again that "~"
is not specified to name destructors, just use the
class name instead).
For example:
// Declarations (usually in header files). template<typename T> class myvector: public boundable<typename std::vector<T>::const_iterator>, private basic_begin<typename std::vector<T>::const_iterator> { ... // Class invariants. public: typedef typename std::vector<T>::size_type size_type; typedef typename std::vector<T>::const_reference const_reference; typedef std::vector<T>::const_iterator const_iterator; myvector(const myvector& right) CONTRACT_CONSTRUCTOR( (class) (myvector) (public) (myvector)( (const myvector&)(right) ) (postcondition) ({ CONTRACT_ASSERT( vector_ == right.vector_ ); }) (body) ( ; // Deferres body definition. ) ) virtual ~myvector(void) CONTRACT_DESTRUCTOR( (class) (myvector) (public) (virtual) (myvector)( (void) ) (body) ( ; ) ) const_iterator begin(void) const CONTRACT_FUNCTION( (class) (myvector) (inherit)(boundable<const_iterator>) (inherit)(basic_begin<const_iterator>) (public) (const_iterator) (begin)( (void) ) (const) (postcondition) (result) ({ if (empty()) CONTRACT_ASSERT( result == end() ); }) (body) ( ; ) ) const_reference operator[](size_type index) const CONTRACT_FUNCTION( (class) (myvector) (public) (const_reference) (operator([], at))( (size_type)(index) ) (const) (precondition) ({ CONTRACT_ASSERT( index < size() ); }) (body) ( ; ) ) ... // Rest of the class. private: std::vector<T> vector_; }; // Separated definitions (eventually in a different file). template<typename T> CONTRACT_CONSTRUCTOR_BODY(myvector<T>, myvector)(const myvector& right) { vector_ = right.vector_; } template<typename T> CONTRACT_DESTRUCTOR_BODY(myvector<T>, myvector)(void) { // Do nothing in this case. } template<typename T> typename myvector<T>::iterator myvector<T>::CONTRACT_BODY(begin)(void) { return vector_.begin(); } template<typename T> typename myvector<T>::const_reference myvector<T>:: CONTRACT_BODY(operator([], at))(size_type index) const { return vector_[index]; }
In addition to the usual benefits of separating function definitions from
their declarations (smaller and more readable header files, less recompilation
needed, etc), this separation also improves the library compile-time error
messages. When a function is defined together with its declaration, the function
implementation code is passed as one single macro parameter (body)({
... })
to the contract macros. Macro preprocessing removes all newline characters
from within the macro parameters so the implementation code is compiled as
if it were written on a single line. As a result, any compile-time error
within the function body code will be reported having the same line number
and it will be harder to track. Instead, if the body definition is separated
from the function declaration, the definition code is not wrapped within
a macro parameter and the compiler errors will indicate useful line numbers.
![]() |
Note |
|---|---|
It is recommended to separate the body definition from the function declaration so that line numbers in compiler errors retain their usual values. |
Finally, note how the signature-sequence unusual syntax does not propagate to the function definitions (only the function name is changed in the function definitions). Therefore, when definitions are separated from declarations, the files containing the definitions (usually the ".cpp" files) will not contain the unusual signature-sequence syntax and they will be easier to read.
![]() |
Warning |
|---|---|
|
Constructor Member Initialization List
Because of a library limitation, it is not possible to separate a constructor
definition from its declaration when the constructor uses a member initialization
list (see |
The library also supports contracts for pure virtual functions.
In this case, the (body)(= 0;)
tokens are used to define the body in the macro parameter passed to CONTRACT_FUNCTION().
The "= 0;" symbol is
the usual C++ symbol used to specify pure virtual functions.
For example:
template<typename T> class pushable { ... public: virtual void push_back(const T& element) CONTRACT_FUNCTION( (class) (pushable) (public) (virtual) (void) (push_back)( (const T&)(element) ) (postcondition) ({ CONTRACT_ASSERT( back() == element ); }) (body) ( = 0; ) ) // Pure virtual body. };
The library supports contracts for friend non-member functions (and friend classes work as usual).
However, when a friend non-member function is contracted:
CONTRACT_BODY(function-name), and not the contracted function function-name,
must be declared friend using .
For example:
class point { public: point(const double& x, const double& y): x_(x), y_(y) {} // Declare friend the body and separate the definition. friend std::ostream& CONTRACT_BODY(operator(<<, out))( std::ostream& s, const point& o); private: double x_; double y_; }; std::ostream& operator<<(std::ostream& s, const point& o) CONTRACT_FUNCTION( (std::ostream&) (operator(<<, out))( (std::ostream&)(s) (const point&)(o) ) (body) ({ return s << "(" << o.x_ << ", " << o.y_ << ")"; }) )
Note that (class) and (struct) can
be used interchangeably in the signature-sequence as
the library makes no differentiation between contracts
for classes and a structs. However, as always remember:
(public),
etc.
CONTRACT_INVARIANT() in a private section also for struct.
struct to have no non-constant public member
variable).
For example:
struct positive { const double& number; // Remember to always specify the access level (public, etc). positive(const double& n): number(n) CONTRACT_CONSTRUCTOR( (struct) (positive) // (struct) in sequence. (public) (positive)( (const double&)(n) ) (precondition) ({ CONTRACT_ASSERT( n > 0.0 ); }) (body) ({ }) ) private: // Remember to always specify class invariants in a private section. CONTRACT_INVARIANT( ({ CONTRACT_ASSERT( number > 0.0 ); }) ) };
This library does not support contracts for unions and
(union) cannot be specified in the signature-sequence.
Contracts for unions are not supported because union member variable allocations
overlap and they cannot have constructors instead the library needs to augment
the object state using a (non overlapping) contract::state
member variable with a default constructor to disable invariant checking
in nested function calls.
This library supports contracts also when constant, linkage, storage, etc qualifiers are specified.
The const and volatile qualifiers are repeated in the signature-sequence
and they can be used as usual. However, when they are both specified they
must appear in the order (const) (volatile) and not (volatile) (const).
The invariant, precondition, and postcondition functions are always const to ensure contract constant-correctness.
In addition, the eventual const
and volatile qualifiers specified
for the contracted function are applied to the body, precondition, and postcondition
functions as well. Therefore, applying the usual const
volatile semantics, note the followings:
const
and const volatile
member.
const
and const members can access
any const and const volatile
member.
volatile
members can only access const volatile members.
For example:
class z { CONTRACT_INVARIANT( ({ // const CONTRACT_ASSERT( check_const() && check_cv() ); }) ) public: int n(int x) CONTRACT_FUNCTION( (class) (z) (public) (int) (n)( (int)(x) ) // no-cv (precondition) ({ // const CONTRACT_ASSERT( check_const() && check_cv() ); }) (postcondition) (result) ({ // const CONTRACT_ASSERT( check_const() && check_cv() ); }) (body) ({ // no-cv check_nocv(); check_const(); check_volatile(); check_cv(); return x; }) ) int c(int x) const CONTRACT_FUNCTION( (class) (z) (public) (int) (c)( (int)(x) ) (const) (precondition) ({ // const CONTRACT_ASSERT( check_const() && check_cv() ); }) (postcondition) (result) ({ // const CONTRACT_ASSERT( check_const() && check_cv() ); }) (body) ({ // const check_const(); check_cv(); return x; }) ) int v(int x) volatile CONTRACT_FUNCTION( (class) (z) (public) (int) (v)( (int)(x) ) (volatile) (precondition) ({ // const volatile CONTRACT_ASSERT( check_cv() ); }) (postcondition) (result) ({ // const volatile CONTRACT_ASSERT( check_cv() ); }) (body) ({ // volatile check_volatile(); check_cv(); return x; }) ) int cv(int x) const volatile // const appears before volatile. CONTRACT_FUNCTION( (class) (z) (public) (int) (cv)( (int)(x) ) (const) (volatile) (precondition) ({ // const volatile CONTRACT_ASSERT( check_cv() ); }) (postcondition) (result) ({ // const volatile CONTRACT_ASSERT( check_cv() ); }) (body) ({ // const volatile check_cv(); return x; }) ) // Used to demonstrate qualified member access constraints. bool check_nocv() { return true; } bool check_const() const { return true; } bool check_volatile() volatile { return true; } bool check_cv() const volatile { return true; } };
The inline qualifier is repeated
in the signature-sequence and it is used as usual. For
example:
class z { CONTRACT_INVARIANT( ({}) ) public: inline void inl(int x) CONTRACT_FUNCTION( (class) (z) (public) (inline) (void) (inl)( (int)(x) ) (body) ({ std::cout << x << std::endl; }) ) }; inline void inl(int x) CONTRACT_FUNCTION( (inline) (void) (inl)( (int)(x) ) (body) ({ std::cout << x << std::endl; }) )
The extern linkage qualifier
is repeated in the signature-sequence for non-member
functions and it is used as usual. For example:
extern void ext(int x) // Some external linkage. CONTRACT_FUNCTION( (extern) (void) (ext)( (int)(x) ) (body) ({ std::cout << x << std::endl; }) ) extern "C" void ext_c(int x) // External linkage callable from C. CONTRACT_FUNCTION( (extern "C") (void) (ext_c)( (int)(x) ) (body) ({ std::cout << x << std::endl; }) )
The static qualifier is used
as usual for static member functions and it is specified in signature-sequence.
The static qualifier for non-member
functions to indicate non-members local to a translation unit was deprecated
from C to C++ (see [Stroustrup1997]) therefore it is not supported by this
library and it cannot be specified in signature-sequence.
The auto and register
storage qualifiers are not repeated in the signature-sequence
but they can appear in the contracted function as usual. When these qualifiers
are used, they will have effect for the contracted function but not for the
contract (i.e., these qualifier will not be applied to the precondition,
postcondition, and body function arguments). For example:
// `auto` is not repeated in signature sequence. double square(auto double d) CONTRACT_FUNCTION( (double) (square)( (double)(d) ) (postcondition) (result) ({ CONTRACT_ASSERT( result == (d * d) ); }) (body) ({ auto double y = d * d; return y; }) ) // `register` is not repeated in signature sequence. int square(register int i) CONTRACT_FUNCTION( (int) (square)( (int)(i) ) (postcondition) (result) ({ CONTRACT_ASSERT( result == (i * i) ); }) (body) ({ register int y = i * i; return y; }) )
Block invariants CONTRACT_ASSERT_BLOCK_INVARIANT() can be used to assert conditions anywhere
within a code block (not just loops). When used within a loop, block invariants
can be used to assert loop invariants.
Loop variants can be used to check the correctness of a loop, including its termination, as explained in detail in [Meyer1997] (and they can only appear within loops). At each loop iteration, the specified loop variant integer expression is automatically asserted to be positive (> 0) and to decrease from the previous loop iteration. Loop variants can be used to assert loop correctness. Because the variant decreases and it cannot be zero or smaller than zero it is possible to guarantee loop termination. A loop can only have one variant.
When asserting loop variants using CONTRACT_ASSERT_LOOP_VARIANT() it is necessary to first call CONTRACT_INIT_LOOP_VARIANT
once (and only once) within a code block that has same of higher scope level
than the loop code block.
For example:
double abs_total(const myvector<double>& vector) CONTRACT_FUNCTION( (double) (abs_total)( (const myvector<double>&)(vector) ) (postcondition) (total) ({ // Result value named `total`. CONTRACT_ASSERT( total >= 0.0 ); }) (body) ({ double total = 0.0; // Block invariants can appear anywhere in code block. CONTRACT_ASSERT_BLOCK_INVARIANT( total == 0.0 ); { // Variant initialized locally to its loop. CONTRACT_INIT_LOOP_VARIANT; for (size_t i = 0; i < vector.size(); ++i) { // Block invariants used to assert loop invariants. CONTRACT_ASSERT_BLOCK_INVARIANT( i < vector.size() ); // Loop variant (can only appear in loops). CONTRACT_ASSERT_LOOP_VARIANT( vector.size() - i ); total += vector[i]; } } return total < 0.0 ? -total : total; }) )
Exception specifications are not repeated in the signature-sequence but they can be specified for contracted member functions as usual.
However, non-member functions with exception specifications cannot be contracted (the library will generated a compile-time error otherwise). A possible workaround for this library limitation is to make the non-member functions with exception specifications static members of some artificially introduced class so they can be contracted. For example:
class c { // Some artificially introduced class. CONTRACT_INVARIANT( ({}) ) public: static void some(const std::string& what) throw(char, int) CONTRACT_FUNCTION( (class) (c) // Ex. specs not repeated in signature. (public) (static) (void) (some)( (const std::string&)(what) ) (body) ({ if (what == "none") return; // OK. if (what == "char") throw char('a'); // OK. if (what == "int") throw int(-1); // OK. throw what; // Not in specs so terminate() is called. }) ) static void none(const std::string& what) throw() CONTRACT_FUNCTION( (class) (c) // Ex. specs not repeated in signature. (public) (static) (void) (none)( (const std::string&)(what) ) (body) ({ if (what == "none") return; // OK. throw what; // Not in specs so terminate() is called. }) ) };
The library uses the following rules to handle exceptions thrown by the contracts:
Therefore, exception specifications work as usual as long as the member function
body does not fail the class invariant when it throws the exception. Furthermore,
if the contract failure handlers are redefined to throw exceptions (instead
of calling std::terminate(),
see Throw on Failure)
then the exception specifications still work as usual because they apply
to both the exceptions thrown by the body and the exceptions thrown by the
contract failure handlers.
The C++ preprocessor only recognizes the ()
parenthesis. It does not recognize any other parenthesis such as <>, [],
or {}. As a consequence, any
comma passed within a macro parameter that is not wrapped by the () parenthesis will be interpreted by the
preprocessor as a parameter separation token and will generate a preprocessing
error. Also macro parameters passed as elements of Boost.Preprocessor
sequences cannot contain commas not wrapped by extra ()
parenthesis (the preprocessor sequence ()
parenthesis are not sufficient to wrap the commas).
Consider the following example:
template<typename K, typename T> std::map<K, T> fill(const std::map<K, T>& source, const K& key, const T& element) CONTRACT_FUNCTION( (template)( (typename)(K) (typename)(T) ) // Commas within another type expression. (std::map<K, T>) (set)( // Commas within a type expression. (const std::map<K, T>&)(source) (const K&)(key) (const T&)(element) ) (precondition) ({ // Commas within a value expression. CONTRACT_ASSERT( std::map<K, T>().empty() ); }) (body) ({ ... }) )
The example above will not compile because the preprocessor will interpret the expression:
CONTRACT_ASSERT( std::map<K, T>().empty() );
as a call to the macro CONTRACT_ASSERT() passing two parameters
separated by the comma:
std::map<K // 1st macro parameter. T>().empty() // 2nd macro parameter.
Because CONTRACT_ASSERT() takes only one parameter, the above code
will generate a preprocessing error.
The expressions std::map<K, T>().empty() is interpreted by the compiler as a value
expression (it will be either be true
or false). Value expressions
can be wrapped by an extra set of parenthesis ()
when passed as macro parameters. The following will compile:
CONTRACT_ASSERT( (std::map<K, T>().empty()) );
A similar issue arises for the preprocessor sequence element:
(std::map<K, T>)
which will be interpreted as composed of 2 parameters separated by the comma (i.e., as a Boost.Preprocessor 2-tuple instead than an element of a Boost.Preprocessor sequence):
std::map<T // 1st macro parameter. T> // 2nd macro parameter.
However, in this case the expression std::map<K, T> needs to be interpreted by the compiler
as a type expression (indeed the type std::map<K, T>) and it cannot be wrapped by the extra
parenthesis (). In fact, depending
on the context where they are used, types wrapped within parenthesis can
generate compiler-time syntactic errors.
In order to wrap the type expression within parenthesis ()
that can be parsed correctly by the preprocessor and interpreted correctly
the compiler, first we created a void-function type that contains the type
expression as the function only argument type:
void(std::map<K, T>) // Function type (wraps comma within parenthesis).
Then, we apply the library metafunction contract::wrap
that returns the type of the first argument of the specified void-function
type:
contract::wrap<void(std::map<K, T>)>::type // Evaluates to the type `std::map<K, T>`.
For convenience, the library provides the macro CONTRACT_WRAP_TYPE() which expands to the code above:
CONTRACT_WRAP_TYPE( (std::macp<K, T>) )
Note the extra parenthesis ()
similar to what it was used for value expression.
Finally, both techniques are applied to eventual commas within code blocks depending if the code is a value or type expression.
![]() |
Note |
|---|---|
However, if the body definition is separated from the contract declaration then the body code does not appear within a macro parameter so there is no need to implement these workarounds in the body code. |
Applying these techniques to the example above, we obtain the following code which compiles:
template<typename K, typename T> std::map<K, T> fill(const std::map<K, T>& source, const K& key, const T& element) CONTRACT_FUNCTION( (template)( (typename)(K) (typename)(T) ) // Commas within type expression using the macro. (typename CONTRACT_WRAP_TYPE( (std::map<K, T>) )) (set)( // Or equivalently, not using the macro. (typename contract::wrap<void (const std::map<K, T>&) >::type)(source) (const K&)(key) (const T&)(element) ) (precondition) ({ // Commas within value expressions must be wrapped by `()`. CONTRACT_ASSERT( (std::map<K, T>().empty()) ); }) (body) ({ // Commas within code blocks use same workarounds as above // wrapping differently commas within type or value expressions. // Or better, separate body definition so it is outside the macro. // OK, commas already wrapped by function call `()`. print(key, element); // Commas within type expression wrapped using the macro. typename CONTRACT_WRAP_TYPE((std::map<K, T>)) m = source; // OK, commas already wrapped by if-statement `()`. if (0 == std::map<K, T>().empty()) m[key] = element; return m; }) )
We conclude this section with a fully working example that can be compiled. This example illustrates how to use the library contract macros to program contracts in all the different library usages scenarios (constructor, destructors, member functions, non-member functions, template functions, etc).
For simplicity, this example only exposes a limited subset of the std::vector
operations and it only programs simple contracts for them (see the STL Vector
Example for complete contracts
of all std::vector operations). Furthermore, the somewhat
artificial base classes have been deliberately introduced to illustrate subcontracting
and they will probably not be part of real code.
// Base classes for subcontracting. #include "pushable.hpp" #include "boundable.hpp" #include "basic_begin.hpp" #include <contract.hpp> // This library. #include <boost/utility.hpp> // For `boost::prior()`. #include <vector> // STL vector. // Wrapper that adds simple (not complete) contracts to C++ STL illustrating // most library usages. For simplicity, assume T is comparable and copyable. template<typename T> class myvector: public pushable<T>, public boundable<typename std::vector<T>::const_iterator>, private basic_begin<typename std::vector<T>::const_iterator> { // Class invariants (checked by any function with a contract). CONTRACT_INVARIANT( (static)({ // Static invariants (optional). // Static class invariants `(static)({ ... })` are optional and they // could have been omitted since they assert nothing in this example. // When present, they can only access static members. }) ({ // Non-static invariants (can access object). CONTRACT_ASSERT( (size() == 0) == empty() ); // More invariants here... }) ) public: typedef typename std::vector<T>::size_type size_type; typedef typename std::vector<T>::iterator iterator; typedef typename std::vector<T>::const_iterator const_iterator; typedef typename std::vector<T>::const_reference const_reference; // Contract for constructor. explicit myvector(size_type count): vector_(count) CONTRACT_CONSTRUCTOR( // Constructor contract macro. (class) (myvector) // Constructor signature. (public) (explicit) (myvector)( (size_type)(count) ) // Never object `this` in constructor preconditions. (postcondition) ({ // Never `CONTRACT_OLDOF(this)` in constructor postconditions. CONTRACT_ASSERT( size() == count ); }) (body) ({ // Do nothing in this case. }) ) // Contractor for overloaded member (resolved by argumnet name). myvector(const myvector& right) CONTRACT_CONSTRUCTOR( (class) (myvector) (public) (myvector)( (const myvector&)(right) ) (postcondition) ({ CONTRACT_ASSERT( vector_ == right.vector_ ); }) (body) (;) ) // Deferres body definition. // Contract for destructor. virtual ~myvector(void) CONTRACT_DESTRUCTOR( // Destructor contract macro. (class) (myvector) // Destructor signature. // Must use `(void)` for no arguments. (public) (virtual) (myvector)( (void) ) // No preconditions allowed (no arguments). // No postconditions allowed (no object after destructor). (body) (;) ) // Contract for member function. void insert(iterator where, size_type count, const T& element) CONTRACT_FUNCTION( // Function contract macro. (class) (copyable)(myvector) // Function signature. // Old values for object `this` and argument `where`. (public) (void) (insert)( (copyable)(iterator)(where) (size_type)(count) (const T&)(element) ) (precondition) ({ // Function preconditions (optional). CONTRACT_ASSERT( (size() + count) <= max_size() ); // More preconditions here... }) (postcondition) ({ // Function postconditions (optional). // Any C++ code allowed in contracts (but keep it simple). // Old values of types tagged copyable via `CONTRACT_OLDOF()`. if (capacity() == CONTRACT_OLDOF(this)->capacity()) { CONTRACT_ASSERT( all_equals( boost::prior(CONTRACT_OLDOF(where)), boost::prior(CONTRACT_OLDOF(where)) + count, element) ); } // More postconditions here... }) (body) ({ // Function body (mandatory). vector_.insert(where, count, element); }) ) // Contract for constant member. const_iterator begin(void) const CONTRACT_FUNCTION( (class) (myvector) // Subcontracting for multiple inheritance. (inherit)(boundable<const_iterator>) (inherit)(basic_begin<const_iterator>) (public) (const_iterator) // Non-void result type. (begin)( (void) ) (const) // Constant. (postcondition) (result) ({ // Named result value. if (empty()) CONTRACT_ASSERT( result == end() ); }) (body) ({ return vector_.begin(); }) ) // Contract for overloaded member (resolved because not const). iterator begin(void) CONTRACT_FUNCTION( (class) (myvector) (public) (iterator) (begin)( (void) ) (postcondition) (result) ({ if (empty()) CONTRACT_ASSERT( result == end() ); }) (body) ( ; ) ) // Contract for operator. const_reference operator[](size_type index) const CONTRACT_FUNCTION( (class) (myvector) // Must spell operator name also in words). (public) (const_reference) (operator([], at))( (size_type)(index) ) (const) (precondition) ({ CONTRACT_ASSERT( index < size() ); }) (body) (;) ) // Main function example used in documentation. void push_back(const T& element) CONTRACT_FUNCTION( (class) (copyable)(myvector) (inherit)(pushable<T>) (public) (void) (push_back)( (const T&)(element) ) (precondition) ({ CONTRACT_ASSERT( size() < max_size() ); }) (postcondition) ({ CONTRACT_ASSERT( size() == (CONTRACT_OLDOF(this)->size() + 1) ); }) (body) ({ vector_.push_back(element); }) ) // Contract for template plus static member function. template<class Iter> static bool all_equals(Iter first, Iter last, const T& element) CONTRACT_FUNCTION( (class) (myvector) (public) (template)( (class)(Iter) ) // Function template. (static) (bool) (all_equals)( // Static member. (Iter)(first) (Iter)(last) (const T&)(element) ) (precondition) ({ CONTRACT_ASSERT( first < last ); }) (body) ({ // For simplicity, let's assume T can be compared. for (Iter i = first; i < last; ++i) { if (*i != element) return false; } return true; }) ) // Similarly, complete contracts sketched here and add contracts // for all other functions (see [Crowl2006] vector example). bool empty(void) const { return vector_.empty(); } size_type size(void) const { return vector_.size(); } size_type max_size(void) const { return vector_.max_size(); } size_type capacity(void) const { return vector_.capacity(); } iterator end(void) { return vector_.end(); } const_iterator end(void) const { return vector_.end(); } const_reference back(void) const { return vector_.back(); } private: std::vector<T> vector_; }; // Deferred constructor body definition. template<typename T> CONTRACT_CONSTRUCTOR_BODY(myvector<T>, myvector)( const myvector& right) { vector_ = right.vector_; } // Deferred destructor body definition. template<typename T> CONTRACT_DESTRUCTOR_BODY(myvector<T>, myvector)(void) { // Do nothing in this case. } // Deferred member function definition. template<typename T> typename myvector<T>::iterator myvector<T>::CONTRACT_BODY(begin)( void) { return vector_.begin(); } // Deferred member operator definition. template<typename T> typename myvector<T>::const_reference myvector<T>:: CONTRACT_BODY(operator([], at))( size_type index) const { return vector_[index]; } // Contract for non-member function. double abs_total(const myvector<double>& vector) CONTRACT_FUNCTION( (double) (abs_total)( (const myvector<double>&)(vector) ) (postcondition) (total) ({ // Result value named `total`. CONTRACT_ASSERT( total >= 0.0 ); }) (body) ({ double total = 0.0; // Block invariants can appear anywhere in code block. CONTRACT_ASSERT_BLOCK_INVARIANT( total == 0.0 ); { // Variant initialized locally to its loop. CONTRACT_INIT_LOOP_VARIANT; for (size_t i = 0; i < vector.size(); ++i) { // Block invariants used to assert loop invariants. CONTRACT_ASSERT_BLOCK_INVARIANT( i < vector.size() ); // Loop variant (can only appear in loops). CONTRACT_ASSERT_LOOP_VARIANT( vector.size() - i ); total += vector[i]; } } return total < 0.0 ? -total : total; }) )
#include <contract.hpp> template<typename T> class pushable { CONTRACT_INVARIANT( ({}) ) public: // Contract for pure virtual function. virtual void push_back(const T& element) CONTRACT_FUNCTION( (class) (pushable) (public) (virtual) (void) (push_back)( (const T&)(element) ) (postcondition) ({ CONTRACT_ASSERT( back() == element ); }) (body) (= 0;) ) // Pure virtual body. virtual const T& back() const = 0; };
#include <contract.hpp> template<class ConstIter> class boundable { CONTRACT_INVARIANT( ({ CONTRACT_ASSERT( begin() <= end() ); }) ) public: virtual ConstIter begin(void) const CONTRACT_FUNCTION( (class) (boundable) (public) (virtual) (ConstIter) (begin)( (void) ) (const) (body) (= 0;) ) virtual ConstIter end(void) const = 0; };
#include <contract.hpp> template<class ConstIter> class basic_begin { CONTRACT_INVARIANT( ({}) ) public: virtual ConstIter begin(void) const CONTRACT_FUNCTION( (class) (basic_begin) (public) (virtual) (ConstIter) (begin)( (void) ) (const) (body) ({ return ConstIter(); // Dummy implementation (for example only). }) ) };
[2] Static member functions cannot be virtual so they cannot be overridden and they cannot subcontract.
[3] However, the destructor body should be programmed to never throw exceptions to comply with C++ STL exception safety requirements.
[4]
As usual in C++, constant-correctness can be enforced at compile-time
only as long as programmers do not use const_cast
and mutable.
[5]
To improve contract readability, it is recommended to configure your
editor C++ syntax highlighting to highlight also the macros CONTRACT_INVARIANT, CONTRACT_FUNCTION,
CONTRACT_CONSTRUCTOR,
CONTRACT_DESTRUCTOR,
CONTRACT_OLDOF, CONTRACT_BODY, CONTRACT_CONSTRUCTOR_BODY,
CONTRACT_DESTRUCTOR_BODY,
CONTRACT_ASSERT, CONTRACT_ASSERT_MSG, CONTRACT_ASSERT_BLOCK_INVARIANT, CONTRACT_ASSERT_BLOCK_INVARIANT_MSG,
CONTRACT_ASSERT_LOOP_VARIANT,
CONTRACT_ASSERT_LOOP_VARIANT_MSG,
CONTRACT_INIT_LOOP_VARIANT,
and the sequence tokens precondition,
postcondition, body, copyable,
and inherit. For example,
this can be done in the Vi IMproved (VIM) editor by adding these symbols
to the "cpp.vim"
file (usually in the VIM system configuration directory).
[6]
Some text editing programs might highlight curly brackets {} within a macro parameter as a C++
syntax error. This is incorrect as the ISO C++ standard allows for
macro parameters to contain curly brackets and the editor might offer
an option for disabling this incorrect syntax error highlighting. For
example, when C++ syntax highlighting is turned on in the Vi IMproved
(VIM) editor, curly brackets within macro parameters are highlighted
as errors but that can be disabled by adding the line let c_no_curly_error=1 in
the ".vimrc"
file (typically in your home directory).
[7]
However, this is the only sensible semantics for copying pointers as
the pointer could of type void* for which it is not possible to copy
the pointed object, plus there might cases where the functions actually
changed the pointer value so CONTRACT_OLDOF() needs to the return the old pointer
value (and not the old pointed value), plus this the usual C++ pointer
copy semantic.
[8] [Meyer1997] argues preconditions asserted using non-public members are ill written thus the Eiffel programming language enforces this rule at compile-time. However, in C++ friend callers could still be able to fully check preconditions via the friendship even when non-public functions are used to assert them therefore this rule is left as a recommended practice for programmers to follow and it is not enforced by the library.
[9]
If two Boost.Preprocessor
sequences are specified one after the other then they are automatically
concatenated into one larger sequence. For example let seq1 be (token1) (token2) and seq2
be (token3) (token4) (token5) then seq1
seq2 is (token1) (token2) (token3) (token4) (token5).
[10] Because the signature-sequence essentially contains all the tokens necessary to declare the function, the contract macros could have been implemented to replace the function declaration entirely instead of being specified right after the function declaration. This would have saved the programmers from repeating the function declaration tokens twice, first to declare the function and then to invoke the contract macro. However, replacing the function declarations entirely with the unfamiliar looking signature-sequence would have drastically reduced readability therefore it was intentionally avoided.
[11] A constant-correct copy constructor constructs the object copying it from a constant-reference of the source object thus it cannot alter the state of the copied object.
[12]
However, this could change in future revisions of the library if C++
were to support delegating constructors (see [Sutter2005]).
Therefore, (explicit) is part of the library API and it should
always be specified in signature-sequence when the
contracted constructor is explicit.
[13] One specific token ordering was fixed so to simplify the implementation of the library code that parsers signature-sequence.
[14]
Using the augmented object state the library could detect infinite recursion
between overriding and overridden function. However, to break the recursion
the base contract will have to call the base body function forcing static
binding (otherwise using dynamic binding the overriding function will
be called causing the infinite recursion). The contract itself cannot
perform the static binding call (e.g., using static_cast<> to the
object) because the object state is changed only if pointers or references
to the objects are used to call the body, but if pointers or reference
are used then C++ uses dynamic binding for the call. So the contract
function could call a special method of the contract class which performs
the static binding call but such a static binding call will raise a compiler
error if the body function is pure virtual. The library does not know
directly when a function is pure virtual (body is =
0;)
or not so the library will have to define the contract class static binding
method also for pure virtual functions and in this case the static binding
call B::f()
will raise a compile time error because the function called via static
binding is pure virtual.
[15] The library could overcome this limitation if future versions of the C++ standard were to support delegating constructors (as proposed by [Sutter2005]).