mirror of
https://github.com/boostorg/contract.git
synced 2026-02-26 16:42:19 +00:00
782 lines
61 KiB
Plaintext
782 lines
61 KiB
Plaintext
|
|
[/ Copyright (C) 2008-2016 Lorenzo Caminiti]
|
|
[/ Distributed under the Boost Software License, Version 1.0 (see accompanying]
|
|
[/ file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).]
|
|
[/ See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html]
|
|
|
|
[section Tutorial]
|
|
|
|
This section illustrates basic uses of this library.
|
|
|
|
[section Non-Member Functions]
|
|
|
|
Contracts for non-member functions are programmed using [funcref boost::contract::function].
|
|
For example (see also [@../../example/features/non_member.cpp =non_member.cpp=]):
|
|
|
|
[import ../example/features/non_member.cpp]
|
|
[non_member]
|
|
|
|
All necessary header files of this library are included by `#include <boost/contract.hpp>`.
|
|
Alternatively, programmers can selectively include only the header files they actually need among =boost/contract/*.hpp= (see __Getting_Started__).
|
|
|
|
It is possible to specify preconditions, postconditions, and exception guarantees for non-member functions (see __Preconditions__, __Postconditions__, and __Exception_Guarantees__).
|
|
|
|
The [funcref boost::contract::function] function returns an RAII object that must always be assigned to a local variable of type [classref boost::contract::check] (otherwise this library will generate a run-time error, see also [macroref BOOST_CONTRACT_ON_MISSING_CHECK_DECL]).
|
|
Furthermore, C++11 `auto` declarations cannot be used here and the [classref boost::contract::check] type must be explicitly specified (otherwise this library will generate a compile-time error).
|
|
[footnote
|
|
The name of this local variable is arbitrary, but `c` is often used in this documentation.
|
|
]
|
|
The function body is programmed right after the declaration of the RAII object.
|
|
|
|
[note
|
|
In some cases, it might be necessary to program some code before the contract.
|
|
For example for acquiring resources that will be used while checking the contract like old values, but also to lock mutexes (or other synchronization mechanisms) in multi-threaded programs (as usual with C++, in these cases it is generally preferred to use RAII objects to automatically control acquisition and release of the resources).
|
|
]
|
|
|
|
At construction, the RAII object does the following (enclosing function entry):
|
|
|
|
# Check preconditions, by calling the nullary functor [^['r]]`()` passed to `.precondition(`[^['r]]`)`.
|
|
|
|
At destruction instead (enclosing function exit):
|
|
|
|
# If the function body did not throw an exception:
|
|
# Check postconditions, by calling the nullary functor [^['s]]`()` passed to `.postcondition(`[^['s]]`)`.
|
|
# Else:
|
|
# Check exception guarantees, by calling the nullary functor [^['e]]`()` passed to `.except(`[^['e]]`)`.
|
|
|
|
This ensures that non-member function contracts are correctly checked at run-time (see __Function_Calls__).
|
|
|
|
[note
|
|
A non-member function can avoid calling [funcref boost::contract::function] for efficiency but only when it has no preconditions, no postconditions, and no exception guarantees.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Preconditions]
|
|
|
|
When preconditions are specified, they are programmed using a functor [^['r]] passed to `.precondition(`[^['r]]`)` that can be called with no parameter [^['r]]`()`.
|
|
Contracts that do not have preconditions simply do not call `.precondition(...)`.
|
|
When they are all specified, preconditions must appear before postconditions and exception guarantees (see __Postconditions__ and __Exception_Guarantees__).
|
|
|
|
C++11 lambda functions are convenient to program preconditions, but any other nullary functor can be used (see __No_Lambda_Functions__).
|
|
[footnote
|
|
Lambda functions with no parameters can be programmed in C++11 as `[...] () { ... }` but also equivalently as `[...] { ... }`.
|
|
This second from is often used in this documentation omitting the empty parameter list `()` for brevity.
|
|
]
|
|
For example, for [funcref boost::contract::function] (same for public functions, but destructors do not have preconditions and constructors use [classref boost::contract::constructor_precondition] instead):
|
|
|
|
boost::contract::check c = boost::contract::function() // Same for all other contracts.
|
|
.precondition([&] { // Capture by reference or value...
|
|
BOOST_CONTRACT_ASSERT(...); // ...but should not modify captures.
|
|
...
|
|
})
|
|
...
|
|
;
|
|
|
|
The precondition functor should capture all the variables that it needs to assert the preconditions.
|
|
These variables can be captured by value when the overhead of copying such variables is acceptable (but in this documentation preconditions often capture these variables by reference to avoid such overhead).
|
|
In any case, precondition assertions should not modify the value of the captured variables, even when those are captured by reference (see __Constant_Correctness__).
|
|
|
|
Any code can be programmed in the precondition functor, but it is recommended to keep this code simple using mainly assertions and if-statements (to avoid programming complex preconditions that might be buggy and also slow to check at run-time).
|
|
It is also recommended to use [macroref BOOST_CONTRACT_ASSERT] to program precondition assertions because this enables the library to print informative error messages when the asserted conditions are evaluated to be false (this is not a variadic macro, see __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if boolean-condition contains commas `,` not already within parenthesis `(...)`...
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``)) // ...use extra parenthesis.
|
|
|
|
This library will automatically call the failure handler [funcref boost::contract::precondition_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] conditions are `false` and also if calling the functor specified via `.precondition(...)` throws an exception (by default, this handler prints an error message to `std::cerr` and terminates the program calling `std::terminate`, but see __Throw_on_Failure__ to throw exceptions, exit the program with an error code, etc.).
|
|
|
|
[note
|
|
Contracts are most useful when their assertions only use public members that are accessible to the caller so the caller can properly check and use the contract.
|
|
In particular, preconditions of a public member function or constructor that use non-public members are essentially incorrect because they cannot be fully checked by the caller (in fact, Eiffel generates a compile-time error in this case).
|
|
|
|
This library leaves it up to the programmers to only use public members when programming contracts, and especially when programming preconditions (see __Specification_vs_Implementation__).
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Postconditions]
|
|
|
|
When postconditions are specified, they are programmed using a functor [^['s]] passed to `.postcondition(`[^['s]]`)` that can be called with no parameter [^['s]]`()`.
|
|
Contracts that do not have postconditions simply do not call `.postcondition(...)`.
|
|
When they are all specified, postconditions must appear after preconditions but before exception guarantees (see __Preconditions__ and __Exception_Guarantees__).
|
|
|
|
C++11 lambda functions are convenient to program postconditions, but any other nullary functor can be used (see __No_Lambda_Functions__).
|
|
For example, for [funcref boost::contract::function] (but same for all other contracts):
|
|
|
|
boost::contract::check c = boost::contract::function() // Same for all other contracts.
|
|
...
|
|
.postcondition([&] { // Capture by reference...
|
|
BOOST_CONTRACT_ASSERT(...); // ...but should not modify captures.
|
|
...
|
|
})
|
|
...
|
|
;
|
|
|
|
The postcondition functor should capture all variables that it needs to assert the postconditions.
|
|
In general, these variables should be captured by reference and not by value (because postconditions need to access the value that these variables will have at function exit, and not the value these variables had when the postcondition functor was first constructed).
|
|
Postconditions can also capture return and old values (see __Return_Value__ and __Old_Values__).
|
|
In any case, postcondition assertions should not modify the value of the captured variables (see __Constant_Correctness__).
|
|
|
|
Any code can be programmed in the postcondition functor, but it is recommended to keep this code simple using mainly assertions and if-statements (to avoid programming complex postconditions that might be buggy and slow to check at run-time).
|
|
It is also recommended to use [macroref BOOST_CONTRACT_ASSERT] to program postcondition assertions because this enables the library to print informative error messages when the asserted conditions are evaluated to be false (this is not a variadic macro, see also __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if boolean-condition has commas `,` not already within parenthesis `(...)`...
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``)) // ...use extra parenthesis.
|
|
|
|
This library will automatically call the failure handler [funcref boost::contract::postcondition_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] conditions are `false` and also if calling the functor specified via `.postcondition(...)` throws an exception (by default, this handler prints an error message to `std::cerr` and terminates the program calling `std::terminate`, but see __Throw_on_Failure__ to throw exceptions, exit the program with an error code, etc.).
|
|
|
|
For non-void virtual and overriding public member functions, the functor [^['s]] passed to `.postcondition(`[^['s]]`)` is not a nullary functor, instead it is a unary functor taking a variable holding the return value as its one parameter [^['s]]`(`[^['result]]`)` (so to properly support subcontracting, see __Virtual_Public_Functions__ and __Public_Function_Overrides__).
|
|
|
|
[endsect]
|
|
|
|
[section Return Value]
|
|
|
|
In non-void functions, postconditions can access the function return value to program assertions.
|
|
In this case, programmers are responsible to declare a local variable before the contract and to assign it to the return value at function exit (when the function does not throw an exception).
|
|
[footnote
|
|
The name of the local variable that holds the return value is arbitrary, but `result` is often used in this documentation.
|
|
]
|
|
For example, for [funcref boost::contract::function] (but same for all other contracts):
|
|
|
|
``/return-type/`` result; // Must be assigned to return value.
|
|
boost::contract::check c = boost::contract::function() // Same for all other contracts.
|
|
...
|
|
.postcondition([&] { // Also capture `result` reference...
|
|
BOOST_CONTRACT_ASSERT(result ...); // ...but should not modify captures.
|
|
...
|
|
})
|
|
...
|
|
;
|
|
|
|
At any point where the enclosing function returns, programmers are responsible to assign the result variable to the expression being returned.
|
|
This can be easily done by making sure that /all/ `return` statements in the function are of the form:
|
|
|
|
return result = ``/expression/``; // Assign `result` at each return.
|
|
|
|
The functor used to program postconditions should capture the result variable by reference and not by value (because postconditions must access the value the result variable will have at function exit, and not the value the result variable had when the postcondition functor was first constructed).
|
|
The return value should never be used in preconditions, old value copies, or exception guarantees (because the return value is not yet correctly evaluated and set when preconditions are checked, old values are copied, or if the function throws an exception).
|
|
In any case, programmers should not modify the result variable in the contract assertions (see __Constant_Correctness__).
|
|
|
|
It is also possible to declared the result variable using `boost::optional` when the function return type does not have a default constructor, or if the default constructor is too expensive or undesirable to execute (see __Optional_Return_Value__).
|
|
|
|
Non-void virtual and overriding public member functions must always declare and use a result variable even when postconditions do not directly use the function return value (so to properly support subcontracting, see __Virtual_Public_Functions__ and __Public_Function_Overrides__).
|
|
|
|
[endsect]
|
|
|
|
[section Old Values]
|
|
|
|
When old values are used in postconditions or in exception guarantees, programmes are responsible to declare local variables before the contract and to assign them to related old value expressions using [macroref BOOST_CONTRACT_OLD].
|
|
[footnote
|
|
The name of a local variable that holds an old value is arbitrary, but [^old_['name]] is often used in this documentation.
|
|
]
|
|
For example, for [funcref boost::contract::function] (but same for all other contracts):
|
|
|
|
boost::contract::old_ptr<``/type/``> old_``/name/`` = BOOST_CONTRACT_OLD(``/expression/``);
|
|
boost::contract::check c = boost::contract::function() // Same for all other contracts.
|
|
...
|
|
.postcondition([&] { // Capture by reference...
|
|
BOOST_CONTRACT_ASSERT(*old_``/name/`` ...); // ...but should not modify captures.
|
|
...
|
|
})
|
|
.except([&] { // Capture by reference...
|
|
BOOST_CONTRACT_ASSERT(*old_``/name/`` ...); // ...but should not modify captures.
|
|
...
|
|
})
|
|
;
|
|
|
|
Old values are handled by this library using the smart pointer class template [classref boost::contract::old_ptr] (so programmers do not directly manage the allocation and deallocation of the pointed memory).
|
|
[footnote
|
|
*Rationale:*
|
|
Old values are optional values because they need to be left uninitialized in case postconditions and exception guarantees are disabled defining [macroref BOOST_CONTRACT_NO_POSTCONDITIONS] and [macroref BOOST_CONTRACT_NO_EXCEPTS] to avoid old value copies in that case (so a pointer or better a `boost::optional` could be used for that).
|
|
Furthermore, old values need to be pointers internally allocated by this library so that they are never copied twice even when calling an overridden function multiple times to check preconditions, postconditions, etc. to implement subcontracting (so a smart pointer class template was used).
|
|
]
|
|
The pointed old value is automatically qualified as `const` (so old values cannot be mistakenly changed by the contract assertions, see __Constant_Correctness__).
|
|
This library ensures that old value pointers are always not null by the time postconditions and exception guarantees are checked (so programmers can safely dereference these pointers in postcondition and exception-guarantee assertions using `operator*` or `operator->` without having to check if old value pointers are not null first).
|
|
|
|
Old values should not be used in preconditions and this library does not guarantee that old value pointers are always not null when preconditions are checked (for example, when postconditions and exception guarantees are disabled defining [macroref BOOST_CONTRACT_NO_POSTCONDITIONS] and [macroref BOOST_CONTRACT_NO_EXCEPTS], when checking an overridden virtual public function contract via subcontracting, etc.).
|
|
Furthermore, see __Old_Values_at_Body__ for delaying the assignment of old values until after class invariants (for constructors, destructors, and public member functions) and preconditions are checked (this allows to program old value expressions under the simplifying assumption that class invariant and precondition assertions are satisfied already).
|
|
|
|
[macroref BOOST_CONTRACT_OLD] is a variadic macro and it takes an extra parameter when used in virtual or overriding public functions (see __Virtual_Public_Functions__ and __Public_Function_Overrides__).
|
|
C++11 auto declarations can be used with [macroref BOOST_CONTRACT_OLD] for brevity `auto `[^old_['name] = BOOST_CONTRACT_OLD(['expression])].
|
|
See __No_Macros__ to program old values without using [macroref BOOST_CONTRACT_OLD] (e.g., on compilers that do not support variadic macros).
|
|
|
|
[note
|
|
This library ensures that old values are copied only once.
|
|
This library also ensures that old values are never copied when postconditions and exception guarantees are disabled defining both [macroref BOOST_CONTRACT_NO_POSTCONDITIONS] and [macroref BOOST_CONTRACT_NO_EXCEPTS] (note that both these two macros must be defined, defining only [macroref BOOST_CONTRACT_NO_POSTCONDITIONS] or only [macroref BOOST_CONTRACT_NO_EXCEPTS] is not sufficient to prevent the run-time cost of old value copies).
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Exception Guarantees]
|
|
|
|
When exception guarantees are specified, they are programmed using a functor [^['e]] passed to `.except(`[^['e]]`)` that can be called with no parameter [^['e]]`()`.
|
|
Contracts that do not have exception guarantees simply do not call `.except(...)`.
|
|
When they are all specified, exception guarantees must appear after both preconditions and postconditions (see __Preconditions__ and __Postconditions__).
|
|
|
|
C++11 lambda functions are convenient to program exception guarantees, but any other nullary functor can be used (see __No_Lambda_Functions__).
|
|
For example, for [funcref boost::contract::function] (but same for all other contracts):
|
|
|
|
boost::contract::check c = boost::contract::function() // Same for all other contracts.
|
|
...
|
|
.except([&] { // Capture by reference...
|
|
BOOST_CONTRACT_ASSERT(...); // ...but should not modify captures.
|
|
...
|
|
})
|
|
;
|
|
|
|
The exception-guarantee functor should capture all variables that it needs to assert the exception guarantees.
|
|
In general, these variables should be captured by reference and not by value (because exception guarantees need to access the value that these variables will have when the function throws, and not the value these variables had when the exception-guarantee functor was first constructed).
|
|
Exception guarantees can also capture old values (see __Old_Values__) but they should not access the return value instead (because the return value will not be set when the function throws an exception).
|
|
In any case, exception-guarantee assertions should not modify the value of the captured variables (see __Constant_Correctness__).
|
|
|
|
Any code can be programmed in the exception-guarantee functor, but it is recommended to keep this code simple using mainly assertions and if-statements (to avoid programming complex exception guarantees that might be buggy and slow to check at run-time).
|
|
|
|
[note
|
|
In real code, it might be difficult to program meaningful exception guarantees without resorting to expensive old value copies that will slow down execution.
|
|
Therefore, the authors recognize that exception guarantees, even if supported by this library, might not be used often in practise (and they are not used in most of the examples listed in the rest of this documentation).
|
|
Instead, exception guarantees might be useful in those cases when they can be programmed just reusing the old value copies already present because used by postconditions.
|
|
]
|
|
|
|
It is also recommended to use [macroref BOOST_CONTRACT_ASSERT] to program exception-guarantee assertions because this enables the library to print informative error messages when the asserted conditions are evaluated to be false (this is not a variadic macro, see also __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if boolean-condition has commas `,` not already within parenthesis `(...)`...
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``)) // ...use extra parenthesis.
|
|
|
|
This library will automatically call the failure handler [funcref boost::contract::except_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] conditions are `false` and also if calling the functor specified via `.except(...)` throws an exception (by default, this handler prints an error message to `std::cerr` and terminates the program calling `std::terminate`).
|
|
|
|
[important
|
|
While it is technically possible for programmers to specify an exception-guarantee handler that throws an exception in case of an exception-guarantee failure, this will force C++ to terminate the program anyway.
|
|
That is because the handler will throw an exception while there is already an active exception on the stack (the exception thrown by the function body that caused this library to check the exception guarantees in the first place).
|
|
|
|
Therefore, programmers are strongly advised to not change the exception-guarantee failure handler to throw exceptions instead of terminating the program.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Class Invariants]
|
|
|
|
When class invariants are specified, they are programmed in a public `const` member function named `invariant` taking no argument and returning `void`.
|
|
Classes that do not have invariants, simply do not declare the `invariant` member function.
|
|
[footnote
|
|
This library uses template meta-programming (SFINAE-based introspection techniques) to check invariants only for classes that declare a member function named `invariant`.
|
|
]
|
|
For example:
|
|
|
|
class a {
|
|
public: // Must be public.
|
|
void invariant() const { // Must be const.
|
|
BOOST_CONTRACT_ASSERT(...);
|
|
...
|
|
}
|
|
|
|
...
|
|
};
|
|
|
|
This member function must be `const` because contracts should not modify the object state (see __Constant_Correctness__).
|
|
This library will generate a compile-time error if the `const` qualifier is missing (unless the [macroref BOOST_CONTRACT_PERMISSIVE] macro is defined).
|
|
|
|
Any code can be programmed in the `invariant` function, but it is recommended to keep this code simple using mainly assertions and if-statements (to avoid programming complex invariants that might be buggy and slow to check at run-time).
|
|
It is also recommended to use [macroref BOOST_CONTRACT_ASSERT] to program class invariant assertions because this enables the library to print informative error messages when the asserted conditions are evaluated to be false (this is not a variadic macro, see also __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if boolean-condition has commas `,` not already within parenthesis `(...)`...
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``)) // ...use extra parenthesis.
|
|
|
|
This library will automatically call failure handlers [funcref boost::contract::entry_invariant_failure] or [funcref boost::contract::exit_invariant_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] conditions are `false` and also if the `invariant` function throws an exception (by default, these handlers print an error message to `std::cerr` and terminate the program calling `std::terminate`, but see __Throw_on_Failure__ to throw exceptions, exit the program with an error code, etc.).
|
|
|
|
See __Access_Specifiers__ to avoid making the `invariant` member function `public`.
|
|
[footnote
|
|
In this documentation the `invariant` member function is often declared `public` for simplicity.
|
|
However, in production code it might not be acceptable to augment the public members of a class adding the `invariant` function (and that can be avoided using [classref boost::contract::access] as explained in __Access_Specifiers__).
|
|
]
|
|
See [macroref BOOST_CONTRACT_INVARIANT_FUNC] to use a name different from `invariant` (e.g., because `invariant` clashes with other names in user-defined classes).
|
|
|
|
[note
|
|
Contract assertion is checked (not event class invariants) when a data member is accessed directly (this is different from Eiffel where even accessing public data members checks class invariants).
|
|
Therefore, it might be best for both `class`es and `struct`s (and also `union`s, see __Unions__) that have invariants to have no mutable public data members and to access data members publicly only via appropriate public member functions (e.g., setters and getters) that can check the class invariants.
|
|
]
|
|
|
|
[heading Static Class Invariants]
|
|
|
|
When static class invariants are specified, they are programmed in a public `static` member functions named `static_invariant` taking no argument and returning `void`.
|
|
Classes that do not have static class invariants, simply do not declare a `static_invariant` member function.
|
|
[footnote
|
|
This library uses template meta-programming (SFINAE-based introspection techniques) to check static invariants only for classes that declare a member function named `static_invariant`.
|
|
]
|
|
For example:
|
|
|
|
class a {
|
|
public: // Must be public.
|
|
static void static_invariant() { // Must be static.
|
|
BOOST_CONTRACT_ASSERT(...);
|
|
...
|
|
}
|
|
|
|
...
|
|
};
|
|
|
|
This member function must be `static` (and it correctly cannot access the object `this`).
|
|
This library will generate a compile-time error if the `static` classifier is missing (unless the [macroref BOOST_CONTRACT_PERMISSIVE] macro is defined).
|
|
|
|
Any code can be programmed in the `static_invariant` function, but it is recommended to keep this code simple using mainly assertions and if-statements (to avoid programming complex static invariants that might be buggy and slow to check at run-time).
|
|
It is also recommended to use [macroref BOOST_CONTRACT_ASSERT] to program the assertions because this enables the library to print informative error messages when the asserted conditions are evaluated to be false (this is not a variadic macro, see also __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if condition has commas `,` not already within parenthesis `(...)`...
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``)) // ...use extra parenthesis.
|
|
|
|
This library will automatically call the failure handlers [funcref boost::contract::entry_invariant_failure] or [funcref boost::contract::exit_invariant_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] conditions are `false` and also if the `static_invariant` function throws an exception (by default, these handlers print an error message to `std::cerr` and terminate the program calling `std::terminate`, but see __Throw_on_Failure__ to throw exceptions, exit the program with an error code, etc.).
|
|
|
|
See __Access_Specifiers__ to avoid making `static_invariant` member function `public`.
|
|
[footnote
|
|
In this documentation the `static_invariant` member function is often declared `public` for simplicity.
|
|
However, in production code it might not be acceptable to augment the public members of a class adding the `static_invariant` function (and that can be avoided using [classref boost::contract::access] as explained in __Access_Specifiers__).
|
|
]
|
|
Set [macroref BOOST_CONTRACT_STATIC_INVARIANT_FUNC] to use a name different from `static_invariant` (e.g., because `static_invariant` clashes with other names in user-defined classes).
|
|
[footnote
|
|
*Rationale:*
|
|
In C++, it is not possible to overload a member function based on the `static` classifier.
|
|
Therefore, this library has to use different names for the member functions checking non-static and static class invariants (namely [macroref BOOST_CONTRACT_INVARIANT_FUNC] and [macroref BOOST_CONTRACT_STATIC_INVARIANT_FUNC]).
|
|
]
|
|
|
|
Finally, see __Volatile_Class_Invariants__ to program invariants for classes with volatile public member functions.
|
|
|
|
[endsect]
|
|
|
|
[section Constructors]
|
|
|
|
Contracts for constructors are programmed using [funcref boost::contract::constructor] and the [classref boost::contract::constructor_precondition] base class.
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[import ../example/features/public.cpp]
|
|
[public_class_begin]
|
|
[public_constructor]
|
|
[public_class_end]
|
|
|
|
It is not possible to specify preconditions using `.precondition(...)` for constructors (the library will generate a compile-time error if `.precondition(...)` is used on the object returned by [funcref boost::contract::constructor]).
|
|
Constructor preconditions are specified using the [classref boost::contract:constructor_precondition] base class instead (but the considerations from __Preconditions__ apply also to the precondition functor passed to [classref boost::contract::constructor_precondition]).
|
|
Programmes should not access the object `this` from constructor preconditions (because the object does not exists yet before the constructor body is executed).
|
|
[footnote
|
|
See __No_Lambda_Functions__ to enforce this at compile-time (not recommended).
|
|
]
|
|
Constructors without preconditions simply do not explicitly initialize the [classref boost::contract::constructor_precondition] base (in fact [classref boost::contract::constructor_precondition] default constructor checks no contract).
|
|
When [classref boost::contract::constructor_precondition] is used:
|
|
[footnote
|
|
There is a MSVC bug that was fixed in MSVC 2013 for which lambdas cannot be used in constructor member initialization lists for templates.
|
|
On MSVC compilers with that bug, an extra (static) member function can be used (together with `bind` and `cref` as needed) to program constructor preconditions instead of using lambdas.
|
|
]
|
|
|
|
* It should be specified as the /first/ class in the inheritance list (so constructor preconditions are checked before initializing any other base or data member).
|
|
* Its inheritance access specifier should always be `private` (so this extra base class does not alter the public inheritance tree of the derived class).
|
|
* It takes the derived class as template parameter (the Curiously Recursive Template Pattern (CRTP) is used here to avoid ambiguity errors with multiple inheritance).
|
|
[footnote
|
|
*Rationale:*
|
|
The [classref boost::contract::constructor_precondition] takes the derived class as its template parameter so the instantiated template type is unique for each derived class.
|
|
This always avoids base class ambiguity errors even when multiple inheritance is used.
|
|
Note that virtual inheritance could not be used instead of the template parameter here to avoid ambiguity errors (because virtual bases are initialized only once by the out-most derived class, and that would not allow to properly check preconditions of all base classes).
|
|
]
|
|
|
|
[note
|
|
A class can avoid inheriting from [classref boost::contract::constructor_precondition] for efficiency but only when all its constructors have no preconditions.
|
|
]
|
|
|
|
It is possible to specify postconditions for constructors (see __Postconditions__).
|
|
Programmers should not access the old value of the object `this` in constructor postconditions (because the object did not exist yet before the constructor body was executed).
|
|
[footnote
|
|
See __No_Lambda_Functions__ to enforce this at compile-time (not recommended).
|
|
]
|
|
The [funcref boost::contract::constructor] function takes `this` as a parameter (because constructors check class invariants, see __Class_Invariants__).
|
|
|
|
The [funcref boost::contract::constructor] function returns an RAII object that must always be assigned to a local variable of type [classref boost::contract::check] (otherwise this library will generate a run-time error).
|
|
Furthermore, C++11 `auto` declarations cannot be used here and the [classref boost::contract::check] type must be explicitly specified (otherwise this library will generate a compile-time error).
|
|
The constructor body is programmed right after the declaration of the RAII object.
|
|
At construction, the RAII object does the following (enclosing constructor entry):
|
|
|
|
# Check static class invariants, by calling [^['type-of]]`(*this)::static_invariant()` (but not non-static class invariants because the object does not exist yet).
|
|
|
|
At destruction instead (enclosing constructor exit):
|
|
|
|
# Check static class invariants, by calling [^['type-of]]`(*this)::static_invariant()`.
|
|
# If the constructor body did not throw an exception:
|
|
# Check non-static class invariants, by calling `this->invariant()`.
|
|
# Check postconditions, by calling the nullary functor [^['s]]`()` passed to `.postcondition(`[^['s]]`)`.
|
|
# Else:
|
|
# Check exception guarantees, by calling the nullary functor [^['e]]`()` passed to `.except(`[^['e]]`)`.
|
|
|
|
This together with C++ object construction mechanism of base classes and the use of [classref boost::contract::constructor_precondition] ensures that the constructor contracts are correctly checked at run-time (see __Constructor_Calls__).
|
|
|
|
[note
|
|
A constructor can avoid calling [funcref boost::contract::constructor] for efficiency but only when it has no postconditions, no exception guarantees, and its class has no invariants.
|
|
(Even if [funcref boost::contract::constructor] is not used by a derived class, contracts of base class constructors will still be correctly checked by C++ object construction mechanism.)
|
|
|
|
The default constructor and copy constructor automatically generated by C++ will not check contracts.
|
|
Therefore, unless these constructors are not public or they have no preconditions, no postconditions, no exception guarantees, and their class has no invariants, programmers should manually define them using [funcref boost::contract::constructor] and [classref boost::contract::constructor_precondition].
|
|
Similar considerations apply to all other operations automatically generated by C++ (move constructor, etc.).
|
|
]
|
|
|
|
Private and protected constructors can omit [funcref boost::contract::constructor] (because they are not part of the public interface of the class so they are technically not required to check class invariants, see __Constructor_Calls__).
|
|
They could still use [classref boost::contract::constructor_precondition] to check preconditions before member initializations, and even use [funcref boost::contract::function] (but not [funcref boost::contract::constructor]) to only check postconditions and exception guarantees without checking class invariants (see __Private_and_Protected_Functions__).
|
|
For example:
|
|
|
|
class a : private boost::contract::constructor_precondition<a> {
|
|
protected:
|
|
// Contract for a protected constructor (same for private constructors).
|
|
a() :
|
|
boost::contract::constructor_precondition<a>([&] {
|
|
BOOST_CONTRACT_ASSERT(...);
|
|
...
|
|
})
|
|
{
|
|
// Following will correctly not check class invariants.
|
|
boost::contract::check c = boost::contract::function()
|
|
// Do not use `.precondition(...)` here.
|
|
.postcondition([&] {
|
|
BOOST_CONTRACT_ASSERT(...);
|
|
...
|
|
})
|
|
;
|
|
|
|
... // Body.
|
|
}
|
|
|
|
...
|
|
};
|
|
|
|
[endsect]
|
|
|
|
[section Destructors]
|
|
|
|
Contracts for destructors are programmed using [funcref boost::contract::destructor].
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_class_begin]
|
|
[public_destructor]
|
|
[public_class_end]
|
|
|
|
It is not possible to specify preconditions for destructors (the library will generate a compile-time error if `.precondition(...)` is used here because destructors can be called at any time after construction so they have no precondition).
|
|
It is possible to specify postconditions for destructors (see __Postconditions__, and also __Static_Public_Functions__ for an example).
|
|
Programmers should not access the object `this` in destructor postconditions (because the object no longer exists after the destructor body has been executed).
|
|
[footnote
|
|
See __No_Lambda_Functions__ to enforce this at compile-time (not recommended).
|
|
]
|
|
The [funcref boost::contract::destructor] function takes `this` as a parameter (because destructors check class invariants, see __Class_Invariants__).
|
|
|
|
The [funcref boost::contract::destructor] function returns an RAII object that must always be assigned to a local variable of type [classref boost::contract::check] (otherwise this library will generate a run-time error).
|
|
Furthermore, C++11 `auto` declarations cannot be used here and the [classref boost::contract::check] type must be explicitly specified (otherwise this library will generate a compile-time error).
|
|
The destructor body is programmed right after the declaration of the RAII object.
|
|
At construction, the RAII object does the following (enclosing destructor entry):
|
|
|
|
# Check static and non-static class invariants, by calling ['[^type-of]]`(*this)::static_invariant()` __AND__ `this->invariant()`.
|
|
|
|
At destruction instead (enclosing destructor exit):
|
|
|
|
# Check static class invariants, by calling [^['type-of]]`(*this)::static_invariant()`.
|
|
# If the destructor body did not throw an exception:
|
|
# Check postconditions, by calling the nullay functor [^['s]]`()` passed to `.postcondition(`[^['s]]`)`.
|
|
# Else (even if destructors should generally be programmed not to throw in C++):
|
|
# Check non-static class invariants, by calling `this->invariant()` (because the object was not successfully destructed).
|
|
# Check exception guarantees, by calling the nullary functor [^['e]]`()` passed to `.except(`[^['e]]`)`.
|
|
|
|
This together with C++ object destruction mechanism of base classes ensures that destructor contracts are correctly checked at run-time (see __Destructor_Calls__).
|
|
|
|
[note
|
|
A destructor can avoid calling [funcref boost::contract::destructor] for efficiency but only when it has no postconditions, no exception guarantees, and its class has no invariants.
|
|
(Even if [funcref boost::contract::destructor] is not used by a derived class, contracts of base class destructors will still be correctly checked by C++ object destruction mechanism.)
|
|
|
|
The default destructor automatically generated by C++ will not check contracts.
|
|
Therefore, unless the destructor is not public or it has no postconditions, no exception guarantees, and its class has no invariants, programmers should manually define it using [funcref boost::contract::destructor].
|
|
]
|
|
|
|
Private and protected destructors can omit [funcref boost::contract::destructor] (because they are not part of the public interface of the class so they are technically not required to check class invariants, see __Destructor_Calls__).
|
|
They could use [funcref boost::contract::function] (but not [funcref boost::contract::destructor]) to only check postconditions and exception guarantees without checking class invariants (see __Private_and_Protected_Functions__).
|
|
For example:
|
|
|
|
class a {
|
|
protected:
|
|
// Contract for a protected destructor (same for private destructors).
|
|
virtual ~a() {
|
|
// Following will correctly not check class invariants.
|
|
boost::contract::check c = boost::contract::function()
|
|
// Do not use `.precondition(...)` here.
|
|
.postcondition([&] {
|
|
BOOST_CONTRACT_ASSERT(...);
|
|
...
|
|
})
|
|
;
|
|
|
|
... // Body.
|
|
}
|
|
|
|
...
|
|
};
|
|
|
|
[endsect]
|
|
|
|
[section Public Functions]
|
|
|
|
Contracts for public member functions are programmed using [funcref boost::contract::public_function].
|
|
In this section, let's consider public member functions that are not static, not virtual, and do not override any function from base classes.
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_class_begin]
|
|
[public_function]
|
|
[public_class_end]
|
|
|
|
It is possible to specify preconditions, postconditions, and exception guarantees for public member functions (see __Preconditions__, __Postconditions__, and __Exception_Guarantees__).
|
|
The [funcref boost::contract::public_function] function takes `this` as a parameter (because public member functions check class invariants, see __Class_Invariants__).
|
|
|
|
The [funcref boost::contract::public_function] function returns an RAII object that must always be assigned to a local variable of type [classref boost::contract::check] (otherwise this library will generate a run-time error).
|
|
Furthermore, C++11 `auto` declarations cannot be used here and the [classref boost::contract::check] type must be explicitly specified (otherwise this library will generate a compile-time error).
|
|
The public member function body is programmed right after the declaration of the RAII object.
|
|
At construction, the RAII object does the following (enclosing public function entry):
|
|
|
|
# Check static and non-static class invariants, by calling [^['type-of]]`(*this)::static_invariant()` __AND__ `this->invariant()`.
|
|
# Check preconditions, by calling the nullary functor [^['r]]`()` passed to `.precondition(`[^['r]]`)`.
|
|
|
|
At destruction instead (enclosing public function exit):
|
|
|
|
# Check static and non-static class invariants, by calling [^['type-of]]`(*this)::static_invariant()` __AND__ `this->invariant()` (even if the function body threw an exception).
|
|
# If the function body did not throw an exception:
|
|
# Check postconditions, by calling the nullary functor [^['s]]`()` passed to `.postcondition(`[^['s]]`)`.
|
|
# Else:
|
|
# Check exception guarantees, by calling the nullary functor [^['e]]`()` passed to `.except(`[^['e]]`)`.
|
|
|
|
This ensures that public member function contracts are correctly checked at run-time (see __Public_Function_Calls__).
|
|
|
|
[note
|
|
A public member function can avoid calling [funcref boost::contract::public_function] for efficiency but only when it has no preconditions, no postconditions, no exception guarantees, it is not virtual, it does not override any virtual function, and its class has no invariants.
|
|
|
|
The default copy assignment operator automatically generated by C++ will not check contracts.
|
|
Therefore, unless this operator is not public or it has no preconditions, no postconditions, no exception guarantees, and its class has no invariants, programmers should manually define it using [funcref boost::contract::public_function].
|
|
Similar considerations apply to all other operations automatically generated by C++ (move operator, etc.).
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Virtual Public Functions]
|
|
|
|
Contracts for public member functions are programmed using [funcref boost::contract::public_function].
|
|
In this section, let's consider public member functions that are virtual but that still do not override any function from base classes.
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_class_begin]
|
|
[public_virtual_function]
|
|
[public_class_end]
|
|
|
|
Public virtual functions must declare an extra trailing parameter of type [classref boost::contract::virtual_]`*` with default value `0` (i.e., `nullptr`).
|
|
[footnote
|
|
The name of this extra parameter is arbitrary, but `v` is often used in this documentation.
|
|
]
|
|
This extra parameter is the last parameter and it has a default value so it does not alter the calling interface of the virtual function in practice (callers will rarely, if ever, have to explicitly deal with this extra parameter, a part from when manipulating the virtual function type directly as a function pointer, for function pointer type-casts, etc.).
|
|
Programmers must pass the extra virtual parameter as the very first argument to all [macroref BOOST_CONTRACT_OLD] and [funcref boost::contract::public_function] calls in the virtual function definition.
|
|
[footnote
|
|
*Rationale:*
|
|
The [classref boost::contract::virtual_]`*` optional parameter is used by this library to determine that a function is virtual (in C++ it is not possible to introspect if a function is declared `virtual`).
|
|
Furthermore, this parameter is internally used by this library to pass result and old values that are evaluated by the overriding function to overridden virtual functions in base classes, and also to check preconditions, postconditions, and exception guarantees of overridden virtual functions when subcontracting (but without executing overridden function bodies).
|
|
]
|
|
|
|
The [funcref boost::contract::public_function] function takes `this` as a parameter (because public virtual functions check class invariants, see __Class_Invariants__).
|
|
|
|
As shown in the example above, when the public virtual function has a non-void return type programmers must pass a reference to the function return value as the second argument of [funcref boost::contract::public_function].
|
|
In this case, this library will pass the return value to the postcondition functor (that must therefore take one single argument matching the return type, possibly as a constant reference `const&` to avoid extra copies of the return value).
|
|
[footnote
|
|
*Rationale:*
|
|
The functor passed to `.postcondition(...)` takes the extra return value parameter because that is used by this library to pass the return value evaluated by the overriding function to all its overridden virtual functions when subcontracting.
|
|
]
|
|
Therefore, postconditions are checked by calling the nullary functor [^['s]]`()` passed to `.postcondition(`[^['s]]`)` for void public virtual functions, or the unary functor [^['s]]`(`[^['result]]`)` for non-void public virtual functions.
|
|
|
|
[important
|
|
It is the responsibility of the programmers to pass the extra parameter `v` to all [macroref BOOST_CONTRACT_OLD] and [funcref boost::contract::public_function] calls within public virtual functions, and also to pass the return value reference after `v` to [funcref boost::contract::public_function] for non-void public virtual functions.
|
|
This library cannot automatically generate compile-time errors if programmers fail to do so (but in general this will cause the library to not correctly check contracts at run-time).
|
|
[footnote
|
|
*Rationale:*
|
|
This library does not require the function type when using [funcref boost::contract::public_function] for non-overriding virtual functions.
|
|
Therefore, this library does not know if the enclosing function has a non-void return type so it cannot check if the return value reference is passed as required for non-overriding virtual functions.
|
|
Instead this library requires the function type for overriding virtual functions thus it gives a compile-time error if the return value reference is missing in those cases.
|
|
]
|
|
|
|
Remember: Always pass `v` as the first argument to old value macros, the contract, etc. for public virtual functions, and always pass the result variable after `v` to the contract for non-void public virtual functions.
|
|
]
|
|
|
|
For the rest, the same considerations made in __Public_Functions__ apply to public virtual functions as well.
|
|
|
|
[endsect]
|
|
|
|
[section Public Function Overrides (Subcontracting)]
|
|
|
|
Contracts for public member functions are programmed using [funcref boost::contract::public_function].
|
|
In this section, let's consider public member functions (virtual or not) that override public virtual functions from one or more public base classes.
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
[footnote
|
|
In this documentation, overriding functions are often marked with the code comment `/* override */`.
|
|
On compilers that support C++11 virtual specifiers, the `override` identifier can be used instead (`override` is not used in the documentation only because virtual specifiers are not widely supported yet, even by compilers that support C++11 lambda functions).
|
|
]
|
|
|
|
[public_derived_class_begin]
|
|
[public_function_override]
|
|
[public_derived_class_end]
|
|
|
|
The extra `typedef` that uses [macroref BOOST_CONTRACT_BASE_TYPES] is required by this library for derived classes and it is internally used detect base classes for subcontracting (see __Base_Classes__).
|
|
|
|
When called from overriding public functions, the [funcref boost::contract::public_function] function template takes an explicit template argument `override_`[^['function-name]] that must be defined by:
|
|
|
|
BOOST_CONTRACT_OVERRIDE(``['function-name]``)
|
|
|
|
This can be used at any point in the public section of the enclosing class (see __Access_Specifiers__ to use [macroref BOOST_CONTRACT_OVERRIDE] in a non-public section of the class instead).
|
|
[macroref BOOST_CONTRACT_OVERRIDE] must be used only once in a class for a given function name and overloaded functions can reuse the same [^override_['function-name]] definition (see __Overloaded_Functions__).
|
|
[macroref BOOST_CONTRACT_NAMED_OVERRIDE] can be used to generate a name different than [^override_['function-name]] (e.g., to avoid generating C++ reserved names containing double underscores "`__`" for function names that already start with an underscore "`_`", see __Named_Overrides__).
|
|
This library will generate a compile-time error if there is no suitable virtual function to override in any of the public base classes for subcontracting.
|
|
[footnote
|
|
This compile-time error generated by the library is similar in principle to the error generated by the C++11 `override` specifier, but it is limited to functions with the extra [classref boost::contract::virtual_]`*` parameter and searched recursively only in `public` base classes passed to [macroref BOOST_CONTRACT_BASE_TYPES] because only those are valid functions to override for subcontracting.
|
|
]
|
|
|
|
For convenience [macroref BOOST_CONTRACT_OVERRIDES] can be used with multiple function names instead of repeating [macroref BOOST_CONTRACT_OVERRIDE] for each function name (on compilers that support variadic macros).
|
|
For example, for three functions named `f`, `g`, and `h` (but same for any number of functions):
|
|
|
|
BOOST_CONTRACT_OVERRIDES(f, g, h)
|
|
|
|
The above is equivalent to:
|
|
|
|
BOOST_CONTRACT_OVERRIDE(f)
|
|
BOOST_CONTRACT_OVERRIDE(g)
|
|
BOOST_CONTRACT_OVERRIDE(h)
|
|
|
|
(However, note that there is not an equivalent to [macroref BOOST_CONTRACT_NAMED_OVERRIDE] that operates on multiple function names at once.)
|
|
|
|
Public function overrides must always list the extra trailing parameter of type [classref boost::contract::virtual_]`*` with default value `0` (i.e., `nullptr`), even when they are not declared `virtual` (because this parameter is present in the signature of the virtual function being overridden from base classes).
|
|
Programmers must pass the extra virtual parameter as the very first argument to all [macroref BOOST_CONTRACT_OLD] and [funcref boost::contract::public_function] calls in the function override definition (see __Virtual_Public_Functions__).
|
|
|
|
When called from a public function override, [funcref boost::contract::public_function] also takes a pointer to the enclosing function, the object `this` (because public function overrides check class invariants, see __Class_Invariants__), and references to each function argument in the order they appear in the function declaration.
|
|
[footnote
|
|
*Rationale:*
|
|
The object `this` is passed after the function pointer to follow `bind`'s syntax.
|
|
The function pointer and references to all function arguments are needed for overriding virtual public functions because this library has to call overridden virtual public functions to check their contracts for subcontracting (even if this library will not actually execute the bodies of the overridden functions).
|
|
]
|
|
|
|
As shown in the example above, when the overriding public function has a non-void return type programmers must pass a reference to the function return value as the second argument of [funcref boost::contract::public_function] (this library will generate a compile-time error otherwise).
|
|
[footnote
|
|
*Rationale:*
|
|
As for non-overriding public virtual functions, also overriding functions use the extra return value parameter to pass it to the overridden functions when subcontracting.
|
|
In the case of overriding functions this library also has the function pointer so it will generate a compile-time error if the function is non-void and programmers forget to specify the extra return value parameter (this extra error checking is not possible instead for non-overriding public virtual functions because their contracts do not take the function pointer as a parameter, see __Virtual_Public_Functions__).
|
|
]
|
|
In this case, this library will pass the return value to the postcondition functor (that must therefore take one single argument matching the return type, possibly as a constant reference `const&` to avoid extra copies of the return value).
|
|
This library will throw [classref boost::contract::bad_virtual_result_cast] if programmers specify return values for public function overrides in derived classes that are not consistent with the return types of the virtual public functions being overridden in the base classes.
|
|
[footnote
|
|
*Rationale:*
|
|
The `boost::bad_any_cast` exception could not used here because it does not print the from- and to- type names (so it is not descriptive enough).
|
|
]
|
|
|
|
[important
|
|
It is the responsibility of the programmers to pass the extra parameter `v` to all [macroref BOOST_CONTRACT_OLD] and [funcref boost::contract::public_function] calls within overriding public functions, and also to pass the return value reference after `v` to [funcref boost::contract::public_function] for non-void overriding public functions.
|
|
This library cannot always generate compile-time errors if programmers fail to do so (but in general this will cause the library to not correctly check contracts at run-time).
|
|
|
|
Remember: Always pass `v` as the first argument to old value macros, the contract, etc. for public function overrides, and always pass the result variable after `v` to the contract for non-void public function overrides.
|
|
]
|
|
|
|
At construction, the RAII object returned by the [funcref boost::contract::public_function]`<...>` function does the following (enclosing public function override entry):
|
|
|
|
# Check static and non-static class invariants for all overridden bases and for the derived class in __AND__ with each other, by calling [^['type-of](['overridden-base_1])]`::static_invariant()` __AND__ [^['overridden-base_1]]`.invariant()` __AND__... [^['type-of](['overridden-base_n])]`::static_invariant()` __AND__ [^['overridden-base_n]]`.invariant()` __AND__ [^['type-of]]`(*this)::static_invariant()` __AND__ `this->invariant()`.
|
|
# Check preconditions for all overridden base functions and for the overriding derived function in __OR__ with each other, by calling the nullary functors [^['r_1]]`()` __OR__... [^['r_n]]`()` __OR__ [^['r]]`()` passed to `.precondition(`[^['r_1]]`)`, ... `.precondition(`[^['r_n]]`)`, `.precondition(`[^['r]]`)` for all of the overridden and overriding functions respectively.
|
|
|
|
At destruction instead (enclosing public function override exit):
|
|
|
|
# Check static and non-static class invariants for all overridden bases and for the derived class in __AND__ with each other, by calling [^['type-of](['overridden-base_1])]`::static_invariant()` __AND__ [^['overridden-base_1]]`.invariant()` __AND__... [^['type-of](['overridden-base_n])]`::static_invariant()` __AND__ [^['overridden-base_n]]`.invariant()` __AND__ [^['type-of]]`(*this)::static_invariant()` __AND__ `this->invariant()` (even if the function body threw an exception).
|
|
# If the function body did not throw an exception:
|
|
# Check postconditions for all overridden base functions and for the overriding derived function in __AND__ with each other, by calling the nullary functors [^['s_1]]`()` __AND__... [^['s_n]]`()` __AND__ [^['s]]`()` passed to `.postcondition(`[^['s_1]]`)`, ... `.postcondition(`[^['s_n]]`)`, `.postcondition(`[^['s]]`)` for all of the overridden and overriding functions respectively (or the unary functors [^['s_1]]`(`[^['result]]`)` __AND__... [^['s_n]]`(`[^['result]]`)` __AND__ [^['s]]`(`[^['result]]`)` for non-void public function overrides).
|
|
# Else:
|
|
# Check exception guarantees for all overridden base functions and for the overriding derived function in __AND__ with each other, by calling the nullary functors [^['e_1]]`()` __AND__... [^['e_n]]`()` __AND__ [^['e]]`()` passed to `.except(`[^['e_1]]`)`, ... `.except(`[^['e_n]]`)`, `.except(`[^['e]]`)` for all of the overridden and overriding functions respectively.
|
|
|
|
This ensures that subcontracts of public function overrides are checked correctly at run-time (see __Public_Function_Calls__).
|
|
|
|
For the rest, the same considerations made in __Virtual_Public_Functions__ apply to public function overrides as well.
|
|
|
|
[endsect]
|
|
|
|
[section Base Classes (Subcontracting)]
|
|
|
|
In order for this library to support subcontracting, programmers must specify the bases of a derived class declaring a public member type named `base_types` via a `typedef` using [macroref BOOST_CONTRACT_BASE_TYPES].
|
|
For example (see also [@../../example/features/base_types.cpp =base_types.cpp=]):
|
|
|
|
[import ../example/features/base_types.cpp]
|
|
[base_types]
|
|
|
|
For convenience, a local macro named `BASES` can be used to avoid repeating the base list twice (first in the derived class declaration `class `[^['class-name]]` : `[^['base-list]] and then again when invoking [macroref BOOST_CONTRACT_BASE_TYPES]`(`[^['base-list]]`)`).
|
|
Being a local macro, `BASES` must be undefined using `#undef BASES` after it has been used to declare `base_types` (to avoid name clashes and macro redefinition errors).
|
|
[footnote
|
|
The name of this local macro is arbitrary, but `BASES` is often used in this documentation.
|
|
]
|
|
|
|
[macroref BOOST_CONTRACT_BASE_TYPES] is a variadic macro and accepts a list of bases separated by commas (see __No_Macros__ to program `base_types` without using macros).
|
|
As already noted in __Constructors__, when the extra base [classref boost::contract::constructor_precondition] is used to program constructor preconditions, it must always be private and appear as the very first base.
|
|
|
|
[important
|
|
Each base passed to [macroref BOOST_CONTRACT_BASE_TYPES] must /explicitly/ specify its inheritance access level `public`, `protected`, or `private` (but `virtual` is optional and can be specified either before or after the access level as usual in C++).
|
|
[footnote
|
|
*Rationale:*
|
|
This library explicitly requires the inheritance access level because derived classes must subcontract only from public bases, but not from protected or private bases (see __Public_Function_Calls__).
|
|
Therefore, [macroref BOOST_CONTRACT_BASE_TYPES] inspects each inheritance access level (using preprocessor meta-programming) and removes non-public bases from the list of bases to be for subcontracting (i.e., from `base_types`).
|
|
]
|
|
This library will generate a compiler-error if the first base is missing its inheritance access level, but this library will not be able to error if the access level is missing from bases after the first one.
|
|
|
|
It is the responsibility of the programmers to make sure that all bases passed to [macroref BOOST_CONTRACT_BASE_TYPES] explicitly specify their inheritance access level (inheritance access levels are instead optional in C++ because `private` is implicitly assumed for `class` types and `public` for `struct` types).
|
|
]
|
|
|
|
See __Access_Specifiers__ to avoid making the `base_types` member type `public`.
|
|
[footnote
|
|
In this documentation the `base_type` member type is often declared `public` for simplicity.
|
|
However, in production code it might not be acceptable to augment the public members of a class adding the `base_types` type (and that can be avoided using [classref boost::contract::access] as explained in __Access_Specifiers__).
|
|
]
|
|
See [macroref BOOST_CONTRACT_BASE_TYPEDEF] to use a name different from `base_types` (e.g., because `base_types` clashes with other names in user-defined classes).
|
|
|
|
[endsect]
|
|
|
|
[section Static Public Functions]
|
|
|
|
Contracts for public member functions are programmed using [funcref boost::contract::public_function].
|
|
In this section, let's consider static public member functions.
|
|
For example (see also [@../../example/features/static_public.cpp =static_public.cpp=]):
|
|
|
|
[import ../example/features/static_public.cpp]
|
|
[static_public]
|
|
|
|
It is possible to specify preconditions, postconditions, and exception guarantees for static public member functions (see __Preconditions__, __Postconditions__, and __Exception_Guarantees__).
|
|
However, when called from static public functions, [funcref boost::contract::public_function] cannot take the object `this` as a parameter (because there is no object `this` in static member functions) so the enclosing class type is is specified as an explicit template parameter (because that type is necessary to check static class invariants, see __Static_Class_Invariants__):
|
|
|
|
// Contract declaration for a static public function (explicit class-type).
|
|
boost::contract::check c = boost::contract::public_function<``[^['class-type]]``>()
|
|
... // Preconditions, postconditions, exception guarantees, etc.
|
|
;
|
|
|
|
The [funcref boost::contract::public_function] function returns an RAII object that must be assigned to a local variable of type [classref boost::contract::check] (otherwise this library will generate a run-time error).
|
|
Furthermore, C++11 `auto` declarations cannot be used here and the [classref boost::contract::check] type must be explicitly specified (otherwise this library will generate a compile-time error).
|
|
The static public member functions body is programmed right after the declaration of the RAII object.
|
|
At construction, the RAII object does the following (enclosing static public function entry):
|
|
|
|
# Check static class invariants, by calling [^['class-type]]`::static_invariant()` (but never non-static class invariants).
|
|
# Check preconditions, by calling the nullary functor [^['r]]`()` passed to `.precondition(`[^['r]]`)`.
|
|
|
|
At destruction instead (enclosing static public function exit):
|
|
|
|
# Check static class invariants, by calling [^['class-type]]`::static_invariant()` (even if the function body threw an exception, but never non-static class invariants).
|
|
# If the function body did not throw an exception:
|
|
# Check postconditions, by calling the nullary functor [^['s]]`()` passed to `.postcondition(`[^['s]]`)`.
|
|
# Else:
|
|
# Check exception guarantees, by calling the nullary functor [^['e]]`()` passed to `.except(`[^['e]]`)`.
|
|
|
|
This ensures that static public member function contracts are correctly checked at run-time (see __Public_Function_Calls__).
|
|
[footnote
|
|
Note that static public functions do not subcontract because they have no object `this` and therefore no inheritance.
|
|
]
|
|
|
|
[note
|
|
A static public member function can avoid calling [funcref boost::contract::public_function] for efficiency but only when it has no preconditions, no postconditions, no exception guarantees, and its class has no static invariants (the class can still have non-static invariants or base classes instead).
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|