Home | Libraries | People | FAQ | More |
This section is a guide to advanced usages of this library.
In C++, pure virtual functions are allowed to have a default
implementation as long as such implementation is programmed out-of-line
so defined outside the class declaring the pure virtual function virtual ... = 0;
.
Contracts for pure virtual public functions are programmed using the boost::contract::public_function
function like for (non-pure) virtual public functions (all consideration
made in Virtual
Public Functions apply). However, contracts have to be programmed
out-of-line, in the default implementation of the pure virtual function.
For example (see pure_virtual_public.cpp
):
template<typename Iterator> class range { public: // Pure virtual function declaration (contract in definition below). virtual Iterator begin(boost::contract::virtual_* v = 0) = 0;
/* ... */ };
// Pure virtual function default implementation (out-of-line in C++). template<typename Iterator> Iterator range<Iterator>::begin(boost::contract::virtual_* v) { Iterator result; // As usual, virtual pass `result` right after `v`... boost::contract::check c = boost::contract::public_function(v, result, this) .postcondition([&] (Iterator const& result) { if(empty()) BOOST_CONTRACT_ASSERT(result == end()); }) ; // Pure function body (never executed by this library). assert(false); return result; }
This library will never actually execute the pure virtual function body while
it is calling the pure virtual function default implementation to check contracts
for subcontracting. Therefore, programmers can safely assert(false)
at the beginning of the body if they intend for that body to never be executed
(or they can program a working body in case they need to use pure virtual
function default implementations as usual in C++).
As seen in Public
Function Overrides, preconditions of overriding public functions are
checked in OR
with preconditions of overridden virtual public functions. Therefore, if
a virtual public function in a base class specifies no precondition then
preconditions specified by all its overriding functions in derived classes
will have no effect (because when checked in OR
with the overridden function from the base class that has no preconditions,
they will always pass): [52]
class u { // Some base class. public: virtual void f(boost::contract::virtual_* v = 0) { boost::contract::check c = boost::contract::public_function(v, this) // No preconditions, same as `ASSERT(true)`. ... ; ... } ... };
This correctly reflects the fact that the overridden function in the base
class can be called from any context (because it has no precondition) and
so must all its overriding functions in all derived classes in accordance
to the substitution
principle. In other words, the code above has the effect as declaring
the virtual public function in the base class with a single precondition
BOOST_CONTRACT_ASSERT(true)
that
will always trivially pass:
class u { // Some base class. public: virtual void f(boost::contract::virtual_* v = 0) { boost::contract::check c = boost::contract::public_function(v, this) .precondition([] { BOOST_CONTRACT_ASSERT(true); // Same as no preconditions. }) ... ; ... } ... };
On the flip side, programmers might sometimes consider to declare a pure
virtual public function in a base class with a single precondition BOOST_CONTRACT_ASSERT(false)
that
will always fail. This indicates that the pure virtual public function can
never be called unless it is redefined by a derived class (which is already
the case with C++ pure virtual functions) and also that the base class designers
have intentionally left it up to derived classes to specify preconditions
for the pure virtual function in question. This technique might make sense
only for preconditions of pure virtual public functions (otherwise BOOST_CONTRACT_ASSERT(false)
will
prevent calling virtual public functions in concrete bases). For example
(see named_override.cpp
):
template<typename T> class generic_unary_pack { public: virtual void _1(T const& value, boost::contract::virtual_* v = 0) = 0; virtual T _1(boost::contract::virtual_* v = 0) const = 0; }; template<typename T> void generic_unary_pack<T>::_1(T const& value, boost::contract::virtual_* v) { boost::contract::check c = boost::contract::public_function(v, this) .precondition([&] { BOOST_CONTRACT_ASSERT(false); // Defer preconditions to overrides. }) ; assert(false); } /* ... */
That said, the need to declare such a precondition BOOST_CONTRACT_ASSERT(false)
that will always fail might be an indication that the base class interface
is not correctly designed. In general, the base class interface should still
contain all functions (eventually as pure virtual) that are necessary to
program its contracts.
It is possible to use boost::optional
to handle return values when programmers cannot construct the result variable
at its point of declaration before the contract (e.g., because an appropriate
constructor for the return type is not available at that point, or just because
it would be too expensive to execute an extra initialization of the return
value at run-time). [53] For example (see optional_result.cpp
):
template<unsigned Index, typename T> T& get(std::vector<T>& vect) { boost::optional<T&> result; // Result not initialized here... boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(Index < vect.size()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(*result == vect[Index]); }) ; // Function body (executed after preconditions checked). return *(result = vect[Index]); // ...result initialized here instead. }
In this example the return type is a reference so it does not have default
constructor that can be used to initialize result
when it is declared before the contract declaration. In addition, Index
needs to be validated to be smaller
than size()
by the precondition before it can be used to retrieve the reference to assign
to result
so vect[Index]
cannot be used to initialize result
when it is declared before the contract
declaration. Therefore, boost::optional
is used to defer result
proper
initialization until the execution of the function body, after the contract
declaration, where Index
has been validated by the precondition and vect[Index]
can be safely evaluated to initialize result
.
As seen in Return Values,
it is the responsibility of the programmers to ensure that result
is always set to the return value
(when the function exits without trowing an exception). This also ensures
that result
is always set
before the postconditions are checked so programmers can always dereference
result
in postconditions
to access the return value (using operator*
and operator->
as usual with boost::optional
,
and without having to explicitly check if result
is an empty boost::optional
object or not). This can be done
ensuring that all return statements in the function
are of the form:
boost::optional<return_type> result; ... return *(result = return_expr); // Assign `result` at each return.
Similarly, boost::optional
can be used to handle the return
value passed to contracts of virtual public functions (pure or not) and of
public function overrides. As seen in Pure
Virtual Public Functions, Virtual
Public Functions, and Public
Function Overrides, in these cases the return value result
must be passed as a parameter to
boost::contract::public_function
right after the parameter v
of type boost::contract::virtual_
*
. Then the functor passed to .postcondition(...)
takes one single parameter of type
boost::optional<
return-type
const&>
const&
.
For example (see optional_result_virtual.cpp
):
template<typename T> T& accessible<T>::at(unsigned index, boost::contract::virtual_* v) { boost::optional<T&> result; // Pass `result` right after `v`... boost::contract::check c = boost::contract::public_function(v, result, this) .precondition([&] { BOOST_CONTRACT_ASSERT(index < size()); }) // ...plus postconditions take `result` as a parameter (not capture). .postcondition([&] (boost::optional<T const&> const& result) { BOOST_CONTRACT_ASSERT(*result == operator[](index)); }) ; assert(false); return *result; }
The inner const&
in the postcondition functor parameter type boost::optional<... const&> ...
is mandatory (while the outer const&
in the postcondition functor parameter
type boost::optional<...>
const&
is not). [54]
Private and protected functions do not check class invariants (because they
are not part of the public class interface) and they do not subcontract (because
they are not accessible at the calling site where the substitution
principle applies, see Function
Calls). However, programmers may still want to specify preconditions
and postconditions for private and protected functions when they want to
check correctness of their implementation and usage from within the class,
base classes, and friend classes or functions. When programmers decide to
specify contracts for private and protected functions, they shall use boost::contract::function
(because, like for non-member functions, this does not check class invariants
and does not subcontract). For example (see private_protected.cpp
):
class counter { protected: // Protected functions use `function()` (like non-members). void set(int n) { boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(n <= 0); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == n); }) ; n_ = n; } private: // Private functions use `function()` (like non-members). void dec() { boost::contract::old_ptr<int> old_get = BOOST_CONTRACT_OLDOF(get()); boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT( get() + 1 >= std::numeric_limits<int>::min()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == *old_get - 1); }) ; set(get() - 1); } int n_; /* ... */
Considerations made in Non-Member Functions apply to private and protected functions as well. See Constructors and Destructors on how to program contracts for private and protected constructors and destructors instead.
When private and protected functions are virtual they should still declare
the extra virtual parameter of type boost::contract::virtual_
*
with default value 0
(see Virtual
Public Functions) even if that parameter does not have to be passed
to BOOST_CONTRACT_OLDOF
and boost::contract::function
takes no such an argument (so the extra virtual parameter will remain unused
and it does not need a name). [55] That is necessary otherwise the private and protected virtual
functions cannot be overridden by public functions in derived classes that
specify contracts (because the boost::contract::virtual_* = 0
parameter has to be part of signatures for public function overrides). For
example (see private_protected_virtual.cpp
):
class counter { // Virtual private and protected functions still declare extra // `virtual_* = 0` parameter (otherwise they cannot be overridden), but... protected: virtual void set(int n, boost::contract::virtual_* = 0) { boost::contract::check c = boost::contract::function() // ...no `v`. .precondition([&] { BOOST_CONTRACT_ASSERT(n <= 0); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == n); }) ; n_ = n; } private: virtual void dec(boost::contract::virtual_* = 0) { boost::contract::old_ptr<int> old_get = BOOST_CONTRACT_OLDOF(get()); // ...no `v`. boost::contract::check c = boost::contract::function() // ...no `v`. .precondition([&] { BOOST_CONTRACT_ASSERT( get() + 1 >= std::numeric_limits<int>::min()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == *old_get - 1); }) ; set(get() - 1); } int n_; /* ... */
However, public functions in derived classes overriding private or protected
virtual functions from base classes shall not specify the extra override_...
template parameter to boost::contract::public_function
because the overridden functions are private or protected and, not being
public, they do not participate to subcontracting (this library will generate
a compile-time error if override_...
is specified because there will be no
virtual public function to override from the base class). For example (see
private_protected_virtual.cpp
):
class counter10 #define BASES public counter : BASES { public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; #undef BASES // Overriding from non-public members so no subcontracting, no override_... virtual void set(int n, boost::contract::virtual_* v = 0) /* override */ { boost::contract::check c = boost::contract::public_function(v, this) .precondition([&] { BOOST_CONTRACT_ASSERT(n % 10 == 0); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == n); }) ; counter::set(n); } virtual void dec(boost::contract::virtual_* v = 0) /* override */ { boost::contract::old_ptr<int> old_get = BOOST_CONTRACT_OLDOF(v, get()); boost::contract::check c = boost::contract::public_function(v, this) .precondition([&] { BOOST_CONTRACT_ASSERT( get() + 10 >= std::numeric_limits<int>::min()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == *old_get - 10); }) ; set(get() - 10); } /* ... */
Furthermore, using multiple inheritance it is possible to override functions
that are private or protected from one base but public from another base.
In this case, public function overrides in derived classes will specify the
extra override_...
template parameter to boost::contract::public_function
(because the overridden functions are private or protected in one base and
those do not participate to subcontracting, but public in another base and
these participate to subcontracting instead). For example (see private_protected_virtual_multi.cpp
):
class countable { public: void invariant() const { BOOST_CONTRACT_ASSERT(get() <= 0); } virtual void dec(boost::contract::virtual_* v = 0) = 0; virtual void set(int n, boost::contract::virtual_* v = 0) = 0; virtual int get(boost::contract::virtual_* v = 0) const = 0; }; /* ... */
class counter10 #define BASES public countable, public counter // Multiple inheritance. : BASES { public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; #undef BASES // Overriding from public members from `countable` so use `override_...`. virtual void set(int n, boost::contract::virtual_* v = 0) /* override */ { boost::contract::check c = boost::contract::public_function< override_set>(v, &counter10::set, this, n) .precondition([&] { BOOST_CONTRACT_ASSERT(n % 10 == 0); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == n); }) ; counter::set(n); } virtual void dec(boost::contract::virtual_* v = 0) /* override */ { boost::contract::old_ptr<int> old_get = BOOST_CONTRACT_OLDOF(v, get()); boost::contract::check c = boost::contract::public_function< override_dec>(v, &counter10::dec, this) .precondition([&] { BOOST_CONTRACT_ASSERT( get() + 10 >= std::numeric_limits<int>::min()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(get() == *old_get - 10); }) ; set(get() - 10); } BOOST_CONTRACT_OVERRIDES(set, dec) /* ... */
Warning | |
---|---|
Unfortunately, the code above does not compile on MSVC (at least up to Visual Studio 2015) because MSVC incorrectly gives a compile-time error when SFINAE fails due to private or protected access levels. Instead, GCC and Clang correctly implement SFINAE failures due to private and protected functions so the code above correctly complies on GCC and Clang. Therefore, currently it is not possible to override a function that is public in one base but private or protected in other base using this library on MSVC, but that can correctly be done on GCC or Clang instead. |
Friend functions are not member functions so boost::contract::function
can used to program contracts for them and all considerations made in Non-Member Functions
apply. For example (see friend.cpp
):
class buffer; class byte { friend bool operator==(buffer const& left, byte const& right); private: char value_; /* ... */
class buffer { // Friend functions are not member functions... friend bool operator==(buffer const& left, byte const& right) { // ...so check contracts via `function` (which won't check invariants). boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(!left.empty()); BOOST_CONTRACT_ASSERT(!right.empty()); }) ; for(char const* x = left.values_.c_str(); *x != '\0'; ++x) { if(*x != right.value_) return false; } return true; } private: std::string values_; /* ... */
However, in some cases a friend function might take an object as parameter
and it can be logically considered an extension of that object's public interface
(essentially at the same level as the object's public functions). In these
cases, programmers might chose to program the friend function contracts using
boost::contract::public_function
(instead of boost::contract::function
)
so to also check the class invariants of the object passed as parameter (and
not just pre- and postconditions). For example (see friend_invariant.cpp
):
[56]
template<typename T> class positive { public: void invariant() const { BOOST_CONTRACT_ASSERT(value() > 0); } // Can be considered an extension of enclosing class' public interface... friend void swap(positive& object, T& value) { boost::contract::old_ptr<T> old_object_value = BOOST_CONTRACT_OLDOF(object.value()); boost::contract::old_ptr<T> old_value = BOOST_CONTRACT_OLDOF(value); // ...so it can be made to check invariants via `public_function`. boost::contract::check c = boost::contract::public_function(&object) .precondition([&] { BOOST_CONTRACT_ASSERT(value > 0); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(object.value() == *old_value); BOOST_CONTRACT_ASSERT(value == *old_object_value); }) ; T saved = object.value_; object.value_ = value; value = saved; } private: T value_; /* ... */
This technique can also be extended to friend functions that take multiple objects as parameters and can be logically considered extensions to the public interfaces of each of these objects. For example:
// Can be considered an extension of multiple objects' public interfaces. friend void f(class1& object1, class2* object2, type3& value3) { // Check preconditions. boost::contract::check pre = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(object2 != nullptr); ... }) ; // Check class invariants for each object (programmers chose the order). boost::contract::check inv1 = boost::contract::public_function(&object1); boost::contract::check inv2 = boost::contract::public_function(object2); // Check postconditions and exception guarantees. boost::contract::check post_except = boost::contract::function() .postcondition(...) .except(...) ; ... // Function body. }
Changing the order of the boost::contract::check
declarations above, programmers can chose the order for checking class invariants
among the different objects passed to the friend function and also whether
to check these invariants before or after preconditions, postconditions,
and exception guarantees of the friend function (see Non-Member
Functions and Public
Functions for information on how the RAII objects returned by boost::contract::function
and boost::contract::public_function
check contract conditions). The example above is programmed to check class1
invariants before class2
invariants (but the order could
have been inverted if programmers so chose).
Note | |
---|---|
In the example above, preconditions are intentionally programmed to be
checked before class invariants so the objects passed to the friend function
can be validated by the preconditions before they are passed as pointers
to |
No special attention is required when using this library with overloaded
functions or constructors. The only exception is for the function pointer
passed to boost::contract::public_function
from public function overrides (see Public
Function Overrides). When the name of public function override are
also overloaded, the related function pointer cannot be automatically deduced
by the compiler so programmers have to use static_cast
to resolve ambiguities (as usual with pointers to overloaded functions in
C++). [57] For example, note how static_cast
is used in the following calls to boost::contract::public_function
(see overload.cpp
):
class string_lines #define BASES public lines : BASES { public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; #undef BASES BOOST_CONTRACT_OVERRIDES(str) // Invoked only once for all `str` overloads. std::string str(boost::contract::virtual_* v = 0) const /* override */ { std::string result; boost::contract::check c = boost::contract::public_function< override_str>( v, result, // `static_cast` resolves overloaded function pointer ambiguities. static_cast<std::string (string_lines::*)( boost::contract::virtual_*) const>(&string_lines::str), this ); return result = str_; } // Overload on (absence of) `const` qualifier. std::string& str(boost::contract::virtual_* v = 0) /* override */ { boost::contract::check c = boost::contract::public_function< override_str>( v, str_, // `static_cast` resolves overloaded function pointer ambiguities. static_cast<std::string& (string_lines::*)( boost::contract::virtual_*)>(&string_lines::str), this ); return str_; } BOOST_CONTRACT_OVERRIDES(put) // Invoked only once for all `put` overloads. void put(std::string const& x, boost::contract::virtual_* v = 0) /* override */ { boost::contract::old_ptr<std::string> old_str = BOOST_CONTRACT_OLDOF(v, str()); boost::contract::check c = boost::contract::public_function< override_put>( v, // `static_cast` resolves overloaded function pointer ambiguities. static_cast<void (string_lines::*)(std::string const&, boost::contract::virtual_*)>(&string_lines::put), this, x ) .postcondition([&] { BOOST_CONTRACT_ASSERT(str() == *old_str + x + '\n'); }) ; str_ = str_ + x + '\n'; } // Overload on argument type. void put(char x, boost::contract::virtual_* v = 0) /* override */ { boost::contract::old_ptr<std::string> old_str = BOOST_CONTRACT_OLDOF(v, str()); boost::contract::check c = boost::contract::public_function< override_put>( v, // `static_cast` resolves overloaded function pointer ambiguities. static_cast<void (string_lines::*)(char, boost::contract::virtual_*)>(&string_lines::put), this, x ) .postcondition([&] { BOOST_CONTRACT_ASSERT(str() == *old_str + x + '\n'); }) ; str_ = str_ + x + '\n'; } // Overload on argument type and arity (also with default parameter). void put(int x, bool tab = false, boost::contract::virtual_* v = 0) /* override */ { boost::contract::old_ptr<std::string> old_str = BOOST_CONTRACT_OLDOF(v, str()); boost::contract::check c = boost::contract::public_function< override_put>( v, // `static_cast` resolves overloaded function pointer ambiguities. static_cast<void (string_lines::*)(int, bool, boost::contract::virtual_*)>(&string_lines::put), this, x, tab ) .postcondition([&] { std::ostringstream s; s << x; BOOST_CONTRACT_ASSERT( str() == *old_str + (tab ? "\t" : "") + s.str() + '\n'); }) ; std::ostringstream s; s << str_ << (tab ? "\t" : "") << x << '\n'; str_ = s.str(); } private: std::string str_; };
Overloaded functions have the same function name so the same override_function-name
type can be reused as template parameter for all boost::contract::public_function
calls in a given class. Therefore, BOOST_CONTRACT_OVERRIDE
only needs to be invoked once for a function name in a given class, even
when that function name is overloaded.
While contracts are usually most useful to program specifications of functions and class interfaces, this library also allows to check contract conditions for implementation code (lambda functions, loops, code blocks, etc.).
Lambda functions are not member functions, they are not part of class public
interfaces so they do not check class invariants and they do not subcontract.
They can use boost::contract::function
to specify preconditions, postconditions, and exception guarantees (considerations
made in Non-Member
Functions apply). For example (see lambda.cpp
):
int total = 0; std::for_each(v.cbegin(), v.cend(), // Contract for a lambda function. [&total] (int const x) { boost::contract::old_ptr<int> old_total = BOOST_CONTRACT_OLDOF(total); boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT( total + x <= std::numeric_limits<int>::max()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(total == *old_total + x); }) ; total += x; // Lambda function body. } );
Similarly, boost::contract::function
can be used to program preconditions, postconditions, and exception guarantees
for loops. For example, for a for-loop but same for while- and all other
loops (see loop.cpp
):
int total = 0; // Contract for a for-loop (same for while- and all other loops). for(std::vector<int>::const_iterator i = v.begin(); i != v.end(); ++i) { boost::contract::old_ptr<int> old_total = BOOST_CONTRACT_OLDOF(total); boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT( total + *i <= std::numeric_limits<int>::max()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(total == *old_total + *i); }) ; total += *i; // For-loop body. }
More in general, boost::contract::function
can be used to program preconditions, postconditions, and exception guarantees
of any block of code in a given function. For example (see code_block.cpp
):
/* ... */ // Contract for a code block. { // Code block entry (check preconditions). boost::contract::old_ptr<int> old_total = BOOST_CONTRACT_OLDOF(total); boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(v.size() == 3); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(total == *old_total + v[0] + v[1] + v[2]); }) ; total += v[0] + v[1] + v[2]; // Code block body. } // Code block exit (check postconditions and exceptions guarantees). /* ... */
Finally, at the moment this library does not support contracts for functions
and classes declared constexpr
.
[58]
This library provides a mechanism to check assertions within implementation
code a part from preconditions, postconditions, exceptions guarantees, and
class invariants. Implementation checks are programmed using a nullary functor
that is directly assigned to a boost::contract::check
object declaration right at the place within the code where the checks need
to be performed (without calling boost::contract::function
,
boost::contract::public_function
,
etc. in this case). For example (see check.cpp
):
int main() { // Implementation checks (via nullary functor). boost::contract::check c = [&] { BOOST_CONTRACT_ASSERT(gcd(12, 28) == 4); BOOST_CONTRACT_ASSERT(gcd(4, 14) == 2); }; return 0; }
The implementation check functor should capture all the variables that it needs to assert the implementation checks. These variables can be captured by value when the overhead of copying such variables is acceptable. In any case, implementation checks 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 implementation check functor, but it is
recommended to keep this code simple using mainly assertions and if-statements
(to avoid programming complex checks that might be buggy and also 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 `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::check_failure
if any of the BOOST_CONTRACT_ASSERT
conditions are false and, more in general, if calling the implementation
check functor 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.).
Similarly to the C-style assert
macro that is disable when NDEBUG
is defined, implementation checks are disabled when BOOST_CONTRACT_NO_CHECKS
is defined. That will skip all implementation checks at run-time but it will
not eliminate some of the overhead of executing and compiling the related
boost::contract::check
declarations. Alternatively, this library provides the BOOST_CONTRACT_CHECK
macro that allows to completely remove run- and compile-time overhead of
implementation checks when BOOST_CONTRACT_NO_CHECKS
is defined (this is not a variadic macro):
BOOST_CONTRACT_CHECK(bool_cond) // Or, if `bool_cond` contains commas `,` not already within parenthesis `()`... BOOST_CONTRACT_CHECK((bool_cond)) // ...use extra parenthesis (not a variadic macro).
For example (see check_macro.cpp
):
int main() { // Implementation checks (via macro, disable run-/compile-time overhead). BOOST_CONTRACT_CHECK(gcd(12, 28) == 4); BOOST_CONTRACT_CHECK(gcd(4, 14) == 2); return 0; }
The BOOST_CONTRACT_CHECK
macro is similar to the C-style assert macro as it accepts a boolean condition
(instead of a nullary functor like boost::contract::check
does). [59] Using BOOST_CONTRACT_CHECK
is essentially equivalent to using the C-style assert
macro a part from the following:
BOOST_CONTRACT_NO_CHECKS
(instead of NDEBUG
for
disabling assert
).
boost::contract::check_failure
(instead assert
calls
std::abort
if the asserted condition is
false and unwinds the stack if evaluating the condition throws an exception).
BOOST_CONTRACT_ALL_DISABLE_NO_ASSERTION
).
(See Disable Contract Checking and Disable Contract Compilation for macros to completely remove run- and compile-time overhead also for preconditions, postconditions, exception guarantees, and class invariants.)
In the examples seen so far, old value variables of type boost::contract::old_ptr
are initialized to a copy of the expression passed to BOOST_CONTRACT_OLDOF
as soon as they are declared. This is correctly happens before the function
body is executed but also before the contract is declared, therefore even
before class invariants (for public functions) and preconditions are checked
at function entry. This might work well in most practical cases, however
technically old values should be copied before executing the function body
but after checking entry class invariants and preconditions
(see Assertions).
Specifically, there could be cases in which it makes sense to evaluate the
expressions passed to BOOST_CONTRACT_OLDOF
only under the assumption that assertions programmed in class invariants
and preconditions are true.
This library allows to construct boost::contract::old_ptr
variables using their default constructor (equivalent to a null pointer)
and then to later assign them to a copy of the expression passed to BOOST_CONTRACT_OLDOF
in a nullary
functor d
()
passed to .old(
d
)
. The functor d
()
is called by this library before the function
body is executed but only after class invariants and preconditions are checked.
Old value assignments via .old(...)
must appear after preconditions but before postconditions and exception guarantees
wen these are all present (see Preconditions,
Postconditions,
and Exception
Guarantees). [60]
For example, the following old value expression s[index]
passed to BOOST_CONTRACT_OLDOF
is valid only after the precondition has checked that index
is within valid range index < s.size()
.
Therefore, old_y
is first
declared using its default constructor (i.e., initialized to a null pointer)
and later assigned to a copy of s[index]
in .old(...)
after the precondition has checked index
(see old.cpp
):
char replace(std::string& s, unsigned index, char x) { char result; boost::contract::old_ptr<char> old_y; // Null, old value copied later... boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(index < s.size()); }) .old([&] { // ...after preconditions (and invariants) checked. old_y = BOOST_CONTRACT_OLDOF(s[index]); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(s[index] == x); BOOST_CONTRACT_ASSERT(result == *old_y); }) ; result = s[index]; s[index] = x; return result; }
The functor passed to .old(...)
should capture all variables it needs
to evaluate to copy the old value expressions passed to BOOST_CONTRACT_OLDOF
.
In general, these variables should be captured by reference and not by value
(because old values need to make copies of the values the captured variables
will have just before executing the function body, and not copy the values
these variables had when the functor passed to .old(...)
was first declared). In any case, the functor passed to .old(...)
should modify only old values and not the values of other captured variables
(see Constant-Correctness).
This library will automatically call the failure handler boost::contract::old_failure
if calling the functor specified via .old(...)
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 Failures to throw exceptions, exit the program with an error code,
etc.).
Note | |
---|---|
If old value pointers are initialized at the point of their construction
instead of using |
As seen in Public
Function Overrides, the BOOST_CONTRACT_OVERRIDE
macro has to be used to declare a type override_...
that is passed to boost::contract::public_function
for public function overrides. The function names passed to BOOST_CONTRACT_OVERRIDE
(and BOOST_CONTRACT_OVERRIDES
)
should never start with an underscore to avoid generating names containing
double underscores override__...
(which are reserved by the C++ standard).
There is a separate macro BOOST_CONTRACT_NAMED_OVERRIDE
that can be used to explicitly specify the name of the type being declared:
[62]
BOOST_CONTRACT_OVERRIDE(func_name) // Generate `override_...`. BOOST_CONTRACT_NAMED_OVERRIDE(type_name, func_name) // Generate `type_name`.
For example, the following public function override is named _1
so BOOST_CONTRACT_OVERRIDE(_1)
would declare a type named override__1
(which is reserved in C++ because it contains double underscores __
). thus BOOST_CONTRACT_NAMED_OVERRIDE(override1, _1)
is used to name the type override1
instead (see named_override.cpp
):
template<typename T> class positive_unary_pack #define BASES public generic_unary_pack<T> : BASES { public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; #undef BASES // BOOST_CONTRACT_OVERRIDE(_1) would generate reserved name `override__1`. BOOST_CONTRACT_NAMED_OVERRIDE(override1, _1) // Generate `override1`. virtual void _1(T const& value, boost::contract::virtual_* v = 0) /* override */ { // Use `override1` generated by BOOST_CONTRACT_NAMED_OVERRIDE above. boost::contract::check c = boost::contract::public_function<override1>( v, static_cast<void (positive_unary_pack::*)(T const&, boost::contract::virtual_*)>(&positive_unary_pack::_1), this, value ) .precondition([&] { BOOST_CONTRACT_ASSERT(value > 0); }) ; value1_ = value; } /* ... */
The BOOST_CONTRACT_NAMED_OVERRIDE
macro can also be used when the name override_...
generated by BOOST_CONTRACT_OVERRIDE
would clash with other names in user code, to generate names in CamelCase
or in any other preferred style, and in any other case when programmers need
or prefer to generate names different from override_...
.
Note that there is not a BOOST_CONTRACT_NAMED_OVERRIDES
macro so BOOST_CONTRACT_NAMED_OVERRIDE
needs to be invoked separately on each function name (there is instead a
BOOST_CONTRACT_OVERRIDES
macro as seen in Public
Function Overrides). [63]
As we have seen so far, programmers are required to decorate their classes declaring the following extra members that are internally used by this library to check contracts:
invariant
and static_invariant
member functions (used
to check class invariants, see Class
Invariants).
base_types
member
type declared via BOOST_CONTRACT_BASE_TYPES
(used to implement subcontracting, see Public
Function Overrides).
override_...
member types declared via BOOST_CONTRACT_OVERRIDE
,
BOOST_CONTRACT_NAMED_OVERRIDE
,
and BOOST_CONTRACT_OVERRIDES
(used to implement subcontracting for overriding functions, see Public
Function Overrides). [64]
In general, these members must be declared public
in the user class in order for this library to be able to access them. [65] However, programmers might need to more precisely control the
public members of their classes to prevent incorrect access of encapsulated
members. All these members can be declared private
as long as the boost::contract::access
class is declared as friend
of the user class. For example (see access.cpp
):
template<typename T> class vector #define BASES public pushable<T> : BASES { friend class boost::contract::access; // Friend `access` class so... typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; // ...private bases. #undef BASES void invariant() const { // ...private invariants. BOOST_CONTRACT_ASSERT(size() <= capacity()); } BOOST_CONTRACT_OVERRIDE(push_back) // ...private overrides. public: void push_back(T const& value, boost::contract::virtual_* v = 0) /* override */ { boost::contract::old_ptr<unsigned> old_size = BOOST_CONTRACT_OLDOF(v, size()); boost::contract::check c = boost::contract::public_function< override_push_back>(v, &vector::push_back, this, value) .precondition([&] { BOOST_CONTRACT_ASSERT(size() < max_size()); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == *old_size + 1); }) ; vect_.push_back(value); } /* ... */
This technique is not used in most examples of this documentation only for brevity, but programmers are encouraged to use it in real code.
Warning | |
---|---|
Not declaring |
If a condition checked using BOOST_CONTRACT_ASSERT
is evaluated to be false or, more in general, if any of the specified contract
code throws an exception (BOOST_CONTRACT_ASSERT
simply expands to code that throws a boost::contract::assertion_failure
exception, see No
Macros), this library will call an appropriate contract
failure handler function as follow:
BOOST_CONTRACT_ASSERT
assertions and exceptions thrown from within .precondition(...)
call boost::contract::precondition_failure
.
BOOST_CONTRACT_ASSERT
assertions and exceptions thrown from within .postcondition(...)
call boost::contract::postcondition_failure
.
BOOST_CONTRACT_ASSERT
assertions and exceptions thrown from within .except(...)
call boost::contract::except_failure
.
BOOST_CONTRACT_ASSERT
assertions and exceptions thrown from invariant()
and static_invariant()
call boost::contract::entry_invariant_failure
when checked at function entry and boost::contract::exit_invariant_failure
when checked at function exit.
.old(...)
call boost::contract::old_failure
.
BOOST_CONTRACT_ASSERT
assertions and exceptions thrown from implementation checks boost::contract::check c
=
nullary-functor
and BOOST_CONTRACT_CHECK(...)
call boost::contract::check_failure
.
By default, these contract failure handlers print a message to the standard
error std::cerr
and then terminate the program calling
std::terminate
. [66] However, programmers can override the default contract failure
handlers to perform any custom action on contract failure using the following
functions respectively:
boost::contract::set_precondition_failure
.
boost::contract::set_postcondition_failure
.
boost::contract::set_except_failure
.
boost::contract::set_entry_invariant_failure
and boost::contract::set_exit_invariant_failure
,
or boost::contract::set_invariant_failure
(to set both entry and exit invariant failure handlers at once for convenience).
boost::contract::set_old_failure
.
boost::contract::set_check_failure
.
These set_..._failure(
f
)
function calls return the contract failure
handler functor f
that they take
as input parameter. [67] For example (see throw_on_failure.cpp
):
int main() { boost::contract::set_precondition_failure( boost::contract::set_postcondition_failure( boost::contract::set_invariant_failure( boost::contract::set_old_failure( [] (boost::contract::from where) { if(where == boost::contract::from_destructor) { // Shall not throw from C++ destructors. std::clog << "ignored destructor contract failure" << std::endl; } else throw; // Re-throw (assertion_failure, user-defined, etc.). } )))); boost::contract::set_except_failure( [] (boost::contract::from where) { // Already an active exception so shall not throw another... std::clog << "ignored exception guarantee failure" << std::endl; } ); boost::contract::set_check_failure( [] { // But now CHECK shall not be used in destructor implementations. throw; // Re-throw (assertion_failure, user-defined, etc.). } ); /* ... */
When programming custom failure handlers that trow exceptions instead of terminating the program, programmers should be wary of the following:
noexcept
since C++11). This library passes
a boost::contract::from
parameter to the contract failure handlers for preconditions, postconditions,
class invariants, and old value copies at body (see boost::contract::precondition_failure
,
boost::contract::postcondition_failure
,
boost::contract::entry_invariant_failure
,
boost::contract::exit_invariant_failure
,
and boost::contract::old_failure
respectively). This boost::contract::from
parameter indicates if the contract failure occurred in a destructor,
constructor, or function call so programmers can use it to code custom
contract failure hander functions that never throw from destructors.
(In the example above, contract failures from destructors are simply
ignored even if that is probably never a safe thing to do in real code.)
boost::contract::check_failure
should also never throw, or implementation checks should never be used
in destructors otherwise these destructors will throw (note that boost::contract::check_failure
does not provide the boost::contract::from
parameter so it is not possible to differentiate from implementation
checks failing from destructors instead than from other parts of the
code).
boost::contract::except_failure
should never throw (regardless of the value of its boost::contract::from
parameter) because when boost::contract::except_failure
is called there is already an active exception on the stack, the exception
that triggered the exception guarantees to be checked in the first place
(throwing an exception while there is already an active exception will
force program termination or lead to undefined behaviour in C++).
Note | |
---|---|
It is the responsibility of the programmers to decide how to handle contract
failures from destructors when they program custom contract failure handlers
that throw exceptions instead of terminating the program (given that C++
and STL exception safety rules requires destructors to never throw). This
is not a simple dilemma and it might be a good reason to terminate the
program instead of throwing exceptions when assertions fail in C++ (as
this library and also C-style |
Contract assertions can be programmed to throw boost::contract::assertion_failure
using BOOST_CONTRACT_ASSERT(
condition
)
as we have seen so far (see No
Macros). Alternatively, contract assertions can be programmed to throw
any other exception (including user-defined exceptions) using code similar
to the following:
if(!condition
) throwexception-object
;
For example, the following precondition functor throws boost::contract:assertion_failure
(via BOOST_CONTRACT_ASSERT
)
on its first assertion and the user-defined exception too_large_error
on its second assertion (both exceptions will cause this library to call
the customized boost::contract::precondition_failure
listed above which will in turn re-throw the exceptions up he stack, see
throw_on_failure.cpp
):
struct too_large_error {}; template<unsigned MaxSize> class cstring #define BASES private boost::contract::constructor_precondition<cstring< \ MaxSize> > : BASES {
public: /* implicit */ cstring(char const* chars) : boost::contract::constructor_precondition<cstring>([&] { BOOST_CONTRACT_ASSERT(chars); // Throw `assertion_failure`. // Or, throw user-defined exception. if(std::strlen(chars) > MaxSize) throw too_large_error(); }) {
/* ... */ };
Finally, note that the exception specifiers noexcept
(since C++11) and throw
(deprecated
in C++11) of the enclosing operation declaring the contract correctly apply
to the contract code as well. Therefore, even if the contract failure handlers
are reprogrammed to throw exceptions in case of contract failures, those
exceptions will never be thrown outside the context of the enclosing operation
if that is not in accordance with the exception specifiers of that operation
(specifically, note that all destructors are implicitly declared noexcept
in C++11). For example, the following
code will correctly never throw from the noexcept
destructor, not even if the class invariants checked at destructor entry
throw too_large_error
and
the contract failure handlers for invariants are programmed to throw from
destructors (the program will always terminate in this case instead, see
throw_on_failure.cpp
):
struct too_large_error {}; template<unsigned MaxSize> class cstring #define BASES private boost::contract::constructor_precondition<cstring< \ MaxSize> > : BASES {
public: void invariant() const { if(size() > MaxSize) throw too_large_error(); // Throw user-defined ex. BOOST_CONTRACT_ASSERT(chars_); // Or, throw `assertion_failure`. BOOST_CONTRACT_ASSERT(chars_[size()] == '\0'); } ~cstring() noexcept { // Exception specifiers apply to contract code. // Check invariants. boost::contract::check c = boost::contract::destructor(this); }
/* ... */ };
/* ... */ // Warning... might cause destructors to throw (unless declared noexcept). boost::contract::set_invariant_failure( [] (boost::contract::from) { throw; // Throw no matter if from destructor, etc. } ); /* ... */
[52] This consequence of the substitution principle “that if any function in an inheritance hierarchy has no preconditions, then preconditions on functions overriding it have no useful effect” is also explicitly mentioned in the contract documentation of the D Programming Language (see [Bright04]).
[53]
Rationale: This library uses boost::optional
instead of std::optional
because std::optional
is not available before C++17.
[54]
Rationale: This library requires the postcondition
functor parameter to be of type boost::optional<... const&>
so the return value does not have
to be copied (because of &
)
while postconditions are still not allowed to change its value (because
of const
, see Constant-Correctness).
In addition, programmers are encouraged to declare the postcondition functor
to take its argument also as a constant reference boost::optional<... const&> const&
to avoid possibly expensive copies
of the boost::optional
type itself.
[55]
Technically, the extra virtual parameter can still be passed to BOOST_CONTRACT_OLDOF
but that is
not necessary and it has no effect so it is not done in this documentation.
[56]
Rationale: Contract programming proposals
for C++ like [N1962] do not provide
a mechanism for friend functions to check class invariants of objects passed
as parameters. In other words, these proposals do not enable contracts
to recognize that in C++ some friend functions logically act as if they
were part of the public interface of the objects they take as parameters.
This is reasonable for proposals that add contracts to the core language
because friend functions are not always meant to extend an object public
interface and C++ does not provide a mechanism to programmatically specify
when they do and when they do not. However, this library adds the flexibility
to let programmers manually specify when friend functions should also check
class invariants of the objects they take as parameters (using boost::contract::public_function
)
and when they should not (using boost::contract::function
instead).
[57]
Rationale: In order to avoid copies, this
library takes all function arguments and the return value passed to boost::contract::public_function
as references when used within public function overrides. Therefore, the
library cannot differentiate when the actual function argument and return
types are passed by reference and when they are not. As a result, the library
cannot automatically reconstruct the type of the enclosing public function
so this type must be deduced from the function pointer passed by programmers
to boost::contract::public_function
.
When this automatic deduction is not possible due to overloaded function
names, programmers must explicitly use static_cast
to resolve ambiguities as usual in C++ with pointers to overloaded functions.
[58]
Rationale: In general, it might be useful
to specify contracts for constexpr
functions and literal classes. However, the implementation of this library
cannot support contracts for constexpr
functions and classes because C++ does not currently allow constexpr
functions to do the following:
Declare local variables of (literal) types with non-trivial constexpr
destructors (this RAII technique
is used by this library to check invariants, postconditions, and exceptions
guarantees at exit); Call other constexpr
functions using try-catch statements (used by this library to report contract
assertion failures and catch any other exception that might be thrown when
evaluating the asserted conditions); Use lambda functions (used by this
library for convenience to program functors that that check preconditions,
postconditions, and exception guarantees). Also note that even if supported,
contracts for constexpr
functions
probably would not use old values (because constexpr
prevents functions from having any side effect visible to the caller and
variables recording such side-effects are usually the candidates for old
value copies) and subcontracting (because constexpr
functions cannot be virtual).
[59]
Of course, nothing prevents programmers from calling functors to specify
boolean conditions when if-guards and other statements are required to
assert the implementation checks. For example, programmers can use C++11
lambda functions to define and call such functors in place where the implementation
checks are specified BOOST_CONTRACT_CHECK([...] -> bool { ... } ())
.
[60]
Rationale: Functors for preconditions,
old value assignments, postconditions, and exception guarantees are all
optional but when specified, they must be specified in that order. Such
order is enforced by the fact that boost::contract::specify_precondition_old_postcondition_except
,
boost::contract::specify_old_postcondition_except
,
boost::contract::specify_postcondition_except
,
boost::contract::specify_except
,
and boost::contract::specify_nothing
provide a progressively smaller subset of their .precondition(...)
,
.old(...)
, .postcondition(...)
,
and .except(...)
member functions. The enforced order
for specifying preconditions, old value assignments, postconditions, and
exception guarantees makes logical sense because it follows the order at
which these are executed at run-time. Other contract programming frameworks
allow to mix this order, that could have been implemented for this library
as well but it would have complicated somewhat the library implementation
while adding no real value (arguably creating confusion in user code by
not enforcing a consistent order for specifying contract conditions).
[61]
Rationale: It would be possible for
this library to internally wrap all old value operations (boost::contract::old_ptr
copy
constructor, boost::contract::make_old
,
etc.) with try-catch statements so to call boost::contract::old_failure
also when old values are copied when they are constructed outside .old(...)
. However, that will prevent this
library from knowing the boost::contract::from
parameter and that would be problematic (specifically because destructors
can have postconditions so that parameter is necessary to make sure user-defined
failure handlers can be programmed to never throw from destructors as
C++ usually requires).
[62]
Rationale: A different macro BOOST_CONTRACT_NAMED_OVERRIDE
is used instead of overloading BOOST_CONTRACT_OVERRIDE
using variadic macros because the override macro cannot be programmed manually
by users so making it a variadic would prevent to use this library on compilers
that do not support variadic macros (see No
Macros).
[63]
Rationale: The syntax for invoking a possible
BOOST_CONTRACT_NAMED_OVERRIDES
macro would need to be something like BOOST_CONTRACT_NAMED_OVERRIDES(type_name1, func_name1, type_name2, func_name2, ...)
.
The authors found such a syntax less readable than repeating single BOOST_CONTRACT_NAMED_OVERRIDE
invocations as in BOOST_CONTRACT_NAMED_OVERRIDE(type_name1, func_name1) BOOST_CONTRACT_NAMED_OVERRIDE(type_name2, func_name2) ...
so
decided not to provide the BOOST_CONTRACT_NAMED_OVERRIDES
macro.
[64]
Rationale: The internals of the override_...
type generated by BOOST_CONTRACT_OVERRIDE
use names reserved by this library so programmers should not actually
use such a type even when it is declared public
.
[65]
There is some variability among compiler implementations: The base_types
member type needs to be declared
public
on MSVC, GCC, and Clang;
The invariant
and static_invariant
member functions need
to be declared public
on MSVC,
but not on GCC and Clang; The override_...
member types do not have to be declared
public
on any compiler. In
any case, declaring the boost::contract::access
class friend
as shown in this
section allows to always declare the extra members private
on all compilers.
[66] Rationale: In general, when a contract fails the only safe thing to do is to terminate program execution (because the contract failure indicates a bug in the program, and in general a buggy program will be in a state for which no operation can be successfully performed, so the program should be stopped as soon as possible). Therefore, this library terminates the program by default. However, for specific applications, programmers could implement some fail-safe mechanism for which some mission-critical operation could always be performed upon handling failures so this library allows programmers to override the default contract failure handlers to fully customize how to handle contract failures.
[67]
Rationale: The set_..._failure
functions take a functor as parameter (to accept not just function pointers
but also lambdas, binds, etc.) and they return this same functor as result
so they can be concatenated (this interface is a bit different from std::set_terminate
). The related get_..._failure
functions can be used to query
the functors currently set as failure handlers (this interface is similar
to std::get_terminate
).