PrevUpHomeNext

Tutorial

Contract Programming Overview
Checking Contracts
Contract Macros
Signature Sequence
Function Arguments
Inheritance
Operators
Overloaded Functions
Function Templates
Deferring the Body
Friends
Structs and Unions
Qualifiers
Blocks and Loops
Exception Specifications
Commas Within Macro Parameters
A Fully Working Example

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]):

  1. It is possible to describe class invariants. These are logical conditions that programmers expect to be true after the constructor has been executed successfully, before and after the execution of every non-static member function with a contract, and before the destructor is executed (e.g., class invariants can define a valid state for all objects of a class). It is possible to describe static class invariants also which are expected to be true before and after the execution of any member function with a contract (including static member functions, constructor entry, and destructor exit).
  2. It is possible to describe function preconditions. These are logical conditions that programmers except to be true when the function is called (e.g., to check constraints on input function arguments).
  3. It is possible to describe function postconditions. These are logical conditions that programmers expect to be true when the function has ended normally (e.g., to check the result and any side effect that a function might have).
  4. It is possible to formalize the notion of overriding a virtual member function via subcontracting. Subcontracting can be justified by substitution principles and it consists of the following rules that the overriding function must obey:
    1. Preconditions cannot be strengthen.
    2. Postconditions cannot be weaken.
    3. Class invariants cannot be weaken.
  5. It is possible to describe block invariants. These are logical conditions that programmes except to be true every time the execution reaches the point where the condition is asserted. When used within a loop (i.e., a block of code that can be executed in iteration), block invariants function like loop invariants asserting conditions that are excepted to be true for every loop iteration.
  6. It is possible to describe a loop variant. This a positive integer expression with a value that is expected to decrease at every subsequent loop iteration.

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.

Non-Member Function Call Semantics

A non-member function call should execute the following steps:

  1. Check preconditions.
  2. Execute function body.
  3. Check postconditions.
Member Function Call Semantics

A member function call should execute the following steps:

  1. Check static class invariants.
  2. Check non-static class invariants AND, when subcontracting, check the base class invariants (for non-static member functions only [2] ).
  3. Check preconditions OR, when subcontracting, check the overridden function preconditions.
  4. Execute function body.
  5. Check static class invariants (even if body throws an exception).
  6. Check non-static class invariants AND, when subcontracting, check base class invariants (for non-static member functions only plus checked even if body throws an exception).
  7. Check postconditions AND, when subcontracting, check overridden function postconditions (only if body did not throw an exception).

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:

  • Class invariants are checked in AND with the invariants of all the base classes (following the inheritance order).
  • Preconditions are checked in OR with the preconditions of all the overridden functions (following the inheritance order).
  • Postconditions are checked in AND with the postconditions of all the overridden functions (following the inheritance order).
Constructor Call Semantics

A constructor call should execute the following steps:

  1. Initialize member variables via the constructor member initialization list (if such a list is specified).
  2. Check static class invariants (but not the non-static class invariants).
  3. Check preconditions.
  4. Execute constructor body.
  5. Check static class invariants (even if body throws an exception).
  6. Check class invariants (even if body throws an exception).
  7. Check postconditions (only if body did not throw an exception).

Before constructor body execution, there is no object therefore:

  • Non-static class invariants do not have to hold true and they are not checked at constructor entry.
  • Preconditions cannot access the object.
  • Postconditions cannot access the old object value (as it was before body execution).
Destructor Call Semantics

A destructor call should execute the following steps:

  1. Check static class invariants.
  2. Check class invariants.
  3. Execute destructor body.
  4. Check static class invariants (but not the non-static class invariants plus checked even if body throws an exception [3] ).

Note that:

  • Destructors have no arguments so they have no preconditions.
  • After destructor body execution, there is no object anymore (because it has been destroyed) so non-static class invariants do not have to hold true and they are not checked at destructor exit.
  • Destructors have no postconditions as there is no function argument and after body execution there is no object.

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).

Constant-Correctness

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:

  • signature-sequence: A Boost.Preprocessor sequence with the function or constructor signature tokens (see subsections below).
  • (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:

  • signature-sequence: The destructor signature tokens (see below).
  • (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] 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] 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 CONTRACT_OLDOF() and not the old pointed value. In other words, as usual, shallow copies are performed for pointers (unless the pointed type defines a different copy operation). Be careful as this might not be what you intended when using CONTRACT_OLDOF() on a pointer [7] .

The notable exception is the object this which is passed to the contract functions by pointer but its old value is automatically deep copied by the library.

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] Note

This macro is similar to the C assert() macro as it can be used at any point within a block of code. However, in case the asserted condition either throws an exception or it is evaluated to be false, this macro invokes contract::block_invariant_failed() instead of calling abort().

CONTRACT_ASSERT_LOOP_VARIANT( integer_expression )

