mirror of
https://github.com/boostorg/contract.git
synced 2026-02-27 04:52:22 +00:00
518 lines
39 KiB
Plaintext
518 lines
39 KiB
Plaintext
|
|
[import ../example/features/public.cpp]
|
|
|
|
[section Tutorial]
|
|
|
|
This section gives an introduction on how to program contracts using this library.
|
|
|
|
[section Free Functions]
|
|
|
|
Consider the following free function `inc` which increments its argument by `1` and returns the value its argument had before the increment (this function is equivalent to the usual C++ operator `int operator++(int& x, int)`).
|
|
Let's write the contract for such a function using code comments (see also [@../../example/features/no_contracts.cpp =no_contracts.cpp=]):
|
|
|
|
[import ../../example/features/no_contracts.cpp]
|
|
[no_contracts]
|
|
|
|
The precondition states that the argument to increment must be smaller than the maximum allowable value of its type (otherwise the increment will go out-of-range).
|
|
The postconditions state that the argument was actually incremented by `1` and that the function return value is equal to the argument before it was incremented.
|
|
|
|
Now let's program this function and its contract using the [funcref boost::contract::function] function from this library (see also [@../../example/features/free_function.cpp =free_function.cpp=]):
|
|
|
|
[import ../../example/features/free_function.cpp]
|
|
[free_function]
|
|
|
|
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 also __Getting_Started__).
|
|
|
|
It is possible to specify both preconditions and postconditions for free functions (see also __Preconditions__ and __Postconditions__).
|
|
|
|
[funcref boost::contract::function] returns an RAII object (that can be assigned to a local variable of explicit type [classref boost::contract::guard] when programmers are not using C++11 auto declarations).
|
|
The free function body is programmed right after the declaration of this RAII object.
|
|
At construction, this RAII object does the following:
|
|
|
|
# Check preconditions, by calling the functor [^['f]]`()` passed to `.precondition(`[^['f]]`)`.
|
|
|
|
At destruction instead:
|
|
|
|
# If the function body did not throw an exception:
|
|
# Check postconditions, by calling the functor [^['g]]`()` passed to `.postcondition(`[^['g]]`)`.
|
|
|
|
This ensures that free function contracts are correctly checked at run-time (see also __Function_Calls__).
|
|
|
|
[note
|
|
A free function can avoid calling [funcref boost::contract::function] for efficiency but only when it has no preconditions and no postconditions.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
The same considerations also apply to private and protected member functions because their contracts are also programmed using [funcref boost::contract::function] like for free functions (see also __Private_and_Protected_Functions__).
|
|
|
|
[section Preconditions]
|
|
|
|
When preconditions are specified, they are programmed using a functor that can be called with no parameter [^['f]]`()` and it is passed to `.precondition(`[^['f]]`)`.
|
|
Contracts that do not have preconditions simply do not call `.precondition(...)`.
|
|
|
|
C++11 lambda functions are very convenient to program preconditions, but any other nullary functor can be used (see also __No_Lambda_Functions__).
|
|
[footnote
|
|
C++11 lambda functions with no parameters can be programmed as `[...] () { ... }` but also equivalently as `[...] { ... }`.
|
|
This second from is often used in this documentation omitting the empty parameter list `()` for brevity.
|
|
]
|
|
For example, for a free function contract (but same for all other contracts):
|
|
|
|
auto c = boost::contract::function() // Same for all other contracts.
|
|
.precondition([&] { // Capture by reference or value...
|
|
BOOST_CONTRACT_ASSERT(...); // ...but should not modify captures.
|
|
...
|
|
})
|
|
...
|
|
;
|
|
|
|
The precondition functor should capture all the variables that it needs to assert the preconditions.
|
|
These variables can be captured by value when the overhead associated with copying such variables is acceptable (but in this documentation preconditions often capture these variables by reference to avoid such overhead).
|
|
In any case, precondition assertions should not modify the value of the captured variables, even when those are captured by reference (see also __Constant_Correctness__).
|
|
|
|
Any code can be programmed for 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 slow to execute at run-time).
|
|
It is also recommended to use the [macroref BOOST_CONTRACT_ASSERT] macro to program the assertions because it enables this library to print very informative error messages when the asserted conditions are evaluated to be false at run-time (this is not a variadic macro, but see also __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if condition has commas `,` not already within parenthesis `(...)`.
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``))
|
|
|
|
This library will automatically call [funcref boost::contract::precondition_failed] if any of the [macroref BOOST_CONTRACT_ASSERT] macro conditions are `false` and also if the precondition functor call throws any exception (by default, this terminates the program calling `std::terminate`, but see __Throw_on_Failure__ to throw exceptions, exit the program with an error code, etc.).
|
|
|
|
Preconditions can be specified before or after postconditions (by calling `.precondition(...)` before or after `.postcondition(...)`).
|
|
This library will generate a compile-time error if preconditions are specified multiple times for the same contract.
|
|
|
|
[note
|
|
Contracts are most useful when the assertions only use public members that are accessible to the caller so the caller can properly check and use the contract.
|
|
In particular, preconditions of a public member function or constructor that use non-public members are essentially incorrect because they cannot be fully checked by the caller (in fact, Eiffel generates a compile-time error in this case).
|
|
|
|
This library leaves it up to the programmers to only use public members when programming contracts and especially when programming preconditions (see also __Specification_and_Implementation__).
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Postconditions]
|
|
|
|
When postconditions are specified, they are programmed using a functor that can be called with no parameter [^['g]]`()` and it is passed to `.postcondition(`[^['g]]`)`.
|
|
Contracts that do not have postconditions simply do not call `.postcondition(...)`.
|
|
|
|
C++11 lambda functions are very convenient to program postconditions, but any other nullary functor can be used (see also __No_Lambda_Functions__).
|
|
For example, for a free function contract (but same for all other contracts):
|
|
|
|
auto 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 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 also __Return_Value__ and __Old_Values__).
|
|
In any case, postcondition assertions should not modify the value of the captured variables (see also __Constant_Correctness__).
|
|
|
|
Any code can be programmed for 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 execute at run-time).
|
|
It is also recommended to use the [macroref BOOST_CONTRACT_ASSERT] macro to program the assertions because it enables this library to print very informative error messages when the asserted conditions are evaluated to be false at run-time (this is not a variadic macro, but see also __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if condition has commas `,` not already within parenthesis `(...)`.
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``))
|
|
|
|
This library will automatically call [funcref boost::contract::postcondition_failed] if any of the [macroref BOOST_CONTRACT_ASSERT] macro conditions are `false` and also if the postcondition functor call throws any exception (by default, this terminates the program calling `std::terminate`, but see __Throw_on_Failure__ to throw exceptions, exit the program with an error code, etc.).
|
|
|
|
Postconditions can be specified before or after preconditions (by calling `.postcondition(...)` before or after `.precondition(...)`).
|
|
This library will generate a compile-time error if postconditions are specified multiple times for the same contract.
|
|
|
|
[endsect]
|
|
|
|
[section Return Value]
|
|
|
|
When the function non-void return value is used in postconditions, programmers are responsible to declare a local variable before the contract and to assign it to the return value at function exit (when the function does not throw an exception).
|
|
[footnote
|
|
The name of the local variable that holds the return value is arbitrary, but `result` is often used in this documentation.
|
|
]
|
|
For example, for a free function contract (but same for all other contracts):
|
|
|
|
``/return-type/`` result; // Must be assigned to return value.
|
|
auto c = boost::contract::function() // Same for all other contracts.
|
|
...
|
|
.postcondition([&] { // Also capture `result` reference...
|
|
... // ...but should not modify captures.
|
|
})
|
|
;
|
|
|
|
At any point where the enclosing function returns, programmers are responsible to assign the result variable to the expression being returned.
|
|
This can be easily done by making sure that /all/ the `return` statements in the function are of the form:
|
|
|
|
return result = ``/expression/``; // Assign `result` at each return.
|
|
|
|
The functor used to program postconditions should capture the result variable by reference (because postconditions must access the value this variable will have at function exit, and not the value this variable had when the postcondition functor was first constructed).
|
|
The return value should never be used in preconditions (because the return value is not yet evaluated and set when preconditions are checked, see also __Assertions__).
|
|
In any case, programmers should not modify the result variable in the contract assertions (see also __Constant_Correctness__).
|
|
|
|
It is also possible to declared result variables using `boost::optional` when the return type does not have a default constructor, or if the default constructor is too expensive to execute, etc. (see also __Optional_Return_Value__).
|
|
|
|
Virtual and overriding public member functions must always declare and use a result variable even when that is not directly used in postconditions (see also __Virtual_Public_Functions__ and __Overriding_Public_Functions__).
|
|
|
|
[endsect]
|
|
|
|
[section Old Values]
|
|
|
|
When old values are used in postconditions, programmes are responsible to declare local variables before the contract and to assign them to the related expressions via the [macroref BOOST_CONTRACT_OLDOF] macro.
|
|
[footnote
|
|
The name of the local variable that holds an old value is arbitrary, but `old_`[^['name]] is often used in this documentation.
|
|
]
|
|
For example, for a free function contract (but same for all other contracts):
|
|
|
|
...
|
|
boost::shared_ptr<``/type/`` const> old_``/name/`` = BOOST_CONTRACT_OLDOF(``/expression/``);
|
|
auto c = boost::contract::function() // Or some other contract object.
|
|
...
|
|
.postcondition([&] { // Capture by reference...
|
|
... // ...but should not modify captures.
|
|
})
|
|
;
|
|
|
|
Old values are handled as smart pointers to `const` values (so they cannot be mistakenly changed by the contract assertions, see also __Constant_Correctness__).
|
|
[footnote
|
|
*Rationale.*
|
|
`boost::shared_ptr` was used instead of `std::shared_ptr` because this library is designed to work well with Boost and technically to not require C++11 (even if the use of this library without C++11 lambda functions requires programmer to write a fare amount of boiler-plate code).
|
|
]
|
|
Therefore, old values must be dereferenced using `operator*` or `operator->` when they are used in postconditions (otherwise the compiler will most likely error).
|
|
This library ensures that old value smart pointers are not null by the time postconditions are checked so programmers can always safely dereference old value pointers in postconditions without any extra checking.
|
|
|
|
Old values should never be used in preconditions (because old values are same as original values when preconditions are checked, see also __Assertions__).
|
|
In fact, this library does not even guarantee that old value pointers are always not null when precondition functors are called (for example, when postcondition contract checking is disabled using [macroref BOOST_CONTRACT_CONFIG_NO_POSTCONDITONS], when checking an overridden virtual function contract via subcontracting, etc.).
|
|
Finally, see __Old_Values_at_Body__ for always delaying the assignment of old values until after preconditions (and possibly class invariants) have been checked first (in case it is important to simplify old value expressions programming them under the assumption that precondition and class invariant conditions are satisfied already).
|
|
|
|
This library ensures that old values are only copied once by the [macroref BOOST_CONTRACT_OLDOF] macro that they are actually never copied when postcondition contract checking is disabled by the [macroref BOOST_CONTRACT_CONFIF_NO_POSTCONDITIONS] configuration macro.
|
|
|
|
[macroref BOOST_CONTRACT_OLDOF] is actually a variadic macro and it takes an extra parameter when used in virtual or overriding public functions (see also __Virtual_Public_Functions__ and __Overriding_Public_Functions__, and see __No_Macros__ to program old values without using macros).
|
|
|
|
In the rest of this documentation, C++11 auto declarations will be often used for old values instead of explicitly writing the `boost::shared_ptr<... const>` type.
|
|
This is done just for convenience, and programmers can alway explicitly specify the declaration type if they are not using C++11 auto declarations.
|
|
|
|
[endsect]
|
|
|
|
[section Class Invariants]
|
|
|
|
When class invariants are specified, they are programmed in a public `const` member function named `invariant` taking no argument and returning `void`.
|
|
Classes that do not have invariants, simply do not declare an `invariant` member function.
|
|
[footnote
|
|
This library uses template meta-programming (SFINAE-based introspection techniques) to check invariants only for classes that declare a member function named `invariant`.
|
|
]
|
|
For example:
|
|
|
|
class a {
|
|
public: // Must be public.
|
|
void invariant() const { // Must be const.
|
|
BOOST_CONTRACT_ASSERT(...);
|
|
...
|
|
}
|
|
|
|
...
|
|
};
|
|
|
|
|
|
This member function must be `const` because contracts should not modify the object state (see also __Constant_Correctness__).
|
|
This library will generate a compile-time error if the `const` qualifier is missing (unless [macroref BOOST_CONTRACT_CONFIG_PERMISSIVE] is set).
|
|
|
|
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 execute at run-time).
|
|
It is also recommended to use the [macroref BOOST_CONTRACT_ASSERT] macro to program the assertions because it enables this library to print very informative error messages when the asserted conditions are evaluated to be false at run-time (this is not a variadic macro, but see also __No_Macros__):
|
|
|
|
BOOST_CONTRACT_ASSERT(``/boolean-condition/``)
|
|
// Or, if condition has commas `,` not already within parenthesis `(...)`.
|
|
BOOST_CONTRACT_ASSERT((``/boolean-condition/``))
|
|
|
|
This library will automatically call [funcref boost::contract::entry_invariant_failed] or [funcref boost::contract::exit_invariant_failed] if any of the [macroref BOOST_CONTRACT_ASSERT] macro conditions are `false` and also if the `invariant` function throws an exception (by default, this terminates the program calling `std::terminate`, but see __Throw_on_Failure__ to throw exceptions, exit the program with an error code, etc.).
|
|
|
|
See __Access__ to avoid making `invariant` a public member function (e.g., in cases when all public members of a class must be controlled exactly).
|
|
Set the [macroref BOOST_CONTRACT_CONFIG_INVARIANT] configuration macro to use a name different from `invariant` (e.g., because `invariant` clashes with other names in the user-defined class).
|
|
|
|
Furthermore, see __Static_Public_Functions__ and __Volatile_Public_Functions__ for programming class invariants for `static` and `volatile` public member function calls respectively.
|
|
|
|
[note
|
|
No contracts are checked (not event class invariants) when a data member is accessed directly (this is different from Eiffel where even accessing public data members checks class invariants).
|
|
Therefore, it might be best for both `class`es and `struct`s to have no mutable public data members and to access data members publicly only via appropriate public member functions that can check the class invariants.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Constructors]
|
|
|
|
Contracts for constructors are programmed using the [funcref boost::contract::constructor] function and the [classref boost::contract::constructor_precondition] base class.
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_constructor]
|
|
|
|
It is not possible to specify preconditions using `.precondition(...)` for constructors (the library will generate a compile-time error if `.precondition(...)` is used on the object returned by [funcref boost::contract::constructor]).
|
|
Constructor preconditions are specified using the [classref boost::contract:constructor_precondition] base class instead (see also __Preconditions__).
|
|
Programmes should not access the object `this` from constructor preconditions (because the object does not exists yet before the constructor body is executed, see also __No_Lambda_Functions__).
|
|
Constructors without preconditions simply do not explicitly initialize the [classref boost::contract::constructor_precondition] base (the base default constructor is used instead and that checks no contract).
|
|
When [classref boost::contract::constructor_precondition] is used:
|
|
|
|
* It must be specified as the /first/ class in the inheritance list (so constructor preconditions are checked before initializing any other base or member).
|
|
* Its inheritance level should always be `private` (so this extra base class does not alter the public inheritance tree of the derived class).
|
|
* It takes the derived class as template parameter (the Curiously Recursive Template Pattern (CRTP) is used here to avoid ambiguity errors with multiple inheritance).
|
|
[footnote
|
|
*Rationale.*
|
|
The [classref boost::contract::constructor_precondition] takes the derived class as its template parameter so the instantiated template type is unique for each derived class always avoiding base class ambiguities even in case of multiple inheritance.
|
|
Virtual inheritance cannot be used resolve such ambiguities because virtual bases are initialized only once by the out-most deriving class, and that would not allow to properly check preconditions of all base classes.
|
|
]
|
|
|
|
It is possible to specify postconditions for constructors (see also __Postconditions__).
|
|
Programmers should not access the old value of the object `this` in constructor postconditions (because the object did not exist yet before the constructor body was executed, see also __No_Lambda_Functions__).
|
|
[funcref boost::contract::constructor] takes `this` as a parameter because constructors check class invariants (see also __Class_Invariants__).
|
|
|
|
[funcref boost::contract::constructor] returns an RAII object (that can be assigned to a local variable of explicit type [classref boost::contract::guard] when programmers are not using C++11 auto declarations).
|
|
The constructor body is programmed right after the declaration of this RAII object.
|
|
At construction, this RAII object does the following:
|
|
|
|
# Check static class invariants, by calling ['[^typeof]]`(*this)::static_invariant()` (but not non-static class invariants because the object does not exists yet).
|
|
|
|
At destruction instead:
|
|
|
|
# Check static class invariants, by calling ['[^typeof]]`(*this)::static_invariant()`.
|
|
# If the constructor body did not throw an exception:
|
|
# Check non-static class invariants, by calling `this->invariant()`.
|
|
# Check postconditions, by calling the functor ['[^g]]`()` passed to `.postcondition(`['[^g]]`)`.
|
|
|
|
This together with C++ construction mechanism of base classes and the use of [classref boost::contract::constructor_precondition] ensures that the constructor contracts are correctly checked at run-time (see also __Constructor_Calls__).
|
|
|
|
[note
|
|
A constructor can avoid calling [funcref boost::contract::constructor] for efficiency but only when it has no postconditions and its class has no invariants.
|
|
(Even if [funcref boost::contract::constuctor] is not used, the constructor's class can still have bases because their contracts are always checked by C++ construction mechanism.)
|
|
]
|
|
|
|
Private and protected constructors can omit [funcref boost::contract::constructor] because they are not part of the public interface of the class so they are not required to check class invariants (see also __Constructor_Calls__).
|
|
They could still use [classref boost::contract::constructor_precondition] to check preconditions before member initializations, and use [funcref boost::contract::function] to just check postconditions instead.
|
|
|
|
[endsect]
|
|
|
|
[section Destructors]
|
|
|
|
Contracts for destructors are programmed using the [funcref boost::contract::destructor] function.
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_destructor]
|
|
|
|
It is not possible to specify preconditions for destructors (the library will generate a compile-time error if `.precondition(...)` is used here because destructors can be called at any time after construction so they have no precondition).
|
|
It is possible to specify postconditions for destructors (see also __Postconditions__ and see __Static_Public_Functions__ for an example).
|
|
Programmers should not access the object `this` in destructor postconditions (because the object no longer exists after the destructor body has been executed, see also __No_Lambda_Functions__).
|
|
[funcref boost::contract::destructor] takes `this` as a parameter because destructors check class invariants (see also __Class_Invariants__).
|
|
|
|
[funcref boost::contract::destructor] returns an RAII object (that can be assigned to a local variable of explicit type [classref boost::contract::guard] when programmers are not using C++11 auto declarations).
|
|
The destructor body is programmed right after the declaration of this RAII object.
|
|
At construction, this RAII object does the following:
|
|
|
|
# Check static and non-static class invariants, by calling ['[^typeof]]`(*this)::static_invariant()` __AND__ `this->invariant()`.
|
|
|
|
At destruction instead:
|
|
|
|
# Check static class invariants, by calling ['[^typeof]]`(*this)::static_invariant()`.
|
|
# If the destructor body threw an exception:
|
|
# Check non-static class invariants, by calling `this->invariant()` (because the object was not successfully destructed).
|
|
# Else:
|
|
# Check postconditions, by calling the functor ['[^g]]`()` passed to `.postcondition(`['[^g]]`)`.
|
|
|
|
This together with C++ destruction mechanism of base classes ensures that destructor contracts are correctly checked at run-time (see also __Destructor_Calls__).
|
|
|
|
[note
|
|
A destructor can avoid calling [funcref boost::contract::destructor] for efficiency but only when it has no postconditions and its class has no invariants.
|
|
(Even if [funcref boost::contract::destructor] is not used, the destructor's class can still have bases because their contracts are always checked by C++ destruction mechanism.)
|
|
]
|
|
|
|
Private and protected destructors can omit [funcref boost::contract::destructor] because they are not part of the public interface of the class so they are not required to check class invariants (see also __Destructor_Calls__).
|
|
They could use [funcref boost::contract::function] to just check postconditions instead.
|
|
|
|
[endsect]
|
|
|
|
[section Public Functions]
|
|
|
|
Contracts for public member functions are programmed using the [funcref boost::contract::public_function] function.
|
|
|
|
Let's first consider public member functions that are not static, not virtual, and do not override any function from base classes.
|
|
For example, the following such a function `find` is declared as a member of the `unique_identifiers` class (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_function]
|
|
|
|
It is possible to specify both preconditions and postconditions for public member functions (see also __Preconditions__ and __Postconditions__).
|
|
[funcref boost::contract::public_function] takes `this` as a parameter because public member functions check class invariants (see also __Class_Invariants__).
|
|
|
|
[funcref boost::contract::public_function] returns an RAII object (that can be assigned to a local variable of explicit type [classref boost::contract::guard] when programmers are not using C++11 auto declarations).
|
|
The public member function body is programmed right after the declaration of this RAII object.
|
|
At construction, this RAII object does the following:
|
|
|
|
# Check static and non-static class invariants, by calling ['[^typeof]]`(*this)::static_invariant()` __AND__ `this->invariant()`.
|
|
# Check preconditions, by calling the functor ['[^f]]`()` passed to `.precondition(`['[^f]]`)`.
|
|
|
|
At destruction instead:
|
|
|
|
# Check static and non-static class invariants, by calling ['[^typeof]]`(*this)::static_invariant()` __AND__ `this->invariant()` (even if the function body threw an exception).
|
|
# If the function body did not throw an exception:
|
|
# Check postconditions, by calling the functor ['[^g]]`()` passed to `.postcondition(`['[^g]]`)`.
|
|
|
|
This ensures that public member function contracts are correctly checked at run-time (see also __Public_Function_Calls__).
|
|
|
|
[note
|
|
A public member function can avoid calling [funcref boost::contract::public_function] for efficiency but only when it has no preconditions and no postconditions, it is not virtual, it does not override any virtual function, and its class has no invariant.
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section Virtual Public Functions]
|
|
|
|
Let's now consider public member functions that are virtual but that still do not override any function from base classes.
|
|
For example, the following such a function `push_back` is declared as a member of the `unique_identifiers` class (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_virtual_function]
|
|
|
|
Public virtual functions must declare an extra trailing parameter of type [classref boost::contract::virtual_]`*` and assign it to `0` by default (i.e., null).
|
|
Because this extra parameter is the last one and it has a default value, it does not alter the calling interface of the virtual function and callers will essentially never have to deal with it explicitly (a part from when manipulating the virtual function type for function pointer type-casting, etc.).
|
|
Programmers must pass the extra virtual parameter as the very first argument to all [macroref BOOST_CONTRACT_OLDOF] and [funcref boost::contract::public_function] calls in the virtual function.
|
|
[footnote
|
|
*Rationale.*
|
|
The [classref boost::contract::virtual_]`*` optional parameter is used by this library to determine that a function is virtual (in C++ it is not possible to introspect if a function has been declared `virtual`).
|
|
Furthermore, this parameter is used by this library to pass result and old values that are evaluated by overriding function to the overridden virtual functions, and also to check preconditions and postconditions (but without executing the body) of the overridden virtual functions when subcontracting.
|
|
]
|
|
|
|
Furthermore, for non-void public virtual functions, programmers must pass a reference to the function return value as the second argument to [funcref boost::contract::public_function].
|
|
In this case the functor specified to `.postcondition(...)` takes a single parameter (possibly as a constant reference `const&`) to the return value.
|
|
[footnote
|
|
*Rationale.*
|
|
The functor passed to `.postcondition(...)` takes the extra return value parameter because that is used by this library to pass the return value evaluated by the overriding function to all its overridden virtual functions when subcontracting.
|
|
]
|
|
|
|
[important
|
|
It is the responsibility of the programmers to pass the extra result parameter to [funcref boost::contract::public_function] for non-void virtual functions.
|
|
[footnote
|
|
*Rationale.*
|
|
This library does not require the function type when using [funcref boost::contract::public_function] for non-overriding virtual functions.
|
|
Therefore, this library does not know if the enclosing function has a non-void return type and it cannot enforce that at compile-time.
|
|
It can do that only for overriding virtual functions.
|
|
]
|
|
]
|
|
|
|
[important
|
|
It is the responsibility of the programmers to pass the virtual extra parameter to all [macroref BOOST_CONTRACT_OLDOF] and [funcref boost::contract::public_function] calls, and also to pass the return value reference to [funcref boost::contract::public_function] for non-void functions.
|
|
This library cannot automatically generate compile-time errors if programmers fail to do so, but in general contract checking will not correctly work at run-time in such cases.
|
|
|
|
['Remember: When "`v`" is present, always pass it to old-of macros and contract-function calls; Always pass result after "`v`" for non-void functions.]
|
|
]
|
|
|
|
For the rest, the same considerations made in __Public_Functions__ apply.
|
|
|
|
[endsect]
|
|
|
|
[section Overriding Public Functions (Subcontracting)]
|
|
|
|
Let's now consider public member functions (virtual or not) that override public virtual functions from one or more public base class.
|
|
For example, the following such a function `push_back` is declared as a member of the `identifiers` derived class and it overrides `push_back` from the `unique_identifiers` base class (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
[footnote
|
|
In this document, overriding functions are often marked with the `/* override */` comment.
|
|
On compilers that support C++11 virtual specifiers `override` can be used instead (`override` is not used in the documentation only because virtual specifiers are not widely supported yet, even by compilers that support other C++11 features like lambda functions).
|
|
]
|
|
|
|
[public_override_function]
|
|
|
|
(See the __Base_Types__ section below for more information about [macroref BOOST_CONTRACT_BASE_TYPES] and `base_types`.)
|
|
|
|
Overriding public functions must always list the extra trailing parameter of type [classref boost::contract::virtual_]`*` with `0` default value, even when they are not declared `virtual` (because this parameter is present in the signature of the virtual function being overridden from base classes).
|
|
Programmers must pass the extra virtual parameter as the very first argument to all [macroref BOOST_CONTRACT_OLDOF] and [funcref boost::contract::public_function] calls in the overriding function (as for non-overriding virtual public functions discussed above).
|
|
|
|
For overriding functions, [funcref boost::contract::public_function] takes an explicit template argument `override_`[^['function-name]] that must be defined at class scope using [macroref BOOST_CONTRACT_OVERRIDE][^(['function-name])] (see right after the function).
|
|
When called from overriding public functions, [funcref boost::contract::public_function] also takes a pointer to the enclosing function, the object `this` (because overriding public functions check class invariants), and references to each function argument in the order they appear in the function declaration.
|
|
[footnote
|
|
*Rationale.*
|
|
The object `this` is passed after the function pointer to follow `bind`'s syntax.
|
|
The function pointer and references to all function arguments are needed for overriding virtual public functions because this library has to call overridden virtual public functions to check their contracts for subcontracting (even if without executing their bodies).
|
|
]
|
|
|
|
Furthermore, for non-void overriding public functions, programmers must pass a reference to the function return value as the second argument to [funcref boost::contract::public_function] (this library will generate a compile-time error otherwise).
|
|
[footnote
|
|
*Rationale.*
|
|
Again, the extra return value parameter is used to pass the return value to the overridden virtual public functions when subcontracting.
|
|
However, in this case the library has also 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 was not possible instead for non-overriding virtual public functions because their contracts do not use the function pointer, see also __Virtual_Public_Functions__).
|
|
]
|
|
In this case the functor specified to `.postcondition(...)` takes a single parameter (possibly as a constant reference `const&`) to the return value (this library will generate a compile-time error otherwise).
|
|
|
|
[important
|
|
['Remember: When "`v`" is present, always pass it to old-of macros and contract-function calls; Always pass result after "`v`" for non-void functions.]
|
|
]
|
|
|
|
[funcref boost::contract::public_function] returns an RAII object (that can be assigned to a local variable of explicit type [classref boost::contract::guard] when programmers are not using C++11 auto declarations).
|
|
The public member function body is programmed right after the declaration of this RAII object.
|
|
At construction, this RAII object doe the following:
|
|
|
|
# Check static and non-static class invariants for all overridden bases and for the derived class, by calling [^['typeof](['overridden_base])]`::static_invariant()` __AND__ [^['overridden_base]]`.invariant()` __AND__... [^['typeof]]`(*this)::static_invariant()` __AND__ `this->invariant()`.
|
|
# Check preconditions for all overridden base functions and for the overriding derived function in __OR__ with each other, by calling functors [^['f1]]`()` __OR__... [^['fn]]`()` passed to `.precondition(`[^['f1]]`)`, ..., `.precondition(`[^['fn]]`)` for all of the overridden and overriding functions respectively.
|
|
|
|
At destruction instead:
|
|
|
|
# Check static and non-static class invariants for all overridden bases and for the derived class, by calling [^['typeof](['overridden_base])]`::static_invariant()` __AND__ [^['overridden_base]]`.invariant()` __AND__... [^['typeof]]`(*this)::static_invariant()` __AND__ `this->invariant()` (even if the function body threw an exception).
|
|
# If the function body did not throw an exception:
|
|
# Check postconditions for all overridden base functions and for the overriding derived function in __AND__ with each other, by calling functors [^['g1]]`()` __AND__... [^['gn]]`()` passed to `.postcondition(`[^['g1]]`)`, ..., `.postcondition(`[^['gn]]`)` for all of the overridden and overriding functions respectively.
|
|
|
|
This ensures that overriding public function subcontracts are checked correctly at run-time (see also __Public_Function_Calls__).
|
|
|
|
This library will generate a compile-time error if there is no suitable virtual function to overload in any of the public base classes (similar to C++11 `override` virtual specifier, but limited to functions with the extra [classref boost::contract::virtual_]`*` parameter and searched recursively only in `public` base classes passed to [macroref BOOST_CONTRACT_BASE_TYPES]).
|
|
|
|
[macroref BOOST_CONTRACT_OVERRIDE] does not have to be used in a public section of the class (see also __Access__).
|
|
[macroref BOOST_CONTRACT_OVERRIDE] must be used only once in a given class and overloaded functions can reuse it, plus [macroref BOOST_CONTRACT_NAMED_OVERRIDE] can be used to generate a name different from `override_...` (see also __Overloads_and_Named_Override__).
|
|
|
|
For the rest, the same considerations made in __Virtual_Public_Functions__ apply.
|
|
|
|
[endsect]
|
|
|
|
[section Base Types (Subcontracting)]
|
|
|
|
In order to support subcontracting, this library must be made aware of base classes.
|
|
Programmers do that using the [macroref BOOST_CONTRACT_BASE_TYPES] macro to declared a public member type named `base_types` with a `typedef`.
|
|
For example (see also [@../../example/features/public.cpp =public.cpp=]):
|
|
|
|
[public_base_types]
|
|
|
|
For convenience, a /local macro/ named `BASES` (any other name can be used) is often used here to avoid repeating the base list twice (first when inheriting `: ...` and then when calling [macroref BOOST_CONTRACT_BASE_TYPES]`(...)`).
|
|
Being a local macro, `BASES` must be undefined after it has been used to declare `base_types` (to avoid macro redefinition errors).
|
|
|
|
[macroref BOOST_CONTRACT_BASE_TYPES] is a variadic macro and accepts a list of bases separated by commas (see __No_Macros__ to program `base_types` without using macros).
|
|
When the extra base [classref boost::contract::constructor_precondition] is used to program constructor preconditions, it must always be private and appear as the very first base (see also __Constructors__).
|
|
|
|
[important
|
|
Each base passed to [macroref BOOST_CONTRACT_BASE_TYPES] must /explicitly/ specify its inheritance access level `public`, `protected`, or `private` (`virtual` can be specified before or after the access level as usual in C++).
|
|
[footnote
|
|
*Rationale.*
|
|
This library explicitly requires the inheritance access level because derived classes must subcontract only from public bases, but not from protected or private bases (see __Public_Function_Calls__).
|
|
Therefore, [macroref BOOST_CONTRACT_BASE_TYPES] inspect each inheritance access level (using preprocessor meta-programming) and strips non-public bases from the `base_types` list of bases to consider for subcontracting.
|
|
]
|
|
This library will generate a compiler-error if the first base is missing the inheritance access level, but this library is not able to automatically detect if the access level is missing from subsequent bases.
|
|
|
|
Therefore, it is the responsibility of the programmers to make sure that all bases passed to [macroref BOOST_CONTRACT_BASE_TYPES] explicitly specify their inheritance access level.
|
|
(Note that the inheritance access level is instead optional in C++ because `private` is implicitly assumed for `class` types and `public` for `struct` types.)
|
|
]
|
|
|
|
See __Access__ to avoid making `base_types` a public member type (e.g., in cases when all public members of a class must be controlled exactly).
|
|
Set the [macroref BOOST_CONTRACT_CONFIG_BASE_TYPES] configuration macro to use a name different from `base_types` (e.g., because `base_types` clashes with other names in the user-defined class).
|
|
|
|
[endsect]
|
|
|
|
[section Private and Protected Functions]
|
|
|
|
Private and protected member 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 also __Function_Calls__).
|
|
Instead, private and protected member functions only check preconditions and postconditions, like free functions do.
|
|
|
|
Therefore, [funcref boost::contract::function] is used to program contracts for private and protected functions, like for free functions.
|
|
For example (see also [@../../example/features/private_protected.cpp =private_protected.cpp=]):
|
|
|
|
[import ../../example/features/private_protected.cpp]
|
|
[private_protected]
|
|
|
|
The same considerations made in __Free_Functions__ apply.
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|