Home | Libraries | People | FAQ | More |
This section is a guide to basic usages of this library.
Contracts for non-member functions are programmed using boost::contract::function
.
For example (see non_member.cpp
):
#include <boost/contract.hpp> // Contract for a non-member function. int inc(int& x) { int result; boost::contract::old_ptr<int> old_x = BOOST_CONTRACT_OLDOF(x); boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(x < std::numeric_limits<int>::max()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(x == *old_x + 1); BOOST_CONTRACT_ASSERT(result == *old_x); }) .except([&] { BOOST_CONTRACT_ASSERT(x == *old_x); }) ; return result = x++; // Function body. }
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 boost::contract::function
function returns an RAII object that must always be assigned to a local variable
of type boost::contract::check
(otherwise this library will generate a run-time error, see BOOST_CONTRACT_ON_MISSING_CHECK_DECL
).
[19] Furthermore, C++11 auto
declarations cannot be used here and the boost::contract::check
type must be explicitly specified (otherwise this library will generate a
compile-time error prior C++17 and a run-time error post C++17). [20] 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. |
At construction, the boost::contract::check
RAII object for non-member functions does the following (enclosing function
entry):
r
()
passed to .precondition(
r
)
.
At destruction instead (enclosing function exit):
s
()
passed to .postcondition(
s
)
.
e
()
passed to .except(
e
)
.
This ensures that non-member function contracts are correctly checked at run-time (see Function Calls). (Also note that functions will correctly check their contracts even when they are called via function pointers, function objects, etc.)
Note | |
---|---|
A non-member function can avoid calling |
When preconditions are specified, they are programmed using a functor r
passed to .precondition(
r
)
that can be called with no parameters as
r
()
.
Contracts that do not have preconditions simply do not call .precondition(...)
. Preconditions must appear before postconditions
and exception guarantees when these are all present (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). [21] For example, for boost::contract::function
(similarly for public functions, instead destructors do not have preconditions
and constructors use boost::contract::constructor_precondition
,
see Public Functions,
Destructors, and
Constructors):
void f(...) { boost::contract::check c = boost::contract::function() // Same for all other contracts. .precondition([&] { // Capture by reference or value... BOOST_CONTRACT_ASSERT(...); // ...and 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. [22] 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 BOOST_CONTRACT_ASSERT
to program precondition assertions because that enables this 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(bool_cond) // Or, if `bool_cond` contains commas `,` not already within parenthesis `()`... BOOST_CONTRACT_ASSERT((bool_cond)) // ...use extra parenthesis (not a variadic macro).
This library will automatically call the failure handler boost::contract::precondition_failure
if any of the BOOST_CONTRACT_ASSERT
conditions are false and, more in general, if calling the functor specified
via .precondition(...)
throws any exception. By default, this
failure handler prints an error message to std::cerr
and terminates the program calling std::terminate
(see Throw
on Failures to change the failure handler 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 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). However, this library does not enforce such a constraint and it leaves it up to programmers to only use public members when programming contracts, especially when asserting preconditions (see Specifications vs. Implementation). |
When postconditions are specified, they are programmed using a functor s
passed to .postcondition(
s
)
that can be called with no parameters as
s
()
.
Contracts that do not have postconditions simply do not call .postcondition(...)
. Postconditions must appear after preconditions
but before exception guarantees when these are all present (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 boost::contract::function
(similarly for all other contracts):
void f(...) { 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 Values 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 BOOST_CONTRACT_ASSERT
to program postcondition assertions because that enables this 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(bool_cond) // Or, if `bool_cond` has commas `,` not already within parenthesis `()`... BOOST_CONTRACT_ASSERT((bool_cond)) // ...use extra parenthesis (not a variadic macro).
This library will automatically call the failure handler boost::contract::postcondition_failure
if any of the BOOST_CONTRACT_ASSERT
conditions are false and, more in general, if calling the functor specified
via .postcondition(...)
throws any exception. By default, this
failure handler prints an error message to std::cerr
and terminates the program calling std::terminate
(see Throw
on Failures to change the failure handler to throw exceptions, exit
the program with an error code, etc.).
For non-void virtual public functions and public function overrides, 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
)
(this is to properly support subcontracting,
see Virtual
Public Functions and Public
Function Overrides).
In non-void functions, postconditions might need to access the function return
value to program assertions. In these cases, 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).
[23] For example, for boost::contract::function
(similarly for all other contracts):
return_type f(...) { return_type result; // Must be later 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. ... }) ... ; ... // Assign `result` at each return. }
At any point where the enclosing function returns, programmers are responsible
to assign the result variable to the expression being returned. This can
be done ensuring that all return
statements in the function are of the form:
return result = return_expr; // 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 when first
declaring the result variable (see Optional
Return Values).
Non-void virtual public functions and public function overrides must always declare and use a result variable even when postconditions do not directly use the function return value (this is to properly support subcontracting, see Virtual Public Functions and Public Function Overrides).
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 BOOST_CONTRACT_OLDOF
.
[24] For example, for boost::contract::function
(similarly for all other contracts):
void f(...) { boost::contract::old_ptr<old_type> old_var = BOOST_CONTRACT_OLDOF(old_expr); ... // More old value declarations here if needed. boost::contract::check c = boost::contract::function() // Same for all other contracts. ... // Preconditions shall not use old values. .postcondition([&] { // Capture by reference... BOOST_CONTRACT_ASSERT(*old_var ...); // ...but should not modify captures. ... }) .except([&] { // Capture by reference... BOOST_CONTRACT_ASSERT(old_var->...); // ...but should not modify captures. ... }) ; ... }
Old values are handled by this library using the smart pointer class template
boost::contract::old_ptr
(so programmers do not directly manage allocation and deallocation of the
pointed memory). [25] The pointed old value type is automatically qualified as const
(so old values cannot be mistakenly
changed by 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 and use these pointers in postcondition and exception guarantee
assertions using operator*
and 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. [26] See Old Value Copies at Body for delaying the copy of old values until after class invariants (for constructors, destructors, and public 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).
BOOST_CONTRACT_OLDOF
is
a variadic macro and it takes an extra parameter when used in virtual public
functions or public function overrides (see Virtual
Public Functions and Public
Function Overrides). C++11 auto declarations can be used with BOOST_CONTRACT_OLDOF
for brevity
auto
old_variable-name
= BOOST_CONTRACT_OLDOF(expression)
. See No Macros
to program old values without using BOOST_CONTRACT_OLDOF
(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 |
When exception guarantees are specified, they are programmed using a functor
e
passed to .except(
e
)
that can be called with no parameters as
e
()
.
Contracts that do not have exception guarantees simply do not call .except(...)
. Exception guarantees must appear after
both preconditions and postconditions when these are all present (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 boost::contract::function
(similarly for all other contracts):
void f(...) { 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 function return value instead (because the return value will not be properly 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).
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 practice (and they are not used in most of the examples listed in the rest of this documentation). In any case, these performance considerations are ultimately left to programmers and their specific application domain. |
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). It is also recommended to use BOOST_CONTRACT_ASSERT
to program exception guarantee assertions because that enables this 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(bool_cond) // Or, if `bool_cond` has commas `,` not already within parenthesis `()`... BOOST_CONTRACT_ASSERT((bool_cond)) // ...use extra parenthesis (not a variadic macro).
This library will automatically call the failure handler boost::contract::except_failure
if any of the BOOST_CONTRACT_ASSERT
conditions are false and, more in general, if calling the functor specified
via .except(...)
throws any exception. By default, this
failure handler prints an error message to std::cerr
and terminates the program calling std::terminate
(see Throw
on Failures to change the failure handler to exit the program with
an error code or to take some other custom action).
Note | |
---|---|
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. 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 the exception guarantees to be checked in the first place). Therefore, programmers should not change the exception guarantee failure handler to throw exceptions. |
Public member functions, constructors, and destructors can be programmed
to check class invariants. When class invariants are specified, they are
programmed in a public const
function named invariant
taking no argument and returning void
.
Classes that do not have invariants, simply do not declare the invariant
function. [27] For example:
class u { 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 BOOST_CONTRACT_PERMISSIVE
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 BOOST_CONTRACT_ASSERT
to program
class invariant assertions because that enables this 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(bool_cond) // Or, if `bool_cond` has commas `,` not already within parenthesis `()`... BOOST_CONTRACT_ASSERT((bool_cond)) // ...use extra parenthesis (not a variadic macro).
This library will automatically call failure handlers boost::contract::entry_invariant_failure
or boost::contract::exit_invariant_failure
if any of the BOOST_CONTRACT_ASSERT
conditions are false and, more in general, if the invariant
function throws an exception when invariants are checked at function entry
or exit respectively. By default, these handlers print an error message to
std::cerr
and terminate the program calling
std::terminate
(see Throw
on Failures to change these failure handlers to throw exceptions,
exit the program with an error code, etc.).
See Access Specifiers
to avoid making the invariant
member function public
. [28] See 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 assertions are not checked (not even class invariants) when data
members are accessed directly (this is different from Eiffel where even
accessing public data members checks class invariants). Therefore, it might
be best for both |
See Volatile
Public Functions to program invariants for classes with volatile
public functions.
Static public functions can be programmed to check static class invariants.
When static class invariants are specified, they are programmed in a public
static
function named static_invariant
taking no argument and
returning void
. Classes that
do not have static class invariants, simply do not declare the static_invariant
function. [29] For example:
class u { 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 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 BOOST_CONTRACT_ASSERT
to program
the assertions because that enables this 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(bool_cond) // Or, if condition has commas `,` not already within parenthesis `()`... BOOST_CONTRACT_ASSERT((bool_cond)) // ...use extra parenthesis (not a variadic macro).
This library will automatically call failure handlers boost::contract::entry_invariant_failure
or boost::contract::exit_invariant_failure
if any of the BOOST_CONTRACT_ASSERT
conditions are false and, more in general, if the static_invariant
function throws an exception when invariants are checked at function entry
or exit respectively. By default, these handlers print an error message to
std::cerr
and terminate the program calling
std::terminate
(see Throw
on Failures to change these failure handlers to throw exceptions,
exit the program with an error code, etc.).
See Access Specifiers
to avoid making static_invariant
member function public
. [30] See 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). [31]
Contracts for constructors are programmed using the boost::contract::constructor
function and the boost::contract::constructor_precondition
base class. For example (see public.cpp
):
class unique_identifiers : private boost::contract::constructor_precondition<unique_identifiers> { public: void invariant() const { BOOST_CONTRACT_ASSERT(size() >= 0); }
public: // Contract for a constructor. unique_identifiers(int from, int to) : boost::contract::constructor_precondition<unique_identifiers>([&] { BOOST_CONTRACT_ASSERT(from <= to); }) { boost::contract::check c = boost::contract::constructor(this) .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == (to - from + 1)); }) ; // Constructor body. for(int id = from; id <= to; ++id) vect_.push_back(id); }
/* ... */ };
It is not possible to specify preconditions using .precondition(...)
for constructors (this library will generate a compile-time error if .precondition(...)
is used on the object returned by boost::contract::constructor
).
Constructor preconditions are specified using the boost::contract::constructor_precondition
base class instead (but considerations from Preconditions
apply also to the precondition functor passed to 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).
[32] Constructors without preconditions simply do not explicitly initialize
the boost::contract::constructor_precondition
base (because boost::contract::constructor_precondition
default constructor checks no contract). When the boost::contract::constructor_precondition
base class is used: [33]
private
(so this extra base class does not alter the public inheritance tree
of its derived classes).
Note | |
---|---|
A class can avoid inheriting from |
It is possible to specify postconditions for constructors (see Postconditions),
but 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). [35] It is also possible to specify exceptions guarantees for constructors
(see Exception
Guarantees), but programmers should not access the object this
or its old value in constructor exception
guarantees (because the object did not exist before executing the constructor
body and it was not properly constructed given the constructor body threw
an exception). [36] The boost::contract::constructor
function takes this
as a parameter
(because constructors check class invariants, see Class
Invariants).
The boost::contract::constructor
function returns an RAII object that must always be assigned to a local variable
of type boost::contract::check
(otherwise this library will generate a run-time error, see BOOST_CONTRACT_ON_MISSING_CHECK_DECL
).
Furthermore, C++11 auto
declarations
cannot be used here and the boost::contract::check
type must be explicitly specified (otherwise this library will generate a
compile-time error prior C++17 and a run-time error post C++17). The constructor
body is programmed right after the declaration of the RAII object.
At construction, the boost::contract::check
RAII object for constructors does the following (enclosing constructor entry):
type-of
(*this)::static_invariant()
(but not non-static class invariants
because the object does not exist yet).
At destruction instead (enclosing constructor exit):
type-of
(*this)::static_invariant()
.
this->invariant()
.
s
()
passed to .postcondition(
s
)
.
e
()
passed to .except(
e
)
.
This together with C++ object construction mechanism of base classes and
the use of boost::contract::constructor_precondition
ensures that the constructor contracts are correctly checked at run-time
(see Constructor
Calls).
Note | |
---|---|
A constructor can avoid calling
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 |
Private and protected constructors can omit boost::contract::constructor
(because they are not part of the public interface of the class so they are
not required to check class invariants, see Constructor
Calls). They could still use boost::contract::constructor_precondition
to check preconditions before member initializations, and even use boost::contract::function
(but not boost::contract::constructor
)
to only check postconditions and exception guarantees without checking class
invariants and without calling .precondition(...)
(see Private
and Protected Functions). For example:
class u : private boost::contract::constructor_precondition<u> { protected: // Contract for a protected constructor (same for private constructors). u() : // Still use this base class to check constructor preconditions. boost::contract::constructor_precondition<u>([&] { 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(...); ... }) .except([&] { BOOST_CONTRACT_ASSERT(...); ... }) ; ... // Constructor body. } ... };
Contracts for destructors are programmed using boost::contract::destructor
.
For example (see public.cpp
):
class unique_identifiers : private boost::contract::constructor_precondition<unique_identifiers> { public: void invariant() const { BOOST_CONTRACT_ASSERT(size() >= 0); }
public: // Contract for a destructor. virtual ~unique_identifiers() { // Following contract checks class invariants. boost::contract::check c = boost::contract::destructor(this); // Destructor body here... (do nothing in this example). }
/* ... */ };
It is not possible to specify preconditions for destructors (this library
will generate a compile-time error if .precondition(...)
is used here and that is 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), but programmers should not access
the object this
in destructor
postconditions (because the object no longer exists after the destructor
body has been executed). [37] It is also possible to specify exceptions guarantees for destructors
(see Exception
Guarantees, even if destructors should usually be programmed to not
throw exceptions in C++ and in fact they are implicitly declared noexcept
since C++11). [38] The boost::contract::destructor
function takes this
as a parameter
(because destructors check class invariants, see Class
Invariants).
The boost::contract::destructor
function returns an RAII object that must always be assigned to a local variable
of type boost::contract::check
(otherwise this library will generate a run-time error, see BOOST_CONTRACT_ON_MISSING_CHECK_DECL
).
Furthermore, C++11 auto
declarations
cannot be used here and the boost::contract::check
type must be explicitly specified (otherwise this library will generate a
compile-time error prior C++17 and a run-time error post C++17). The destructor
body is programmed right after the declaration of the RAII object.
At construction, the boost::contract::check
RAII object for destructors does the following (enclosing destructor entry):
type-of
(*this)::static_invariant()
AND
this->invariant()
.
At destruction instead (enclosing destructor exit):
type-of
(*this)::static_invariant()
.
s
()
passed to .postcondition(
s
)
.
this->invariant()
(because the object was not successfully
destructed).
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
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 |
Private and protected destructors can omit boost::contract::destructor
(because they are not part of the public interface of the class so they are
not required to check class invariants, see Destructor
Calls). They could use boost::contract::function
(but not boost::contract::destructor
)
to only check postconditions and exception guarantees without checking class
invariants and without calling .precondition(...)
(see Private
and Protected Functions). For example:
class u { protected: // Contract for a protected destructor (same for private destructors). virtual ~u() { // Following will correctly not check class invariants. boost::contract::check c = boost::contract::function() // Do not use `.precondition(...)` here. .postcondition([&] { BOOST_CONTRACT_ASSERT(...); ... }) // Could use `.except(...)` here in rare cases of destructors declared to throw. ; ... // Destructor body. } ... };
Contracts for public functions are programmed using boost::contract::public_function
.
In this section, let's consider public functions that are not static, not
virtual, and do not override any function from base classes. For example
(see public.cpp
):
class unique_identifiers : private boost::contract::constructor_precondition<unique_identifiers> { public: void invariant() const { BOOST_CONTRACT_ASSERT(size() >= 0); }
public: // Contract for a public function (but no static, virtual, or override). bool find(int id) const { bool result; boost::contract::check c = boost::contract::public_function(this) .postcondition([&] { if(size() == 0) BOOST_CONTRACT_ASSERT(!result); }) ; // Function body. return result = std::find(vect_.begin(), vect_.end(), id) != vect_.end(); }
/* ... */ };
It is possible to specify preconditions, postconditions, and exception guarantees
for public functions (see Preconditions,
Postconditions,
and Exception
Guarantees). When called from non-static public functions, the boost::contract::public_function
function takes this
as a parameter
(because public functions check class invariants, see Class
Invariants).
The boost::contract::public_function
function returns an RAII object that must always be assigned to a local variable
of type boost::contract::check
(otherwise this library will generate a run-time error, see BOOST_CONTRACT_ON_MISSING_CHECK_DECL
).
Furthermore, C++11 auto
declarations
cannot be used here and the boost::contract::check
type must be explicitly specified (otherwise this library will generate a
compile-time error prior C++17 and a run-time error post C++17). The public
function body is programmed right after the declaration of the RAII object.
At construction, the boost::contract::check
RAII object for public functions does the following (enclosing public function
entry):
type-of
(*this)::static_invariant()
AND
this->invariant()
.
r
()
passed to .precondition(
r
)
.
At destruction instead (enclosing public function exit):
type-of
(*this)::static_invariant()
AND
this->invariant()
(even if the function body threw an exception).
s
()
passed to .postcondition(
s
)
.
e
()
passed to .except(
e
)
.
This ensures that public function contracts are correctly checked at run-time (see Public Function Calls).
Note | |
---|---|
A public function can avoid calling
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 |
Contracts for public functions are programmed using boost::contract::public_function
.
In this section, let's consider public functions that are virtual but that
do not override any function from base classes. For example (see public.cpp
):
class unique_identifiers : private boost::contract::constructor_precondition<unique_identifiers> { public: void invariant() const { BOOST_CONTRACT_ASSERT(size() >= 0); }
public: // Contract for a public virtual function (but no override). virtual int push_back(int id, boost::contract::virtual_* v = 0) { // Extra `v`. int result; boost::contract::old_ptr<bool> old_find = BOOST_CONTRACT_OLDOF(v, find(id)); // Pass `v`. boost::contract::old_ptr<int> old_size = BOOST_CONTRACT_OLDOF(v, size()); // Pass `v`. boost::contract::check c = boost::contract::public_function( v, result, this) // Pass `v` and `result`. .precondition([&] { BOOST_CONTRACT_ASSERT(!find(id)); // ID cannot be already present. }) .postcondition([&] (int const result) { if(!*old_find) { BOOST_CONTRACT_ASSERT(find(id)); BOOST_CONTRACT_ASSERT(size() == *old_size + 1); } BOOST_CONTRACT_ASSERT(result == id); }) ; // Function body. vect_.push_back(id); return result = id; }
/* ... */ };
Virtual public functions must declare an extra trailing parameter of type
boost::contract::virtual_
*
with default value 0
(i.e., nullptr
). [39] 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
(callers will rarely, if ever, have to explicitly deal with this extra parameter
a part from when manipulating the virtual function type directly for function
pointer type-casting, etc.). Programmers must pass the extra virtual parameter
as the very first argument to all BOOST_CONTRACT_OLDOF
and boost::contract::public_function
calls in the virtual public function definition. [40]
When called from virtual public functions, the boost::contract::public_function
function takes this
as a parameter
(because public functions check class invariants, see Class
Invariants). For virtual public functions returning void
:
class u { public: // A void virtual public function (that does not override). virtual void f(t_1 a_1, ..., t_n a_n, boost::contract::virtual_* v = 0) { boost::contract::check c = boost::contract::public_function( v, this) // No result parameter... .precondition([&] { ... }) .postcondition([&] { ... }) // ...so nullary functor. .except([&] { ... }) ; ... } ... }
For virtual public functions not returning void
,
programmers must also pass a reference to the function return value as the
second argument to boost::contract::public_function
.
In this case, the library will pass this return value reference to the postcondition
functor that must therefore take one single argument matching the return
type, otherwise this library will generate a compile-time error (the functor
parameter can be a constant reference const&
to avoid extra copies of the return
value): [41]
class u { public: // A void virtual public function (that does not override). virtual t f(t_1 a_1, ..., t_n a_n, boost::contract::virtual_* v = 0) { t result; boost::contract::check c = boost::contract::public_function( v, result, this) // Result parameter... .precondition([&] { ... }) .postcondition([&] (t const& result) { ... }) // ...so unary functor. .except([&] { ... }) ; ... // Assign `result` at each return. } ... }
Important | |
---|---|
It is the responsibility of the programmers to pass the extra virtual parameter
Mnemonics:
|
For the rest, considerations made in Public Functions apply to virtual public functions as well.
Note | |
---|---|
A virtual public function should always call |
Contracts for public functions are programmed using boost::contract::public_function
.
In this section, let's consider public functions (virtual or not) that override
virtual public functions from one or more public base classes. For example
(see public.cpp
):
[43]
class identifiers #define BASES public unique_identifiers : BASES { public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; // Bases typedef. #undef BASES void invariant() const { // Check in AND with bases. BOOST_CONTRACT_ASSERT(empty() == (size() == 0)); }
public: // Contract for a public function override. int push_back(int id, boost::contract::virtual_* v = 0) /* override */ { int result; boost::contract::old_ptr<bool> old_find = BOOST_CONTRACT_OLDOF(v, find(id)); boost::contract::old_ptr<int> old_size = BOOST_CONTRACT_OLDOF(v, size()); boost::contract::check c = boost::contract::public_function< override_push_back // Pass override plus below function pointer... >(v, result, &identifiers::push_back, this, id) // ...and arguments. .precondition([&] { // Check in OR with bases. BOOST_CONTRACT_ASSERT(find(id)); // ID can be already present. }) .postcondition([&] (int const result) { // Check in AND with bases. if(*old_find) BOOST_CONTRACT_ASSERT(size() == *old_size); }) ; // Function body. if(!find(id)) unique_identifiers::push_back(id); // Else, do nothing. return result = id; } BOOST_CONTRACT_OVERRIDE(push_back) // Define `override_push_back`.
/* ... */ };
The extra typedef
declared using
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 public function overrides, the boost::contract::public_function
function template takes an explicit template argument override_
function-name
that must be defined using BOOST_CONTRACT_OVERRIDE
:
BOOST_CONTRACT_OVERRIDE(func_name)
This can be declared at any point in the public section of the enclosing
class (see Access
Specifiers to use BOOST_CONTRACT_OVERRIDE
in a non-public section of the class instead). BOOST_CONTRACT_OVERRIDE
is used only once in a class for a given function name and overloaded functions
can reuse the same override_function-name
definition (see Function
Overloads). 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).
For convenience BOOST_CONTRACT_OVERRIDES
can be used with multiple function names instead of repeating 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 other number of functions), the following:
BOOST_CONTRACT_OVERRIDES(f, g, h)
Is equivalent to: [44]
BOOST_CONTRACT_OVERRIDE(f) BOOST_CONTRACT_OVERRIDE(g) BOOST_CONTRACT_OVERRIDE(h)
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. [45]
Public function overrides must always list the extra trailing parameter of
type 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 BOOST_CONTRACT_OLDOF
and boost::contract::public_function
calls in the public function override definition (see Virtual
Public Functions).
When called from public function overrides, the boost::contract::public_function
function 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. [46] For public function overrides returning void
:
class u { public: // A void public function override. void f(t_1 a_1, ..., t_n a_n, boost::contract::virtual_* v = 0) /* override */ { boost::contract::check c = boost::contract::public_function<override_f>( v, &u::f, this, a_1, ..., a_n) // No result parameter... .precondition([&] { ... }) .postcondition([&] { ... }) // ...so nullary functor. .except([&] { ... }) ; ... } BOOST_CONTRACT_OVERRIDE(f) ... }
For public function overrides not returning void
,
programmers must also pass a reference to the function return value as the
second argument to boost::contract::public_function
(this library will generate a compile-time error otherwise). [47] In this case, the library will pass this return value reference
to the postcondition functor that must therefore take one single argument
matching the return type, otherwise this library will generate a compile-time
error (the functor parameter can be a constant reference const&
to avoid extra copies of the return
value):
class u { public: // A non-void public function override. t f(t_1 a_1, ..., t_n a_n, boost::contract::virtual_* v = 0) /* override */ { t result; boost::contract::check c = boost::contract::public_function<override_f>( v, result, &u::f, this, a_1, ..., a_n) // Result parameter... .precondition([&] { ... }) .postcondition([&] (t const& result) { ... }) // ...so unary functor. .except([&] { ... }) ; ... // Assign `result` at each return. } BOOST_CONTRACT_OVERRIDE(f) ... }
This library will throw 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. [48]
Important | |
---|---|
It is the responsibility of the programmers to pass the extra virtual parameter
Mnemonics:
|
At construction, the boost::contract::check
RAII object for public function overrides does the following (enclosing public
function override entry):
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()
.
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):
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).
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).
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 public function override contracts and subcontracts are correctly checked at run-time (see Public Function Calls).
For the rest, considerations made in Virtual Public Functions apply to public function overrides as well.
Note | |
---|---|
A public function override should always call |
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 BOOST_CONTRACT_BASE_TYPES
.
For example (see base_types.cpp
):
class chars #define BASES /* local macro (for convenience) */ \ private boost::contract::constructor_precondition<chars>, \ public unique_chars, \ public virtual pushable<char>, \ virtual protected has_size, \ private has_empty : BASES // Bases of this class. { public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; // Bases typedef. #undef BASES // Undefine local macro. /* ... */
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 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). [49]
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 boost::contract::constructor_precondition
is used to program constructor preconditions, its inheritance access level
must always be private
and it
must be specified as the very first base.
Important | |
---|---|
Each base passed to Mnemonics:
|
See Access Specifiers
to avoid making the base_types
member type public
. [51] See BOOST_CONTRACT_BASES_TYPEDEF
to use a name different from base_types
(e.g., because base_types
clashes with other names in user-defined classes).
Contracts for public functions are programmed using boost::contract::public_function
.
In this section, let's consider static public functions. For example (see
static_public.cpp
):
template<class C> class make { public: static void static_invariant() { // Static class invariants. BOOST_CONTRACT_ASSERT(instances() >= 0); } // Contract for a static public function. static int instances() { // Explicit template parameter `make` (check static invariants). boost::contract::check c = boost::contract::public_function<make>(); return instances_; // Function body. } /* ... */
It is possible to specify preconditions, postconditions, and exception guarantees
for static public functions (see Preconditions,
Postconditions,
and Exception
Guarantees). When called from static public functions, 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 (the class type is required to check static
class invariants, see Class
Invariants):
class u { public: // A static public function. static void f() { boost::contract::check c = boost::contract::public_function<u>() // Class type `u` as explicit template parameter. .precondition([&] { ... }) .postcondition([&] { ... }) .except([&] { ... }) ; ... } ... };
The boost::contract::public_function
function returns an RAII object that must be assigned to a local variable
of type boost::contract::check
(otherwise this library will generate a run-time error, see BOOST_CONTRACT_ON_MISSING_CHECK_DECL
).
Furthermore, C++11 auto
declarations
cannot be used here and the boost::contract::check
type must be explicitly specified (otherwise this library will generate a
compile-time error prior C++17 and a run-time error post C++17). The static
public functions body is programmed right after the declaration of the RAII
object.
At construction, the boost::contract::check
RAII object for static public functions does the following (enclosing static
public function entry):
class-type
::static_invariant()
(but never non-static class invariants).
r
()
passed to .precondition(
r
)
.
At destruction instead (enclosing static public function exit):
class-type
::static_invariant()
(even if the function body threw an
exception, but never non-static class invariants).
s
()
passed to .postcondition(
s
)
.
e
()
passed to .except(
e
)
.
This ensures that static public function contracts are correctly checked
at run-time (static public functions do not subcontract because they have
no object this
and therefore
there is no inheritance, see Public
Function Calls).
Note | |
---|---|
A static public function can avoid calling |
[19]
The name of this local variable is arbitrary, but c
is often used in this documentation for “c”heck or “c”aminiti
;-)
.
[20]
Rationale: C++17 zero-copy guarantee on
function return values skips the trick this library uses to force a compile-time
error when auto
is incorrectly
used instead of boost::contract::check
.
The library is still able to generate a run-time error in this case on
C++17. In any case, after reading this documentation it should be evident
to programmers that auto
should
not be used in boost::contract::check
declarations so this misuse of auto
should not be an issue in practice.
[21]
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.
[22] In this documentation preconditions often capture variables by reference to avoid extra copies.
[23]
The name of the local variable that holds the return value is arbitrary,
but result
is often used
in this documentation.
[24]
The name of a local variable that holds an old value is arbitrary, but
old_variable-name
is often used
in this documentation.
[25]
Rationale: Old values have to be optional
values because they need to be left uninitialized when they are not used
because both postconditions and exception guarantees are disabled (defining
BOOST_CONTRACT_NO_POSTCONDITIONS
and BOOST_CONTRACT_NO_EXCEPTS
).
That is to avoid old value copies when old values are not used, so a pointer,
or better a boost::optional
, could have been used for that.
In addition, 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.
[26]
For example, old value pointers might be null in preconditions when postconditions
and exception guarantees are disabled defining BOOST_CONTRACT_NO_POSTCONDITIONS
and BOOST_CONTRACT_NO_EXCEPTS
,
but also when checking an overridden virtual public function contract via
subcontracting, etc.
[27]
This library uses template meta-programming (SFINAE-based introspection
techniques) to check invariants only for classes that declare a member
function named BOOST_CONTRACT_INVARIANT_FUNC
.
[28]
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 boost::contract::access
as explained in Access
Specifiers).
[29]
This library uses template meta-programming (SFINAE-based introspection
techniques) to check static invariants only for classes that declare a
member function named BOOST_CONTRACT_STATIC_INVARIANT_FUNC
.
[30]
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 boost::contract::access
as explained in Access
Specifiers).
[31]
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
for BOOST_CONTRACT_INVARIANT_FUNC
and for BOOST_CONTRACT_STATIC_INVARIANT_FUNC
).
[32] See No Lambda Functions to enforce this constraint at compile-time (but not recommended because of extra boiler-plate code).
[33]
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 (see No
Lambda Functions).
[34]
Rationale: The 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 resolution errors even when multiple inheritance
is used. Note that virtual inheritance could not be used instead of
the template parameter here to resolve ambiguities (because virtual
bases are initialized only once by the outer-most derived class, and
that would not allow to properly check preconditions of all base classes).
[35] See No Lambda Functions to enforce this constraint at compile-time (but not recommended because of extra boiler-plate code).
[36] See No Lambda Functions to enforce these constraints at compile-time (but not recommended because of extra boiler-plate code).
[37] See No Lambda Functions to enforce this constraint at compile-time (but not recommended because of extra boiler-plate code).
[38]
Exceptions guarantees in destructors can access both the object this
and its old value because the object
exited before executing the destructor body and it still exists given the
destructor body failed throwing an exception so the object should still
be properly constructed and satisfy its class invariants.
[39]
The name of this extra parameter is arbitrary, but v
is often used in this documentation.
[40]
Rationale: The boost::contract::virtual_
*
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
to implement subcontracting.
[41]
Rationale: The extra function result parameter
taken by the functor passed to .postcondition(...)
is used by this library to pass the return value evaluated by the overriding
function to all its overridden virtual functions when subcontracting.
[42]
Rationale: This library does not require
programmers to specify the function type when using boost::contract::public_function
for non-overriding virtual public 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 public functions. Instead the function type is
passed to this library for virtual public function overrides and that
also allows this library to give a compile-time error if the return value
reference is missing in those cases.
[43]
In this documentation, function overrides 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 simply because virtual specifiers are
not widely supported yet, even by compilers that support C++11 lambda functions).
[44]
This library does not provider an equivalent of BOOST_CONTRACT_NAMED_OVERRIDE
that operates on multiple function names at once (simply because programmers
will probably not use BOOST_CONTRACT_NAMED_OVERRIDE
often in the first place).
[45]
The compile-time error generated by the library in this case is similar
in principle to the error generated by the C++11 override
specifier, but it is limited to functions with the extra boost::contract::virtual_
*
parameter and searched recursively only
in public
base classes passed
to BOOST_CONTRACT_BASE_TYPES
because only those are considered for subcontracting.
[46]
Rationale: The object this
is passed after the function pointer to follow std::bind
's
syntax. The function pointer and references to all function arguments are
needed for public function overrides 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).
[47] Rationale: As for non-overriding virtual public functions, also public function overrides use the extra return value parameter to pass it to the overridden functions when subcontracting. In the case of public function overrides, this library 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 virtual public functions because their contracts do not take the function pointer as a parameter, see Virtual Public Functions).
[48]
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).
[49]
The name of this local macro is arbitrary, but BASES
is often used in this documentation.
[50]
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). BOOST_CONTRACT_BASE_TYPES
inspects each inheritance access level using preprocessor meta-programming
and removes non-public bases from the list of bases inspected for subcontracting.
However, this library cannot always detect when programmers forget to
specify the inheritance access level because, when commas are used to
separate template parameters passed to base classes, the preprocessor
will not be able to correctly use commas to identify the next base class
token in the inheritance list (the preprocessor cannot distinguish between
commas that are not protected by round parenthesis, like the ones used
in templates). Therefore, this library relies on inheritance access levels
to program the preprocessor to correctly identify the next base class
token in the inheritance list (thus inheritance access levels must always
be explicit specified by programmers).
[51]
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 boost::contract::access
as explained in Access
Specifiers).