CONTRACT_INIT_LOOP_VARIANT

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).

Step-by-Step Example

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:

  1. The class invariant asserts that the vector is empty if and only if its size is zero.
  2. The precondition asserts that the vector size must be smaller than the maximum size when push_back() is called (so there is available space to add one extra vector element).
  3. The postcondition asserts that the vector size must increase by 1 after 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).

Contract Code Blocks

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] 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] Important

Signature Sequence Mnemonic

The tokens in the signature sequence appear in the exact same order as they appear in the relative function declaration.

An Example

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):

  1. The class keyword for myvector (this indicates that push_back is a member function).
  2. The myvector class type (this indicates that push_back is a member function of myvector).
  3. The 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).
  4. The public access level of the push_back() member function.
  5. The void return type.
  6. The push_back function name.
  7. The parenthesis "(" to open the function argument list.
  8. The const T& function argument type.
  9. The element function argument name.
  10. The parenthesis ")" 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:

  1. 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).
  2. 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.

Full Syntax

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] Important

Not in Signature Sequence

The keywords friend, throw (for exception specifications), auto, register, and union do not appear in the signature-sequence (the library will generate compile-time errors otherwise). However, these keywords and the related features are still supported for the contracted functions as discussed in the rest of this section (with the exception of union for which contracts are not supported).

Syntax Errors

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] 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 (BOOST_PP_...). Furthermore, the error line number will only refer to the first line of code at which the contract macro appears (because new lines within macro parameters are removed by the preprocessor).

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] Note

An empty function argument list cannot be represented simply by empty parenthesis () as in (function-name)() because the C++ standard does not allow to specify empty preprocessor sequence elements like () thus (function-name)( (void) ) must be used instead. Note that the C++ standard allows to use void to specify a function with no arguments function-name(void) (this is the syntax used in all the examples of this documentations to be consistent with the relative signature-sequence syntax requirement).

The C99 standard instead allows for empty preprocessor sequence elements () (because it allows for empty macro parameters, see [Nelson2004] and Boost.Preprocessor for details). C99 also defines deprecated the use of void for functions with no arguments -- function-name(void) is deprecated. Therefore, on C99 compilers (function-name)() should be used in signature-sequence for functions with no arguments instead of (function-name)( (void) ).

This library supports both syntaxes (function-name)( (void) ) and (function-name)() in signature-sequence but the later syntax will only compile for C99 (so it is not recommended).

The same limitation applies to the CONTRACT_INVARIANT() macro when the class has no invariant. Also in this case the library supports both syntaxes CONTRACT_INVARIANT( ({}) ) and CONTRACT_INVARIANT() bu the later syntax will only compile for C99 (so it is not recommended).

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.

Multiple Inheritance

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] 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).

Base Function Call

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] Warning

Base Calls via CONTRACT_BODY()

Overriding functions must use base-class::CONTRACT_BODY(function-name) and not just base-class::function-name when calling the overridden functions (otherwise the contract calls will cause infinite recursion).

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] 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 const int&, are not valid preprocessor tokens).

If two functions share the same name, the same number of arguments, and the same const qualifier then their argument names (and not just their types) must be different to allow the library to distinguish these two functions from each other. (This is usually not a significant limitation because different argument names can be given.)

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.

Separating Declaration and Definition

When the function body is defined separately from the function declaration:

  • The (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.
  • The 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).
  • Similarly, it is possible to separate constructor and destructor body definitions. In this case, the macros 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] 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] 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 CONTRACT_CONSTRUCTOR_BODY() macro for details and workarounds) [15] .

Pure Virtual Functions

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:

  1. The function definition must always be separated from its friendship declaration (whereas, C++ allows to define friends function in place together with they friendship declarations).
  2. The body function 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:

  1. To always specify the member access level (public), etc.
  2. To use CONTRACT_INVARIANT() in a private section also for struct.
  3. That no contract is checked (not event the class invariants) when member variables are accessed directly (so it might be best for the 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.

Const and Volatile

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:

  1. Class invariants can access any const and const volatile member.
  2. Preconditions and postconditions of non-const and const members can access any const and const volatile member.
  3. Preconditions and postconditions of 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; }
};

Inline

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;
}) )

Extern and Static

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.

Auto and Register

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:

  1. When an exception is thrown while checking invariants, preconditions, or postconditions then the contract condition is considered failed (because it was not possible to check it to be true) so the relative contract failure handler function is invoked.
  2. When an exception is thrown while executing the body then the class invariants are checked (for member functions only) and, if they hold true, the exception is passed up to the caller as usual (otherwise the class invariant failed handler function is invoked).

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) ({
    ...
}) )
Value Expressions

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()) );
Type Expressions

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.

Body Code Block

Finally, both techniques are applied to eventual commas within code blocks depending if the code is a value or type expression.

[Note] 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]).


PrevUpHomeNext