diff --git a/doc/advanced_topics.qbk b/doc/advanced_topics.qbk index 7d9edd3..3997a67 100644 --- a/doc/advanced_topics.qbk +++ b/doc/advanced_topics.qbk @@ -3,6 +3,42 @@ This section illustrates more advanced uses of this library. +[section Old Values at Body] + +In the examples seen so far old value variables of type [classref boost::contract::old_ptr] are initialized to a copy of the expression passed to [macroref BOOST_CONTRACT_OLDOF] at the point of their declaration. +This correctly is before the function body is executed but also before the contract is executed, therefore even before class invariants at function entry and preconditions are checked. + +This is convenient and might be sufficient in most cases. +However, in general old values should be copied before executing the function body but after checking entry class invariants and preconditions (see __Assertions__). +There can be cases in which the expression passed to [macroref BOOST_CONTRACT_OLDOF] should be evaluated only if the assertions in class invariants and preconditions are checked to be true. + +This library allows to construct [classref boost::contract::old_ptr] variables using their default constructor (equivalent to a null pointer) and then assign them later to a copy of the expression passed to [macroref BOOST_CONTRACT_OLDOF] in a functor with no parameter [^['h]]`()` passed to `.old(`[^['h]]`)`. +The nullary functor [^['h]]`()` is called by this library before the function body is executed but only after class invariants and preconditions are checked: + + boost::contract::old_ptr<...> old_``[^['name]]``; // Use default constructor. + boost::contract::guard c = boost::contract::function() // Same for all other contracts. + ... + .old([&] { // Capture by reference... + old_``[^['name]]`` = BOOST_CONTRACT_OLDOF(``[^['expression]]``); // ...but modify only old values. + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(*old_``[^['name]]`` ...); + ... + }) + ; + +For example, the following old value expression `s[index]` passed to [macroref BOOST_CONTRACT_OLDOF] is valid only after the precondition has checked that `index` is within 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 that `index` is in range (see also [@../../example/features/old.cpp =old.cpp=]): + +[import ../example/features/old.cpp] +[old] + +The functor passed to `.old(...)` should capture all variables it needs to evaluate and copy old value expressions. +In general, these variables should be captured by reference and not by value (because old values need to copy values the variables will have just before executing the function body, and not the value these variables had when the functor passed to `.old(...)` was first declared). +In any case, this functor should modify only old values and not the value of other captured variables (see also __Constant_Correctness__). + +[endsect] + [section Optional Return Value] It is possible to use `boost::optional` to handle the return value 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 result value at run-time). @@ -29,121 +65,256 @@ When this technique is used, programmers have to make sure that each return stat This also ensures that `result` is always set to the return value before the postconditions are checked. Therefore, 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). -Similarly, `boost::optional` can be used for return values passed to virtual and overriding functions (see __Pure_Virtual_Public_Functions__ for an example). +[heading Virtual and Overriding Public Functions] + +Similarly, `boost::optional` can be used for return values of virtual and overriding public functions. +As usual with non-void virtual and overriding public functions the return value `result` must be passed as a parameter to [funcref boost::contract::public_function] right after `v` (see also __Virtual_Public_Functions__). +This this case the functor passed to `.postcondition(...)` takes a parameter of type `boost::optional<`[^['result-type]]` const&> const&` (see __Pure_Virtual_Public_Functions__ for an example): + + boost::optional<``[^['return-type]]``> result; + ... + boost::contract::guard c = boost::contract::public_function(v, result, ...) + ... + .postcondition([&] (boost::optional<``[^['return-type]]`` const&> const& result) { + ... + } + ; + ... + return *(result = ...); + +The inner `const&` in the postcondition functor parameter type `boost::optional<... const&>` is mandatory (the outer `const&` in the postcondition functor parameter type `boost::optional<... const&> const&` is not). +[footnote +*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 prevented from changing it (because of `const`, see also __Constant_Correctness__). +Note that the library cannot require the actual functor parameter `result` to be `const&` instead (because programmers can always program the functor without specifying the `const&` outside the `boost::optional<... const&>` type). +] [endsect] [section Pure Virtual Public Functions] -In C++, pure virtual functions are allowed to have a /default implementation/ as long as such implementation is programmed out-of-line and defined outside the class declaring the pure virtual function as `virtual ... = 0;`. -Contracts for pure virtual public functions are programmed using the [funcref boost::contract::public_function] function very much like contracts for virtual public functions, so all consideration made in __Virtual_Public_Functions__ apply. +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 as `virtual ... = 0;`. +Contracts for pure virtual public functions are programmed using the [funcref boost::contract::public_function] function like for (non-pure) virtual public functions, so all consideration made in __Virtual_Public_Functions__ apply. However, in this case contracts are always programmed out-of-line, in the default implementation of the pure virtual function. -For example, note how the following `shape::area` default implementation (which is used to program the contract) must be defined out-of-line and therefore outside the `shape` class declaration (see also [@../../example/features/pure_virtual.cpp =pure_virtual.cpp=]): +For example, note how the following `shape::get_surface` default implementation (which is used to program the pure virtual function contract) must be defined out-of-line and therefore outside the `shape` class declaration (see also [@../../example/features/pure_virtual_public.cpp =pure_virtual_public.cpp=]): -[import ../example/features/pure_virtual.cpp] -[pure_virtual] +[import ../example/features/pure_virtual_public.cpp] +[pure_virtual_public] This library will never actually execute the pure virtual function body while it is calling the pure virtual function default implementation to check its contract 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 want to take full advantage of C++ pure virtual function default implementation outside of what strictly needed by this library). -Finally, in this example, the pure virtual function does not have enough information to meaningfully initialize the return value `result` (it is missing the actual shape dimensions like edges, radius, etc.) so `boost::optional` is used to declare the return value (see also __Optional_Return_Value__). +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 C++ pure virtual function default implementation outside of what strictly required by this library). + +Note that because of subcontracting, preconditions of derived class functions are checked in __OR__ with preconditions of base class functions (see __Public_Function_Overrides__). +If base class member functions specify no precondition then preconditions specified by overriding functions in derived classes will have no effect (because when checked in __OR__ with the base class function that has no precondition they will always pass). +This correctly reflects the fact that the base class member function can be called in any context (because it has no precondition) and so must all its overriding function in derived classes in order for the derived class to act like the base class in virtue of inheritance. +However, it is sometimes acceptable for a base class to declare a pure virtual function with a precondition `BOOST_CONTRACT_ASSERT(false)` indicating that the pure virtual function must be redefined by derived classes (as always with pure virtual functions) and also that derived classes will be responsible to specify preconditions (this technique makes sense only for preconditions of pure virtual functions otherwise it will prevent a concrete base function from being ever called successfully). +For example (see also [@../../example/features/named_override.cpp =named_override.cpp=]): + +[import ../example/features/named_override.cpp] +[named_override_pure_virtual_assert_false] As already discussed in __Private_and_Protected_Functions__, private and protected member functions do not check class invariants and do not subcontract (not even when they are virtual or pure virtual). Therefore, no contract is ever programmed for a private or protected pure virtual function (because that contract would never be checked during subcontracting anyway). [endsect] -[section Overloads and Named Overrides] +[section Overloaded Functions] -Calls to [funcref boost::contract::public_function] from different overloaded functions reuse the same `override_`[^['function-name]] template argument. -Therefore, [macroref BOOST_CONTRACT_OVERRIDE][^(['function-name])] is used only once in a given class even when [^['function-name]] is overloaded. -For example (see also [@../../example/features/override_overload.cpp =override_overload.cpp=]): +As seen in __Public_Function_Overrides__, [funcref boost::contract::public_function] takes a pointer to the enclosing function as a parameter when used in overriding public functions. +When an overriding public function is overloaded, the 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++). +[footnote +*Rationale.* +In oder to avoid copies, this library takes all contracted function arguments and the return value as references when passed to [funcref boost::contract::public_function] for overriding public functions. +Therefore, the library cannot differentiate the actual argument and return types of the contracted functions when they are passed by reference and when they are not. +As a result, the library cannot automatically reconstruct the contracted function pointer type which must be instead deduced from the function pointer explicitly passed by programmers to [funcref boost::contract::public_function] (in turn this requires using `static_cast` to resolve ambiguities as usual in C++ when obtaining the pointer of overloaded functions). +] +For example, note how `static_cast` is used in the following calls to [funcref boost::contract::public_function] (see also [@../../example/features/overload.cpp =overload.cpp=]): -[import ../example/features/override_overload.cpp] -[override_overload] +[import ../example/features/overload.cpp] +[overload] -Note that the function name passed to [macroref BOOST_CONTRACT_OVERRIDE] should never start with an underscore to avoid generating names containing double underscores `override__...` that are reserved by the C++ standard. +Overloaded functions have the same function name so the same [^override_['function-name]] type can be used as template parameter of [funcref boost::contract::public_function]. +Therefore, [macroref BOOST_CONTRACT_OVERRIDE] only needs to be invoked once for any given function name even when the function name is overloaded (as shown in the example above). + +[endsect] + +[section Named Overrides] + +The function name passed to [macroref BOOST_CONTRACT_OVERRIDE] should never start with an underscore to avoid generating names containing double underscores `override__...` that are reserved by the C++ standard. There is a separate macro [macroref BOOST_CONTRACT_NAMED_OVERRIDE] that can be used to explicitly specify the name of the type that will be passed to [funcref boost::contract::public_function] as a template argument: [footnote *Rationale.* -It is best to use a different macro name [macroref BOOST_CONTRACT_NAMED_OVERRIDE] instead of overloading [macroref BOOST_CONTRACT_OVERRIDE] using variadic macros because the override macros cannot be programmed manually by the users so making them variadic macros would prevent using this library on compilers that do not support variadic macros (see also __No_Macros__). +A different macro [macroref BOOST_CONTRACT_NAMED_OVERRIDE] is used instead of overloading [macroref BOOST_CONTRACT_OVERRIDE] using variadic macros because the override macros cannot be programmed manually by the users so making them variadic would prevent the use of this library on compilers that do not support variadic macros (see also __No_Macros__). ] - BOOST_CONTRACT_OVERRIDE(``[^['function-name]]``) // Generate `override_...`. - BOOST_CONTRACT_NAMED_OVERRIDE(``[^['type-name]]``, ``[^['function-name]]``) + BOOST_CONTRACT_OVERRIDE(``[^['function-name]]``) // Generate `override_...`. + BOOST_CONTRACT_NAMED_OVERRIDE(``[^['type-name]]``, ``[^['function-name]]``) // Generate `type-name`. -This second macro can be used for function names that start with an underscore `_...`, when the name `override_`[^['function-name]] clashes with another name the user class, or in any other case when programmers need to generate a name different than `override_...`. -For example (see also [@../../example/features/named_override.cpp =named_override.cpp=]): +For example, the following overriding member function is named `_1` so `BOOST_CONTRACT_OVERRIDE(_1)` would generate a type named `override__1` (which is reserved in C++ because it contains double underscores `__`), `BOOST_CONTRACT_NAMED_OVERRIDE(override1, _1)` is used to name the type `override1` instead (see also [@../../example/features/named_override.cpp =named_override.cpp=]): -[import ../example/features/named_override.cpp] [named_override] -[endsect] - -[section Volatile Public Functions] - -TODO +The [macroref BOOST_CONTRACT_NAMED_OVERRIDE] macro can be used for function names that start with an underscore `_...`, when the name `override_`[^['function-name]] generated by [macroref BOOST_CONTRACT_OVERRIDE] would clash with other names in the user code, to generate names in CamelCase or any other style, in any other case when programmers need or want to generate names different than `override_...`. [endsect] -[section Old Values at Body] +[section Volatile Class Invariants] -TODO +This library allows to specify a different set of class invariants to be checked for public volatile member functions. +These /volatile class invariants/ are programmed in a public member function `const volatile` qualified and named `invariant` (see [macroref BOOST_CONTRACT_INVARIANT] to name the invariant function differently from `invariant` and __Access__ to not have to declare it public). -[import ../example/features/old.cpp] -[old] +In general, `const volatile` qualified invariants work the same as `const` qualified invariant (see __Class_Invariants__) with the only difference that `volatile` and `const volatile` member functions check `const volatile` invariants while mutable (i.e., neither `const` nor `volatile` qualified) and `const` member functions check `const` invariants. +A given class can specify both `const volatile` and `const` qualified invariant member functions: +[footnote +*Rationale.* +Constructors and destructors check `const volatile` and `const` invariants in that order because the qualifier that limits the calls the least is checked first (note that a `const volatile` calls can be made on any object while `const` calls cannot be made on `volatile` non-`const` objects, in that sense the `const volatile` qualifier limits calls on an object less than `const` alone does). +This is consistent with `static` class invariants that are checked even before `const volatile` invariants (the `static` classifier limits calls even less than `const volatile` in the sense that an object is not even needed to make static calls). +While there is a more important reason to check `static` invariants before all other invariants (see __Contract_Programming_Overview__), the above is the only reason why this library checks `const volatile` invariants before `const` invariants for constructors and destructors. +] + +* Constructors check both `const volatile` and `const` qualified invariants in that order (at exit if no exception is thrown). +* Destructors check both `const volatile` and `const` qualified invariants (at entry). +* Both mutable and `const` public member functions check `const` qualified invariants (at entry and at exit if no exception is thrown). +* Both `volatile` and `const volatile` public member functions check `const volatile` qualified invariants (at entry and at exit if no exception is thrown). + +This ensures that volatile class invariants are correctly checked (see also __Constructor_Calls__, __Destructor_Calls__, and __Public_Function_Calls__). +For example (see also [@../../example/features/volatile.cpp =volatile.cpp=]): + +[import ../example/features/volatile.cpp] +[volatile] + +While this library does not automatically check `const volatile` invariants for non-volatile functions, programmers can explicitly call the `const volatile` invariant function from the `const` invariant function if that makes sense for the contracts being specified (that way all public member functions `volatile` and not will check `const volatile` invariants): +[footnote +*Rationale.* +Note that while all public member functions can be made to check `const volatile` invariants, it is never possible to make volatile public member functions check `const` non-volatile invariants. +That is because both `const` and `volatile` can always be added but never stripped in C++ (a part from forcefully via `const_cast`) but `const` is always automatically added by this library in order to enforce contract constant-correctness (see __Constant_Correctness__). +That said, it would be incorrect for this library to also automatically add `volatile` and require all functions to check `const volatile` (not just `const`) invariants because only `volatile` members can be accessed from `const volatile` invariants so there could be many `const` (but not `const volatile`) members that are accessible from `const` invariants but not from `const volatile` invariants. +To avoid this confusion, this library has chosen to draw a clear dichotomy between `const` and `const volatile` invariants so that only volatile members check `const volatile` invariants and only non-volatile members check `const` (but not `const volatile`) invariants. +This is simple and should serve most cases. +If programmers need non-volatile members to check `const volatile` invariants, they can explicitly do so by calling the `const volatile` invariant function from the `const` invariant function as shown in this documentation. +] + + class ``[^['class-type]]`` { + public: + void invariant() const { + ``[^['class-type]]`` const volatile& cv = *this; + cv.invariant(); // Call `void invariant() const volatile` below. + ... + } + + void invariant() const volatile { + ... + } + + ... + }; + +As usual, static class invariants can also be specified (see __Static_Class_Invariants__) and private and protected member functions do not check any invariant (see __Private_and_Protected_Functions__). + +[endsect] + +[section Old Value Requirements] + +Old values require copying the expression passed to [macroref BOOST_CONTRACT_OLDOF] so the type of that expression must be copy constructible. +More precisely, dereferencing an old value pointer of type [classref boost::contract::old_ptr]`` requires `boost::is_copy_constructible::value` to be `true` (otherwise this library will generate a compile-time error). + +* In some cases it might be acceptable, or even desirable, to cause a compile-time error when a program uses old value types that are not copy constructible (because it is not possible to fully check the correctness of the program as stated by the contract assertions that use these old values). +In these cases, programmers can declare old values using [classref boost::contract::old_ptr] as seen so far (or equivalently using C++11 `auto` declarations `auto ... = BOOST_CONTRACT_OLDOF(...)`). +[footnote +*Rationale*. +When C++11 `auto` declarations are used, this library defaults the type of [macroref BOOST_CONTRACT_OLDOF] to [classref boost::contract::old_ptr] because it generates a compile-time error for non-copyable types so it is in general more conservative than [classref boost::contract::noncopyable_old_ptr]. +] +* However, in other cases it might be desirable to simply not check assertions that use some old values when the related old value types are not copy constructible. +Programmers can do this by using [classref boost::contract::noncopyable_old_ptr] instead of [classref boost::contract::old_ptr] to program these old values (and by checking if the old value pointer is not null before dereferencing it in postconditions). + +For example, consider the following `accumulate` function template that could in general be instantiated for types `T` that are not copy constructible (i.e., `boost::is_copy_constructible::value` is `false`): + +[import ../example/features/noncopyable_old.cpp] +[noncopyable_old] + +The old value pointer `old_total` is programmed using [classref boost::contract::noncopyable_old_ptr] so if `T` is not copy constructible then `total` will simply not be copied and `old_total` will be left as a null pointer (in these cases `old_total` must be checked to be not null `if(old_total) ...` before it can be dereferenced in the postconditions). +If the above example used [classref boost::contract::old_ptr] instead then the library would have generated a compile-time error if `accumulate` is instantiated for types `T` that are not copy constructible (but only if `old_total` is actually dereferenced in the contract assertions somewhere `*old_total ...`). + +Note that the `noncopyable_...` prefix of the name [classref boost::contract::noncopyable_old_ptr]`` refers to the pointed type `T` that may or not be copy constructible without causing a compile-time error in this case (the old value pointer itself is always copyable, or at least copy assignable). + +[heading No C++11] + +In general, the `boost::is_copy_constructible` type trait requires C++11 for full support. +On non-C++11 compilers, it is possible to inherit the old value type from `boost::noncopyable`, or use `BOOST_MOVABLE_BUT_NOT_COPYABLE`, or explicitly specialize the `boost::is_copy_constructible` template (see [@http://www.boost.org/doc/libs/release/libs/type_traits/doc/html/boost_typetraits/reference/is_copy_constructible.html boost::is_copy_constrictible] for more information): + + #include + + namespace boost { + template<> + struct is_copy_constructible<``[^['old-value-type]]``> : false_type {}; + } [endsect] [section Assertion Requirements (Static-If)] -In general, assertions can introduce a new set of requirements on the types used by the program: Some of the type requirements might be necessary only to program the assertions and they would not be required by the program otherwise. +In general, assertions can introduce a new set of requirements on the types used by the program. +Some of these type requirements might be necessary only to check the assertions and they would not be required by the program otherwise. * In some cases it might be acceptable, or even desirable, to cause a compile-time error when a program uses types that do not provide all the operations needed to check contract assertions (because it is not possible to fully check the correctness of the program as stated by the contracts). -In these cases, programmers can specify contract assertions as we have seen so far, and compilation will fail if user types do not provide all operations necessary to check the contracts. +In these cases, programmers can specify contract assertions as we have seen so far, compilation will fail if user types do not provide all operations necessary to check the contracts. * However, in other cases it might be desirable that adding contracts to a program does not alter its type requirements and that assertions are simply not checked when user types do not provide all the operations necessary to check them. -With this library, this can be done using [funcref boost::contract::call_if] and [funcref boost::contract::call_if_c]. +Programmers can do this by using [funcref boost::contract::check_if] (and [funcref boost::contract::check_if_c]) within the contract assertions. -For example, let's consider the STL `vector` class template. -This class template (as specified by the STL without the contracts) does not normally require that `T` has an equality operator `==` (it only requires `T` to be copy constructible). -However, the full contracts of the `vector::push_back(value)` member function include a postcondition `back() == value` which introduces the new requirement that `T` must have an equality operator `==`. -Programmers can specify this postcondition as-is an let the program fail to compile when users instantiate this template with a type `T` that does not provide an equality operator `==`. -Otherwise, programmers can specify this postcondition using [funcref boost::contract::call_if] so to check the assertion only for types `T` that have an equality operator `==`, and trivially check `true` otherwise (see also [@../../example/features/call_if.cpp =call_if.cpp=]): +For example, let's consider the following `vector` class template. +This class template does not usually require that `T` has an equality operator `==` (it only requires `T` to be copy constructible, see `std::vector` documentation). +However, the contracts of the `vector::push_back(value)` member function include a postcondition `back() == value` which introduces the new requirement that `T` must also have an equality operator `==`. +Programmers can specify this postcondition as usual `BOOST_CONTRACT_ASSERT(back() == value)` an let the program fail to compile when users instantiate this template with a type `T` that does not provide an equality operator `==`. +Otherwise, programmers can specify this postcondition using [funcref boost::contract::check_if] to check the assertion only for types `T` that have an equality operator `==` and trivially check `true` otherwise, for example (see also [@../../example/features/check_if.cpp =check_if.cpp=]): -[import ../example/features/call_if.cpp] -[call_if] +[import ../example/features/check_if.cpp] +[check_if] -[funcref boost::contract::call_if] takes a condition template parameter as a boolean meta-function. -It takes a "then" nullary functor `t`, it calls and returns `t()` when the boolean meta-function evaluates to `true` at compile-time (thus `t()` must be valid code only in this case). -It provides an `else_` member to specify an "else" nullary functor `e`, it calls and returns `e()` when the boolean meta-function evaluates to `false` at compile-time (thus `e()` must be valid code only in this case). -It also provides an `else_if` member that can be used (multi times) to specify other boolean meta-function conditions and "then" and "else" nullary functors to be evaluated and valid only when the boolean meta-function is `false` at compile-time. -`else_` and `else_if` are optional if `t()` return value is `void` (otherwise the return types of `t()`, `e()`, and all the other nullary functors passed to `else_if` must all be compatible). -(There are also alternative versions named [funcref boost::contract::call_if_c] and `else_if_c` that take boolean constants instead of boolean meta-function conditions as template parameters.) +The [funcref boost::contract::check_if] function template is a special case of the more general facility [funcref boost::contract::call_if]: +Specifically, `boost::contract::check_if<`[^['condition]]`>(`[^['check]]`)` is equivalent to: - boost::contract::call_if<``[^['boolean-meta-function-1]]``>( - ``[^['then-nullary-functor-1]]`` - ).else_if<``[^['boolean-meta-function-2]]``>( // Optional. - ``[^['then-nullary-functor-2]]`` - ) - ``[^['...]]`` // Else-if can be repeated zero or more times. - .else_( // Optional for functors returning `void`. - ``[^['else-nullary-functor]]`` + boost::contract::call_if<``[^['condition]]``>( + ``[^['check]]`` + ).else_( + [] { return true; } ) -Therefore, the above `push_back` example calls (via `bind`) the functor `std::equal_to()(boost::cref(back()), boost::cref(value))` that checks the postcondition `back() == value` requiring `T`'s equality operator `==`, but only when the `boost::has_equal` meta-function evaluates to `true` at compile-time. -Otherwise, the lambda function `[] { return true; }` is used to trivially evaluate the postcondition assertion to be `true` when `T` does not have an equality operator `==`. +Where [^['condition]] is a nullary boolean meta-function and [^['check]] is a nullary boolean functor. +If [^['condition]]`::value` is statically evaluated to be `true` at compile-time then [^['check]]`()` is called at run-time and its boolean result is returned by the enclosing `call_if`. +Otherwise, if [^['condition]]`::value` is statically evaluated to be `false` at compile-time then `[] { return true; }()` is called at run-time and `true` is trivially returned by the enclosing `call_if`. +Note that [^['check]] must be a functor template (and not just a functor) so its code that contains the assertion operations with the extra type requirements (e.g., the operator `==`) will not be instantiated and compiled for specific types unless the compiler determines it will be actually called at run-time (C++14 generic lambdas and functor templates like `std::equal_to` can be used to program [^['check]], but C++11 lambdas cannot). + +More in general, [funcref boost::contract::call_if] accepts a number of optional else-if and one optional else statement: -[heading Old Value Requirements] + boost::contract::call_if<``[^['condition1]]``>( + ``[^['then1]]`` + ).template else_if<``[^['condition2]]``>( // Optional. + ``[^['then2]]`` + ) + ... // Optionally, other `else_if`. + .else_( // Optional for `void` functors, otherwise required. + ``[^['else]]`` + ) -TODO +Where [^['condition1]], [^['condition2]], ... are nullary boolean meta-functions and [^['then1]], [^['then2]], ..., [^['else]] are nullary functors. +The return types of the functor calls [^['then1]]`()`, [^['then2]]`()`, ..., [^['esle]]`()` must either all be the same (possibly all `void`) or be of types implicitly convertible into one another. +At run-time [funcref boost::contract::call_if] will call the functor [^['then1]]`()`, or [^['then2]]`()`, ..., or [^['else]]`()` depending on which meta-function [^['condition1]]`::value`, [^['condition2]]`::value`, ... is evaluated to be `true` or `false` at compile-time, at it will return the value returned by the functor being called +If [^['then1]], [^['then2]], ..., [^['else]] are nullary functor templates (not just nullary functors) then their code will only be compiled if the compiler determines they need to be actually called at run-time (so only if the related [^['condition1]]`::value`, [^['condition2]]`::value`, ... are evaluated to be `true` or `false` at compile-time). +All the `esle_if<...>(...)` statements are optional, the `else_(...)` statement is optional if the functor calls return `void` but it is required otherwise. + +In general, [funcref boost::contract::call_if] can be used to program contract assertions that compile and check different functor templates depending on related conditions being evaluated to be `true` at compile-time (but in most cases [funcref boost::contract::check_if] should be sufficient, simpler and less verbose to use). + +The [funcref boost::contract::check_if_c], [funcref boost::contract::call_if_c], and `.else_if_c` function templates work similarly to their counterparts without the `..._c` postfix above, but they take their condition template parameters as static boolean values instead of nullary boolean meta-functions. [heading Static-If (C++14)] -[funcref boost::contract::call_if] is a general-purpose facility and it can be used together with C++14 generic lambdas to implement statements similar to `static if` (at least at function scope, see also [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3613.pdf N3613]). -For example, consider the following implementation of `std::advance` that uses `static if`-like statements implemented via [funcref boost::contract::call_if] (see also [@../../example/features/static_if_cxx14.cpp =static_if_cxx14.cpp=]): +The [funcref boost::contract::call_if] function template is a general facility and its use is not limited to programming contracts. +In fact, [funcref boost::contract::call_if] can be used together with C++14 generic lambdas to program statements similar to the `static if` proposal (at least at function scope, see also [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3613.pdf N3613]). +For example, consider the following implementation of `std::advance` that uses `static if`-like statements programmed via [funcref boost::contract::call_if] (see also [@../../test/call_if/advance_cxx14.cpp =advance_cxx14.cpp=]): -[import ../example/features/static_if_cxx14.cpp] -[static_if_cxx14] +[import ../test/call_if/advance_cxx14.cpp] +[advance_cxx14] This implementation is much more concise, easy to read and maintain than the usual implementation of `std::advance` that uses tag dispatching. [footnote @@ -154,58 +325,60 @@ This implementation is much more concise, easy to read and maintain than the usu [section Access] -As we have seen, this library requires programmers to augment their classes declaring special members that are internally used to implement contracts: +As seen so far, programmers are required to decorate their classes declaring extra members that are internally used by this library to check contracts: -* The `invariant` and `static_invariant` member functions (used by this library to check class invariants). -* The `base_types` member type (used by this library to implement subcontracting). -* The `override_`[^['function-name]] member type (used by this library to implement subcontracting for overriding functions). - -Normally, some of these members must be public in order to be accessible by this library. -However, in some cases programmers might need to exactly control the public members of their classes in order to prevent users from incorrectly accessing encapsulated members (this might be especially true in large projects). - -`override_`[^['function-name]] never needs to be `public` and programmers can always declare it using [macroref BOOST_CONTRACT_OVERRIDE] in a `private` (or `protected`) section of the class. +* The `invariant` and `static_invariant` member functions (used to check class invariants, see also __Class_Invariants__). +* The `base_types` member type declared via [macroref BOOST_CONTRACT_BASE_TYPES] (used to implement subcontracting, see also __Public_Function_Overrides__). +* The `override_...` member types declared via [macroref BOOST_CONTRACT_OVERRIDE], [macroref BOOST_CONTRACT_NAMED_OVERRIDE], and [macroref BOOST_CONTRACT_OVERRIDES] (used to implement subcontracting for overriding functions, see also __Public_Function_Overrides__). [footnote *Rationale.* -The internals of the `override_...` type generated by [macroref BOOST_CONTRACT_OVERRIDE] use names reserved by this library to users are not able to actually use such a type even when it is defined as a `public` member. -In theory, using C++14 generic lambdas, the [macroref BOOST_CONTRACT_OVERRIDE] macro could be re-implemented in a way so it can be expanded at function scoped (instead of class scoped). +The internals of the `override_...` type generated by [macroref BOOST_CONTRACT_OVERRIDE] use names reserved by this library to users should not actually use such a type even when it is defined `public`. +(On a related note, in theory using C++14 generic lambdas, the [macroref BOOST_CONTRACT_OVERRIDE] macro could be re-implemented in a way so it can be expanded at function scoped, instead of class scoped.) ] -`invariant`, `static_invariant`, and `base_types` normally need to be `public`, but programmers can declare them in a `private` (or `protected`) section of the class as long as they also declare the [classref boost::contract::access] class as `friend`. -For example (see also [@../../example/features/access.cpp =access.cpp=]): + +In general, these members must be declared `public` in the user class in order for this library to access them. +[footnote +There is some variability among compiler implementations: +the `base_types` member type needs to be declared `public` on all MSVC, GCC, ang 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 [classref boost::contract::access] class `friend` allows to always declare all these extra members `private` on all compilers. +] +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 [classref boost::contract::access] class is declared as `friend`, for example (see also [@../../example/features/access.cpp =access.cpp=]): [import ../example/features/access.cpp] [access] This technique is not used in most examples of this documentation only for brevity, but programmers are encouraged to use it in real code. -See also __Base_Types__, __Class_Invariant__, and __Overloads_and_Named_Override__ to declare these members with different names (e.g., when they clash with other names in the user-defined class). - [endsect] [section Separate Body Implementation] Contracts are part of the program specification and not of its implementation (see also __Specification_and_Implementation__). -However, this library uses function definitions to program the contracts so contract code will normally appear together with the function implementation code. -Contract code programmed with this library must always appear at the very top of the function definition so programmers will easily be able to distinguish it from the rest of function implementation in any case (so it is not a real problem in practise). +However, this library uses function definitions to program the contracts so contract code appears together with the function implementation code. +This is not ideal, but contract code programmed with this library must always appear at the very top of the function definition so programmers will easily be able to distinguish it from the rest of function implementation code (so this might not be real problem in practise). -In some cases, it might be desirable to completely separate the contract code from the code that implements the function body implementation code. +In some cases, it might be desirable to completely separate the contract code (function specification) from the function body code (function implementation). For example, this could be necessary for software that ships only header files and pre-compiled source code to its users (notably, that cannot be done for template code in C++). -If the contracts are programmed in the function definitions that are pre-compiled with the source code, users will not be able to inspect the contract code to understand semantics and usage of the functions (again, this might not be a real problem in practice for example if contract code is already being extracted from the source code and presented as part of the documentation of the shipped software). +If the contracts are programmed in the function definitions that are pre-compiled with the source code, users will not be able to inspect the contract code to understand semantics and usage of the functions (again, this might not be a real problem in practice for example if contract code is already somehow extracted from the source code and presented as part of the documentation of the shipped software). In such cases, the function implementation can be programmed in an extra /body function/ (e.g., named `..._body`) that is defined in the source code. -The original function definition remains in the header file instead, it programs the contract and simply calls the extra body function. -At the cost of programmers writing an extra function declaration for the body function, this technique allows to keep the contract code in header files while separating the body implementation code to source files (with the limitation that constructor member initialization lists must still be programmed in the header files because that is where the originally constructors are actually defined). +The original function definition remains in the header files instead, it programs the contract and simply calls the extra body function. +At the cost of programmers writing an extra function declaration for the body function, this technique allows to keep the contract code in header files while separating the body implementation code to source files (with the limitation that constructor member initialization lists must still be programmed in the header files because that is where the constructors are actually defined). -For example, the following header file only contains function declarations and contract code (see also [@../../example/features/separate_body.hpp =separate_body.hpp=]): +For example, the following header file only contains function declarations and contract code (function specifications) and constructor member initializations (see also [@../../example/features/separate_body.hpp =separate_body.hpp=]): [import ../example/features/separate_body.hpp] [separate_body_hpp] -Instead, the code implementing the function bodies is programmed in a separate source file (see also [@../../example/features/separate_body.cpp =separate_body.cpp=]): +Instead, the function bodies (function implementations) is programmed in a separate source file (see also [@../../example/features/separate_body.cpp =separate_body.cpp=]): [import ../example/features/separate_body.cpp] [separate_body_cpp] -The same technique can be used for free, private, and protected functions. +The same technique can be used for non-member, private, and protected functions. [endsect] @@ -252,9 +425,11 @@ By default, none of these macros are defined so this library checks all contract For example, programmers could decide to check all contracts during early development builds, but later check only preconditions and maybe entry invariants for release builds by defining [macroref BOOST_CONTRACT_NO_POSTCONDITIONS] and [macroref BOOST_CONTRACT_NO_EXIT_INVARIANTS]. -[heading Disable Contract Compilation] +[endsect] -Library macros can also be used to disable compile-time overhead introduced by contracts but at the cost of manually programming `#ifdef` statements around contract code. +[section Disable Contract Compilation] + +This library provides macros that can be used to disable compile-time overhead introduced by contracts but at the cost of manually programming `#ifdef` statements around contract code. The authors of this library do not recommend to use this practice unless strictly necessary because it makes the contract code more verbose, less readable, and in most applications the compile-time overhead of contracts should not represent an issue (it should be sufficient to disable contract checking at run-time as indicated before). In any case, the following example illustrates how to completely disable contract code compilation for non-member functions (see also [@../../example/features/ifdef.cpp =ifdef.cpp=]): @@ -283,72 +458,65 @@ Also the [macroref BOOST_CONTRACT_NO_ENTRY_INVARIANTS] and [macroref BOOST_CONTR [section No Macros (No C++11)] -[import ../example/features/no_macros.cpp] - -This section illustrates how to write contracts without using this library macros and programming the related code manually instead (a part from [macroref BOOST_CONTRACT_OVERRIDE] and [macroref BOOST_CONTRACT_NAMED_OVERRIDE] that cannot be programmed manually). +It is possible to specify contracts without using this library macros and programming the related code manually instead (a part from [macroref BOOST_CONTRACT_OVERRIDE], [macroref BOOST_CONTRACT_OVERRIDES], and [macroref BOOST_CONTRACT_NAMED_OVERRIDE] that cannot be programmed manually). Some of this library macros are variadic macros, others are not (see below). -Variadic macros were officially added to the language since C++11. -However, C++ compilers have been supporting variadic macros as an extension for a long time, plus essentially all compilers that support C++11 lambda functions also support C++11 variadic macros (and this library might rarely be used without the convenience of C++11 lambda functions, see also __No_Lambda_Functions__). -Therefore this section can be considered more of a curiosity than anything else because programmers should seldom need to use this library without its macros. +Variadic macros were officially added to the language since C++11 but most compilers have been supporting variadic macros as an extension for a long time, plus essentially all compilers that support C++11 lambda functions also support C++11 variadic macros (and this library might rarely be used without the convenience of C++11 lambda functions, see also __No_Lambda_Functions__). +Therefore, the following can be considered mainly a curiosity because programmers should seldom need to use this library without using its macros. -[heading Overrides (Not Variadic)] +[heading Overrides] -As shown in __Overriding_Public_Functions__ and __Overloads_and_Named_Overrides__, this library provides the [macroref BOOST_CONTRACT_OVERRIDE] and [macroref BOOST_CONTRACT_NAMED_OVERRIDE] macros to program overriding public functions. -These macros cannot be programmed manually, but these are not variadic macros and programmers should never need not to use them. +As shown in __Public_Function_Overrides__ and __Named_Overrides__, this library provides the [macroref BOOST_CONTRACT_OVERRIDE] and [macroref BOOST_CONTRACT_NAMED_OVERRIDE] macros to program contracts for overriding public functions. +These macros cannot be programmed manually but they are not variadic macros so programmers should be able to use them on all C++ compilers. [footnote *Rationale.* These macros expand SFINAE-based introspection templates that cannot be reasonably programmed by users (that remains the case even if C++14 generic lambdas were to be used here). ] +The [macroref BOOST_CONTRACT_OVERRIDES] macro is a variadic macro instead but programmes can manually repeat the non-variadic macro [macroref BOOST_CONTRACT_OVERRIDE] for each overriding public function name on compilers that do not support variadic macros. [heading Assertions (Not Variadic)] As shown in __Preconditions__, __Postconditions__, __Class_Invariants__, etc. this library provides the [macroref BOOST_CONTRACT_ASSERT] macro to assert contract conditions. This is not a variadic macro and programmers should be able to use it on all C++ compilers. -However, this macro: +However, the macro invocation `BOOST_CONTRACT_ASSERT(`[^['condition]]`)` simply expands to code equivalent to the following: - BOOST_CONTRACT_ASSERT(``[^['boolean-condition]]``) - -Simply expands to code equivalent to the following: - - if(!``[^['boolean-condition]]``) { + if(!``[^['condition]]``) { throw boost::contract::assertion_failure(__FILE__, __LINE__, - BOOST_PP_STRINGIZE(``[^['boolean-condition]]``)); + BOOST_PP_STRINGIZE(``[^['condition]]``)); } -That is because this library considers any exception thrown from within preconditions, postconditions, and class invariants as a contract failure and reports it calling the related contract failure handler ([funcref boost::contract::precondition_failed], [funcref boost::contract::postcondition_failed], [funcref boost::contract::entry_invariant_failed] or [funcref boost::contract::exit_invariant_failed]). -In fact, if there is a need for it, programmers can always program contract assertions that throw user-defined exceptions without using [macroref BOOST_CONTRACT_ASSERT] (see also __Throw_on_Failure__): +That is because this library considers any exception thrown from within preconditions, postconditions, and class invariants as a contract failure and reports it calling the related contract failure handler ([funcref boost::contract::precondition_failure], etc., see also __Throw_on_Failure__). +In fact, if there is a need for it, programmers can always program contract assertions that throw an exception as follow (see [@../../example/features/throw_on_failure.cpp =throw_on_failure.cpp=] for an example): - if(!``[^['boolean-condition]]``) throw ``[^['user-defined-exception]]``(...); + if(!``[^['condition]]``) throw ``[^['exception-type]]``(...); + +However, using [macroref BOOST_CONTRACT_ASSERT] always allows this library to show detailed information about the assertion code, its file and line number, etc. [heading Base Types (Variadic)] -As shown in __Base_Types__, this library provides the [macroref BOOST_CONTRACT_BASE_TYPES] variadic macro to declare a `base_types` member type that lists the public bases of a derived class. -Programmers can also declare `base_types` without using [macroref BOOST_CONTRACT_BASE_TYPES] at the cost of writing a bit more code manually (see also [@../../example/features/no_macros.cpp =no_macros.cpp=]): +As shown in __Base_Classes__, this library provides the [macroref BOOST_CONTRACT_BASE_TYPES] variadic macro to declare the `base_types` member type that lists all public bases of a derived class. +Programmers can also declare `base_types` without using [macroref BOOST_CONTRACT_BASE_TYPES] at the cost of writing a bit more code manually, for example (see also [@../../example/features/base_types_no_macros.cpp =base_types_no_macros.cpp=]): -[no_base_types_macro] +[import ../example/features/base_types_no_macros.cpp] +[base_types_no_macros] -The `base_types` member type must be a `boost::mpl::vector` listing /only/ `public` base classes (because only public bases subcontract, see also __Function_Calls__). -[footnote -*Rationale.* -`boost::mpl::vector` is used because this library is design to work with other Boost libraries. -] +The `base_types` member type must be a `boost::mpl::vector` and it must list /only/ `public` base classes (because only public bases subcontract, see also __Function_Calls__). If the [macroref BOOST_CONTRACT_BASE_TYPES] macro is not used, it is the responsibility of the programmers to maintain the correct list of bases in the `boost::mpl::vector` each time the derived class inheritance list changes (this might complicate maintenance). In general, it is recommended to use the [macroref BOOST_CONTRACT_BASE_TYPES] macro if possible. [heading Old Values (Variadic)] -As shown in __Old_Values__ and __Virtual_Public_Functions__, this library provides the [macroref BOOST_CONTRACT_OLDOF] variadic macro to initialize old values. -Programmers can also initialize old values without using [macroref BOOST_CONTRACT_OLDOF] at the cost of writing a bit more code manually (see also [@../../example/features/no_macros.cpp =no_macros.cpp=]): +As shown in __Old_Values__, this library provides the [macroref BOOST_CONTRACT_OLDOF] variadic macro to assign old value copies. +Programmers can also assign old values without using [macroref BOOST_CONTRACT_OLDOF] at the cost of writing a bit more code manually, for example (see also [@../../example/features/old_no_macros.cpp =old_no_macros.cpp=]): -[no_oldof_macro] +[import ../example/features/old_no_macros.cpp] +[old_no_macros] -C++ ternary operator `... ? ... : ...` must be used here to avoid evaluating and copying the old value expression (`size()` in this example) when old values are not being copied (because postcondition checking is disabled, within overridden virtual function calls for subcontracting, etc.). -[classref boost::contract::old]`()` creates an empty old value (that introduces no copy). -[funcref boost::contract::copy_old]`()` returns `true` if and only if old values are being copied. -When used in public virtual functions, programmers must pass the extra [classref boost::contract::virtual_]`* v` argument to [funcref boost::contract::copy_old]`(v)`, for example (see also [@../../example/features/no_macros.cpp =no_macros.cpp=]): +The ternary operator `boost::contract::copy_old(v) ? size() : boost::contract::null_old()` must be used here to avoid evaluating and copying the old value expression `size()` when [funcref boost::contract::copy_old] returns `false` because old values are not being copied (postcondition checking is disable at run-time, an overridden virtual function call is not checking postconditions yet, etc.). +The enclosing [funcref boost::contract::make_old] copies the old value expression and creates an old value pointer, while [funcref boost::contract::null_old] indicates when a null old value pointer should be created. -[no_virtual_oldof_macro] +The [funcref boost::contract::make_old] and [funcref boost::contract::copy_old] functions are used exactly as above but without the extra `v` parameter when they are called from within non-virtual functions (see also __Public_Function_Overrides__). +The old value pointer returned by [funcref boost::contract::make_old] can be assigned to either [classref boost::contract::old_ptr] or [classref boost::contract::noncoyable_old_ptr] (see also __Old_Value_Requirements__). In general, it is recommended to use the [macroref BOOST_CONTRACT_OLDOF] macro if possible. @@ -356,19 +524,18 @@ In general, it is recommended to use the [macroref BOOST_CONTRACT_OLDOF] macro i [section No Lambda Functions (No C++11)] -This section illustrates how to use this library without C++11 lambda functions. -This presets some advantages: +This section shows how to use this library without C++11 lambda functions. +This has some advantages: -* It allows to use this library on compilers that do not support C++11 lambda functions (essentially C++03 compilers can be used, see __No_Macros__ to also avoid using variadic macros). -* Functors can be programmed to fully enforce constant-correctness and other contract requirements at compile-time (see also __Constant_Correctness__). +* It allows to use this library on compilers that do not support C++11 lambda functions (essentially most C++03 compilers can be used, see __No_Macros__ to also avoid using variadic macros). +* Contract functions (see the `..._precondition`, `..._old`, and `..._postcondition` functions in the example below) can be programmed to fully enforce constant-correctness and other contract requirements at compile-time (see also __Constant_Correctness__). [footnote -If C++ supported to capture lambda function variables by constant references `[const&] () { ... }` that could be used to program contract functors using lambda functions that also enforce __Constant_Correctness__ at compile-time (not that value captures cannot be used to program postconditions, plus it introduces an extra copy that might not always be possible or desirable). +If C++ allowed lambda functions to capture variables by constant reference (e.g., `[const&] (...) { ... }` or `[const& `[^['variable-name]]`] (...) { ... }`) also lambdas could be used to program contract functors that fully enforce __Constant_Correctness__ at compile-time. +Note that C++11 lambda allows to capture variables by value (`[=] (...) { ... }` and `[`[^['variable-name]]`] (...) { ... }`), these value captures are `const` (unless the lambda is explicitly declared `mutable`) but they are not suitable to program postconditions using this library (see __Postconditions__), plus they introduce an extra copy that might be too expensive in general. ] -* Contracts are automatically separated from function body implementations (see also __Specification_and_Implementation__ and __Separate_Body_Implementation__). +* Contracts are separated from function body implementations (see also __Specification_and_Implementation__ and __Separate_Body_Implementation__). -However, not using C++11 lambda functions comes to the significant cost of having to manually write a great deal of extra code to program the contract functors (therefore, the authors think this library is most useful when C++11 lambda functions can be used). - -For example (see also [@../../example/features/no_lambdas.hpp =no_lambdas.hpp=] and [@../../example/features/no_lambdas.cpp =no_lambdas.cpp=]): +However, not using C++11 lambda functions comes to the significant cost of having to manually write a great deal of extra code to program the contract functions, for example (see also [@../../example/features/no_lambdas.hpp =no_lambdas.hpp=] and [@../../example/features/no_lambdas.cpp =no_lambdas.cpp=]): [import ../example/features/no_lambdas.hpp] [no_lambdas_hpp] @@ -378,20 +545,29 @@ For example (see also [@../../example/features/no_lambdas.hpp =no_lambdas.hpp=] If programmers also want to fully enforce all contract programming constant-correctness requirements at compile-time, they should follow these rules when programming the contract functions (see also __Constant_Correctness__): -* In general, precondition and postcondition functions (e.g., named `..._precondition` and `..._postcondition`) should take their arguments by `const&` and they should be either `static` or `const` member functions. -However, postconditions can (but do not have to) take old value arguments by value (because these are pointers to `const` objects already). -* In general, old value functions (e.g., named `..._old`) should take their arguments by `const&` a part from old value pointers that should be taken by `&` (so only old value pointers can be modified), and they should be either `static` or `const` member functions. -* Constructor precondition and old value functions should be `static` (because constructor preconditions and old-values cannot access the object `this`, see also __Constructor_Calls__). +* Precondition functions (i.e., the `..._precondition` functions in the example above) can take their arguments either by `const` value or by `const&`, and they should be either `static` or `const` member functions. +* Postcondition functions (i.e., the `..._postcondition` functions in the example above) should take their arguments by `const&`, and they should be either `static` or `const` member functions. +* Old value functions (i.e., the `..._old` functions in the example above) should take their arguments by `const&` a part from old value pointers that should be taken by `&` (so only old value pointers can be modified), and they should be either `static` or `const` member functions. +* Constructor precondition and old value functions should be `static` (because constructor preconditions and old values cannot access the object `this`, see also __Constructor_Calls__). * Destructor postcondition functions should be `static` (because destructor postconditions cannot access the object `this`, see also __Destructor_Calls__). -Note that the extra contract functions also allowed to program only the contract code in the header file (see also __Specification_and_Implementation__). -All function body implementation code was instead programmed in the source file (including the constructor member initialization list, that could not be done with the technique illustrated by __Separate_Body_Implementation__). +Note that the extra contract functions also allow to program only the contract code in the header file (see also __Specification_and_Implementation__). +All function body implementation code was instead programmed in the source file (including the constructor member initialization list, that could not be done with the technique shown in __Separate_Body_Implementation__). [footnote In this example, `bind` was used to generate nullary functors from the contract functions. As always with `bind`, `cref` and `ref` must be used to bind arguments by `const&` and `&` respectively, plus it might be necessary to explicitly `static_cast` the function pointer passed to `bind` in case the bound function name is overloaded. ] +Also note that the contract functions can always be declared `private` if programmers need to exactly control the public members of the class (this was not done in this example only for brevity, see also __Access__). -Finally, note that the extra contract functions can always be declared `private` if programmers need to exactly control the public members of the class (this was not done in this example only for brevity, see also __Access__). +Alternatively, on compilers that do not support C++11 lambda functions but that support type-of (either native as an extension or via emulation, these should be most recent C++03 compilers), [@http://www.boost.org/doc/libs/release/libs/local_function/doc/html/index.html Boost.LocalFunction] can be used to program the contract functions, for example (see also [@../../example/features/no_lambdas_local_func.cpp =no_lambda_local_func.cpp=]): + +[import ../example/features/no_lambdas_local_func.cpp] +[no_lambdas_local_func] + +This code is somewhat less verbose than the previous example (about 30% less lines of code) but the contract code is hard to ready. + +Other libraries could also be used to program the contract functions without C++11 lambda functions (Boost.Lambda, Boost.Fusion, etc.) but like the techniques shown above, they will all result in contract code more verbose, or harder to read and maintain than the contract code programmed using C++11 lambda functions. +Therefore, authors think this library is most useful when used together with C++11 lambda functions. [endsect] diff --git a/doc/contract.qbk b/doc/contract.qbk index 329d8ed..ace1c9d 100644 --- a/doc/contract.qbk +++ b/doc/contract.qbk @@ -17,6 +17,7 @@ [def __Getting_Started__ [link boost_contract.getting_started Getting Started]] [def __Contract_Programming_Overview__ [link boost_contract.contract_programming_overview Contract Programming Overview]] +[def __Assertions__ [link boost_contract.contract_programming_overview.assertions Assertions]] [def __Benefits_and_Costs__ [link boost_contract.contract_programming_overview.benefits_and_costs Benefits and Costs]] [def __Function_Calls__ [link boost_contract.contract_programming_overview.function_calls Function Calls]] [def __Public_Function_Calls__ [link boost_contract.contract_programming_overview.public_function_calls Public Function Calls]] @@ -36,20 +37,20 @@ [def __Constructors__ [link boost_contract.tutorial.constructors Constructors]] [def __Destructors__ [link boost_contract.tutorial.destructors Destructors]] [def __Public_Functions__ [link boost_contract.tutorial.public_functions Public Functions]] -[def __Public_Virtual_Functions__ [link boost_contract.tutorial.public_virtual_functions Public Virtual Functions]] +[def __Virtual_Public_Functions__ [link boost_contract.tutorial.virtual_public_functions Virtual Public Functions]] +[def __Public_Function_Overrides__ [link boost_contract.tutorial.public_function_overrides__subcontracting_ Public Function Overrides]] [def __Base_Classes__ [link boost_contract.tutorial.base_classes__subcontracting_ Base Classes]] -[def __Overriding_Public_Functions__ [link boost_contract.tutorial.overriding_public_functions__subcontracting_ Overriding Public Functions]] [def __Private_and_Protected_Functions__ [link boost_contract.tutorial.private_and_protected_functions Private and Protected Functions]] [def __Advanced_Topics__ [link boost_contract.advanced_topics Advanced Topics]] +[def __Old_Values_at_Body__ [link boost_contract.advanced_topics.old_values_at_body Old Values at Body]] [def __Optional_Return_Value__ [link boost_contract.advanced_topics.optional_return_value Optional Return Value]] [def __Pure_Virtual_Public_Functions__ [link boost_contract.advanced_topics.pure_virtual_public_functions Pure Virtual Public Functions]] -[def __Old_Values_at_Body__ [link boost_contract.advanced_topics.old_values_at_body Old Values at Body]] -[def __Static_Public_Functions__ [link boost_contract.advanced_topics.static_public_functions Static Public Functions]] +[def __Named_Overrides__ [link boost_contract.advanced_topics.overloads_and_named_overrides Named Overrides]] [def __Volatile_Public_Functions__ [link boost_contract.advanced_topics.volatile_public_functions Volatile Public Functions]] -[def __Overloads_and_Named_Overrides__ [link boost_contract.advanced_topics.overloads_and_named_overrides Overloads and Named Overrides]] -[def __Separate_Body_Implementation__ [link boost_contract.advanced_topics.separate_body_implementation Separate Body Implementation]] +[def __Old_Value_Requirements__ [link boost_contract.advanced_topics.old_value_requirements Old Value Requirements]] [def __Access__ [link boost_contract.advanced_topics.access Access]] +[def __Separate_Body_Implementation__ [link boost_contract.advanced_topics.separate_body_implementation Separate Body Implementation]] [def __Throw_on_Failure__ [link boost_contract.advanced_topics.throw_on_failure Throw on Failure]] [def __Disable_Contract_Checking__ [link boost_contract.advanced_topics.disable_contract_checking Disable Contract Checking]] [def __No_Macros__ [link boost_contract.advanced_topics.no_macros__no_c__11_ No Macros]] diff --git a/doc/tutorial.qbk b/doc/tutorial.qbk index 0da76e3..1007735 100644 --- a/doc/tutorial.qbk +++ b/doc/tutorial.qbk @@ -122,6 +122,9 @@ It is also recommended to use the [macroref BOOST_CONTRACT_ASSERT] macro to prog This library will automatically call [funcref boost::contract::postcondition_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] macro conditions are `false` and also if the postcondition functor 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.). +For non-void public functions, the functor passed to `.postcondition(...)` is not a nullary functor. +Instead, it is a unary functor taking a variable holding the return value as its one parameter (see also __Public_Functions__). + [endsect] [section Return Value] @@ -393,7 +396,7 @@ 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 nullary functor ['[^g]]`()` passed to `.postcondition(`['[^g]]`)`. + # Check postconditions, by calling the functor [^['g]]`()` passed to `.postcondition(`['[^g]]`)` (or [^['g]]`(`['result]`)` for non-void public functions). This ensures that public member function contracts are correctly checked at run-time (see also __Public_Function_Calls__). @@ -472,6 +475,16 @@ This library will generate a compile-time error if there is no suitable virtual This error is similar in principle to the error generated by C++11 `override` specifier, but it is limited to functions with the extra [classref boost::contract::virtual_]`*` parameter and searched recursively only in `public` base classes passed to [macroref BOOST_CONTRACT_BASE_TYPES] because only those are valid functions to override for subcontracting. ] +For convenience [macroref BOOST_CONTRACT_OVERRIDES] can be used with multiple functions names instead of repeating [macroref BOOST_CONTRACT_OVERRIDE] for each function name (on compilers that support variadic macros): + + BOOST_CONTRACT_OVERRIDES(``[^['function-name1]]``, ``[^['function-name2]]``, ...) + +Which is equivalent to: + + BOOST_CONTRACT_OVERRIDE(``[^['function-name1]]``) + BOOST_CONTRACT_OVERRIDE(``[^['function-name2]]``) + ... + Overriding public functions must always list the extra trailing parameter of type [classref boost::contract::virtual_]`*` with `0` default value (i.e., `nullptr`), even when they are not declared `virtual` (because this parameter is present in the signature of the virtual function being overridden from base classes). Programmers must pass the extra virtual parameter as the very first argument to all [macroref BOOST_CONTRACT_OLDOF] and [funcref boost::contract::public_function] calls in the overriding function. @@ -507,7 +520,7 @@ 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 the nullary functors [^['g1]]`()` __AND__... [^['gn]]`()` passed to `.postcondition(`[^['g1]]`)`, ..., `.postcondition(`[^['gn]]`)` for all of the overridden and overriding functions respectively. + # Check postconditions for all overridden base functions and for the overriding derived function in __AND__ with each other, by calling the functors [^['g1]]`()` __AND__... [^['gn]]`()` passed to `.postcondition(`[^['g1]]`)`, ..., `.postcondition(`[^['gn]]`)` for all of the overridden and overriding functions respectively (or [^['g1]]`(`[^['result]]`)` __AND__... [^['gn]]`(`[^['result]]`)` for non-void public function overrides). This ensures that overriding public function subcontracts are checked correctly at run-time (see also __Public_Function_Calls__). diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 722a3fb..25c6ae6 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -6,14 +6,27 @@ test-suite features : [ subdir-run features : function_comments ] [ subdir-run features : function ] - [ subdir-run features : base_types ] [ subdir-run features : public ] + [ subdir-run features : base_types ] [ subdir-run features : static_public ] [ subdir-run features : private_protected ] + [ subdir-run features : old ] [ subdir-run features : optional_result ] + [ subdir-run features : pure_virtual_public ] + [ subdir-run features : overload ] + [ subdir-run features : named_override ] + [ subdir-run features : volatile ] + [ subdir-run features : noncopyable_old ] + [ subdir-run features : check_if ] + [ subdir-run features : access ] + [ subdir-run features : separate_body ] [ subdir-run features : throw_on_failure ] [ subdir-run-with-no features : ifdef ] + [ subdir-run features : base_types_no_macros ] + [ subdir-run features : old_no_macros ] + [ subdir-run features : no_lambdas ] + [ subdir-run features : no_lambdas_local_func ] ; test-suite meyer97 : diff --git a/example/features/access.cpp b/example/features/access.cpp index 7552395..dc98cd3 100644 --- a/example/features/access.cpp +++ b/example/features/access.cpp @@ -1,26 +1,31 @@ #include #include -#include +#include template class pushable { -public: +private: + friend class boost::contract::access; + void invariant() const { BOOST_CONTRACT_ASSERT(capacity() <= max_size()); } +public: virtual void push_back(T const& value, boost::contract::virtual_* v = 0) = 0; - virtual std::size_t max_size() const = 0; - virtual std::size_t capacity() const = 0; +protected: + virtual unsigned capacity() const = 0; + virtual unsigned max_size() const = 0; }; -template // Contract for pure virtual function. +template void pushable::push_back(T const& value, boost::contract::virtual_* v) { - auto old_capacity = BOOST_CONTRACT_OLDOF(v, capacity()); - auto c = boost::contract::public_function(v, this) + boost::contract::old_ptr old_capacity = + BOOST_CONTRACT_OLDOF(v, capacity()); + boost::contract::guard c = boost::contract::public_function(v, this) .precondition([&] { BOOST_CONTRACT_ASSERT(capacity() < max_size()); }) @@ -28,31 +33,34 @@ void pushable::push_back(T const& value, boost::contract::virtual_* v) { BOOST_CONTRACT_ASSERT(capacity() >= *old_capacity); }) ; + assert(false); } - //[access template class vector #define BASES public pushable : BASES { - // In private section: +private: friend class boost::contract::access; // Friend `access` class so... typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; // ...private bases. #undef BASES + + BOOST_CONTRACT_OVERRIDE(push_back) // ..private overrides. - void invariant() const { // ...private invariants (static and non). + void invariant() const { // ...private invariants. BOOST_CONTRACT_ASSERT(size() <= capacity()); } - + public: - virtual void push_back(T const& value, boost::contract::virtual_* v = 0) + void push_back(T const& value, boost::contract::virtual_* v = 0) /* override */ { - auto old_size = BOOST_CONTRACT_OLDOF(v, size()); - auto c = boost::contract::public_function( - v, &vector::push_back, this, value) + boost::contract::old_ptr old_size = + BOOST_CONTRACT_OLDOF(v, size()); + boost::contract::guard c = boost::contract::public_function< + override_push_back>(v, &vector::push_back, this, value) .precondition([&] { BOOST_CONTRACT_ASSERT(size() < max_size()); }) @@ -64,18 +72,21 @@ public: vect_.push_back(value); } -private: - BOOST_CONTRACT_OVERRIDE(push_back) // Allowed even without friend `access`. - std::vector vect_; - /* ... */ //] -public: - // TODO: Write contracts for those functions too. - std::size_t size() const { return vect_.size(); } - std::size_t max_size() const { return vect_.max_size(); } - std::size_t capacity() const { return vect_.capacity(); } -}; -//] + unsigned size() const { return vect_.size(); } + unsigned max_size() const { return vect_.max_size(); } + unsigned capacity() const { return vect_.capacity(); } + +private: + std::vector vect_; +}; + +int main() { + vector vect; + vect.push_back(123); + assert(vect.size() == 1); + return 0; +} diff --git a/example/features/base_types.cpp b/example/features/base_types.cpp index deb19cb..a6fedf8 100644 --- a/example/features/base_types.cpp +++ b/example/features/base_types.cpp @@ -109,8 +109,10 @@ private: class chars #define BASES /* local macro (for convenience) */ \ private boost::contract::constructor_precondition, \ - public unique_chars, public virtual pushable, \ - virtual protected has_size, private has_empty + public unique_chars, \ + public virtual pushable, \ + virtual protected has_size, \ + private has_empty : BASES // Bases of this class. { public: @@ -170,11 +172,10 @@ protected: }; int main() { - chars s("aba"); + chars s("abc"); assert(s.find('a')); assert(s.find('b')); - assert(!s.find('c')); - + assert(!s.find('x')); return 0; } diff --git a/example/features/base_types_no_macros.cpp b/example/features/base_types_no_macros.cpp new file mode 100644 index 0000000..716e65a --- /dev/null +++ b/example/features/base_types_no_macros.cpp @@ -0,0 +1,181 @@ + +#include +#include + +template +class pushable { +public: + void invariant() const { + BOOST_CONTRACT_ASSERT(capacity() <= max_size()); + } + + virtual void push_back(T x, boost::contract::virtual_* v = 0) = 0; + +protected: + virtual int capacity() const = 0; + virtual int max_size() const = 0; +}; + +template +void pushable::push_back(T x, boost::contract::virtual_* v) { + boost::contract::old_ptr old_capacity = + BOOST_CONTRACT_OLDOF(v, capacity()); + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(capacity() < max_size()); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(capacity() >= *old_capacity); + }) + ; + assert(false); // Shall never execute this body. +} + +struct has_size { virtual int size() const = 0; }; +struct has_empty { virtual bool empty() const = 0; }; + +class unique_chars + : private boost::contract::constructor_precondition +{ +public: + void invariant() const { + BOOST_CONTRACT_ASSERT(size() >= 0); + } + + unique_chars(char from, char to) : + boost::contract::constructor_precondition([&] { + BOOST_CONTRACT_ASSERT(from <= to); + }) + { + boost::contract::guard c = boost::contract::constructor(this) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(size() == (to - from + 1)); + }) + ; + + for(char x = from; x <= to; ++x) vect_.push_back(x); + } + + virtual ~unique_chars() { + boost::contract::guard c = boost::contract::destructor(this); + } + + int size() const { + boost::contract::guard c = boost::contract::public_function(this); + return vect_.size(); + } + + bool find(char x) const { + bool result; + boost::contract::guard c = boost::contract::public_function(this) + .postcondition([&] { + if(size() == 0) BOOST_CONTRACT_ASSERT(!result); + }) + ; + + return result = std::find(vect_.begin(), vect_.end(), x) != vect_.end(); + } + + virtual void push_back(char x, boost::contract::virtual_* v = 0) { + boost::contract::old_ptr old_find = + BOOST_CONTRACT_OLDOF(v, find(x)); + boost::contract::old_ptr old_size = + BOOST_CONTRACT_OLDOF(v, size()); + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(!find(x)); + }) + .postcondition([&] { + if(!*old_find) { + BOOST_CONTRACT_ASSERT(find(x)); + BOOST_CONTRACT_ASSERT(size() == *old_size + 1); + } + }) + ; + + vect_.push_back(x); + } + +protected: + unique_chars() {} + + std::vector const& vect() const { return vect_; } + +private: + std::vector vect_; +}; + +//[base_types_no_macros +#include + +class chars : + private boost::contract::constructor_precondition, + public unique_chars, + public virtual pushable, + virtual protected has_size, + private has_empty +{ +public: + // Program `base_types` without macros (list only public bases). + typedef boost::mpl::vector > base_types; + + /* ... */ +//] + + void invariant() const { + BOOST_CONTRACT_ASSERT(empty() == (size() == 0)); + } + + chars(char from, char to) : unique_chars(from, to) { + boost::contract::guard c = boost::contract::constructor(this); + } + + chars(char const* const c_str) : + boost::contract::constructor_precondition([&] { + BOOST_CONTRACT_ASSERT(c_str[0] != '\0'); + }) + { + boost::contract::guard c = boost::contract::constructor(this); + + for(int i = 0; c_str[i] != '\0'; ++i) push_back(c_str[i]); + } + + void push_back(char x, boost::contract::virtual_* v = 0) /* override */ { + boost::contract::old_ptr old_find = + BOOST_CONTRACT_OLDOF(v, find(x)); + boost::contract::old_ptr old_size = + BOOST_CONTRACT_OLDOF(v, size()); + boost::contract::guard c = boost::contract::public_function< + override_push_back>(v, &chars::push_back, this, x) + .precondition([&] { + BOOST_CONTRACT_ASSERT(find(x)); + }) + .postcondition([&] { + if(*old_find) BOOST_CONTRACT_ASSERT(size() == *old_size); + }) + ; + + if(!find(x)) unique_chars::push_back(x); + } + BOOST_CONTRACT_OVERRIDE(push_back); + + bool empty() const { + boost::contract::guard c = boost::contract::public_function(this); + return size() == 0; + } + + int size() const { return unique_chars::size(); } + +protected: + int max_size() const { return vect().max_size(); } + int capacity() const { return vect().capacity(); } +}; + +int main() { + chars s("abc"); + assert(s.find('a')); + assert(s.find('b')); + assert(!s.find('x')); + return 0; +} + diff --git a/example/features/call_if.cpp b/example/features/call_if.cpp deleted file mode 100644 index 61b5121..0000000 --- a/example/features/call_if.cpp +++ /dev/null @@ -1,38 +0,0 @@ - -//[call_if -template -class vector { -public: - void push_back(T const& value) { - auto old_size = BOOST_CONTRACT_OLDOF(size()); - auto c = boost::contract::public_function(this) - .precondition([&] { - BOOST_CONTRACT_ASSERT(this->size() < max_size()); - }) - .postcondition([&] { - // Check this only if T has operator== (otherwise always true). - BOOST_CONTRACT_ASSERT( - boost::contract::call_if >( - boost::bind(std::equal_to(), boost::cref(back()), - boost::cref(value)) - ).else_([] { return true; }) - ); - - BOOST_CONTRACT_ASSERT(this->size() == *old_size + 1); - }) - ; - - vect_.push_back(value); - } - - /* ... */ -//] - - void invariant() const { - BOOST_CONTRACT_ASSERT(size() <= max_size()); - } - -private: - std::vector vect_; -}; - diff --git a/example/features/check_if.cpp b/example/features/check_if.cpp new file mode 100644 index 0000000..54b43cd --- /dev/null +++ b/example/features/check_if.cpp @@ -0,0 +1,52 @@ + +#include +#include +#include +#include +#include + +//[check_if +template +class vector { +public: + void push_back(T const& value) { + boost::contract::guard c = boost::contract::public_function(this) + .postcondition([&] { + // Instead of `ASSERT(back() == value)` to handle T no `==`. + BOOST_CONTRACT_ASSERT( + boost::contract::check_if >( + boost::bind(std::equal_to(), + boost::cref(back()), + boost::cref(value) + ) + ) + ); + }) + ; + + vect_.push_back(value); + } + + /* ... */ +//] + + T const& back() const { return vect_.back(); } + +private: + std::vector vect_; +}; + +int main() { + vector v; + v.push_back(1); // Type `int` has `==` so check postcondition. + assert(v.back() == 1); + + struct i { int value; } j; + j.value = 10; + vector w; + w.push_back(j); // Type `i` has no `==` so skip postcondition. + assert(j.value == 10); + + return 0; +} + diff --git a/example/features/named_override.cpp b/example/features/named_override.cpp index 5e9a18b..360a30b 100644 --- a/example/features/named_override.cpp +++ b/example/features/named_override.cpp @@ -1,21 +1,100 @@ +#include +#include +#include + +//[named_override_pure_virtual_assert_false +template +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 +void generic_unary_pack::_1(T const& value, boost::contract::virtual_* v) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + // Derived concrete classes will enforce preconditions. + BOOST_CONTRACT_ASSERT(false); + }) + ; + assert(false); +} + +/* ... */ +//] + +template +T generic_unary_pack::_1(boost::contract::virtual_* v) const { + boost::optional result; // Do not assume T has default constructor. + boost::contract::guard c = boost::contract::public_function(v, result, this) + .postcondition([&] (boost::optional const& result) { + BOOST_CONTRACT_ASSERT(*result == _1()); + }) + ; + + assert(false); + return *result; +} + //[named_override -class a - #define BASES public b +template +class positive_unary_pack + #define BASES public generic_unary_pack : BASES { public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; #undef BASES + + // BOOST_CONTRACT_OVERRIDE(_1) would generate reserved symbol `override__1`. + BOOST_CONTRACT_NAMED_OVERRIDE(override1, _1) - void _f(boost::contract::virtual_* v = 0) /* override */ { - auto c = boost::contract::public_function( - v, &a::_f, this) - /* ... */ + virtual void _1(T const& value, boost::contract::virtual_* v = 0) + /* override */ { + // Use `override1` type generated by NAMED_OVERRIDE macro above. + boost::contract::guard c = boost::contract::public_function( + v, + static_cast(&positive_unary_pack::_1), + this, + value + ) + .precondition([&] { + BOOST_CONTRACT_ASSERT(value > 0); + }) ; + value1_ = value; } - BOOST_CONTRACT_NAMED_OVERRIDE(override_underscore_f, _f) // Explicit name. -}; + + /* ... */ //] + virtual T _1(boost::contract::virtual_* v = 0) const /* override */ { + T result; // Class default constructor already used T's default ctor. + boost::contract::guard c = boost::contract::public_function( + v, + result, + static_cast(&positive_unary_pack::_1), + this + ) + .postcondition([&] (T const& result) { + BOOST_CONTRACT_ASSERT(result > 0); + }) + ; + return result = value1_; + } + +private: + T value1_; +}; + +int main() { + positive_unary_pack u; + u._1(123); + assert(u._1() == 123); + return 0; +} diff --git a/example/features/no_lambdas.cpp b/example/features/no_lambdas.cpp index 3c064cd..1f7a839 100644 --- a/example/features/no_lambdas.cpp +++ b/example/features/no_lambdas.cpp @@ -1,64 +1,79 @@ -#include "separate_body.hpp" +#include "no_lambdas.hpp" #include +#include //[no_lambdas_cpp -template -int array::instances_ = 0; - -template -array::array(std::size_t count) : - boost::contract::constructor_precondition( - &array::constructor_precondition), - values_(new T[MaxSize]) // Member initialization can be here. +template +array::array(unsigned count) : + boost::contract::constructor_precondition(boost::bind( + &array::constructor_precondition, count)), + values_(new T[MaxSize]) // Member initializations can be here. { - boost::shared_ptr old_instances; - auto c = boost::contract::constructor(this) - .old(boost::bind(&array::constructor_old, boost::ref(old_instances)) - .postcondition(boost::bind(&array::constructor_postcondition, this)) + boost::contract::old_ptr old_instances; + boost::contract::guard c = boost::contract::constructor(this) + .old(boost::bind(&array::constructor_old, boost::ref(old_instances))) + .postcondition(boost::bind(&array::constructor_postcondition, this, + boost::cref(count), boost::cref(old_instances))) ; - for(std::size_t i = 0; i < count; ++i) values_[i] = T(); + for(unsigned i = 0; i < count; ++i) values_[i] = T(); size_ = count; ++instances_; } -template +template array::~array() { - boost::shared_ptr old_instances; - auto c = boost::contract::destructor(this) - .old(boost::bind(&array::destructor_old, boost::ref(old_instances)) - .postcondition(&array::destructor_postcondition) + boost::contract::old_ptr old_instances; + boost::contract::guard c = boost::contract::destructor(this) + .old(boost::bind(&array::destructor_old, this, + boost::ref(old_instances))) + .postcondition(boost::bind(&array::destructor_postcondition, + boost::cref(old_instances))) ; delete[] values_; --instances_; } -template -void array::push_back_body(T const& value) { - boost::shared_ptr old_size; - auto c = boost::contract::public_function(v, this) - .precondition(boost::bind(&array::push_back_precondition, - this, boost::cref(value))) - .old(boost::bind(&array::push_back_old, this, boost::ref(old_size)) - .postcondition(boost::bind(&array::push_back_postcondition, - this, boost::cref(value), old_size)) +template +void array::push_back(T const& value, + boost::contract::virtual_* v) { + boost::contract::old_ptr old_size; + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition(boost::bind(&array::push_back_precondition, this)) + .old(boost::bind(&array::push_back_old, this, boost::cref(v), + boost::ref(old_size))) + .postcondition(boost::bind(&array::push_back_postcondition, this, + boost::cref(old_size))) ; - values_[size_++] == value; + values_[size_++] = value; } -template -std::size_t array::size_body() const { - auto c = boost::contract::public_function(this); // Check invariants. +template +unsigned array::size() const { + // Check invariants. + boost::contract::guard c = boost::contract::public_function(this); return size_; } -template -int array::instance() { - auto c = boost::contract::public_function(); // Check invariants. +template +int array::instances() { + // Check static invariants. + boost::contract::guard c = boost::contract::public_function(); return instances_; } + +template +int array::instances_ = 0; //] +int main() { + array a(2); + assert(a.size() == 2); + a.push_back('x'); + assert(a.size() == 3); + return 0; +} + diff --git a/example/features/no_lambdas.hpp b/example/features/no_lambdas.hpp index eb1c777..2c37a30 100644 --- a/example/features/no_lambdas.hpp +++ b/example/features/no_lambdas.hpp @@ -1,13 +1,15 @@ +#ifndef NO_LAMBDAS_HPP_ +#define NO_LAMBDAS_HPP_ + #include //[no_lambdas_hpp -template -class array - : private boost::contract::constructor_precondition > -{ +template +class array : + private boost::contract::constructor_precondition > { public: - static void static_invariant() const { + static void static_invariant() { BOOST_CONTRACT_ASSERT(instances() >= 0); } @@ -15,51 +17,53 @@ public: BOOST_CONTRACT_ASSERT(size() <= MaxSize); } - explicit array(std::size_t count); - static void constructor_precondition(std::size_t const& count) { - BOOST_CONTRACT_ASSERT(count <= MaxSize) + explicit array(unsigned count); + static void constructor_precondition(unsigned const count) { + BOOST_CONTRACT_ASSERT(count <= MaxSize); } - static void constructor_old( - boost::shared_ptr& old_instances) { + static void constructor_old(boost::contract::old_ptr& + old_instances) { old_instances = BOOST_CONTRACT_OLDOF(instances()); } - void constructor_postcondition(std::size_t const& count, - boost::shared_ptr old_instances) const { + void constructor_postcondition(unsigned const count, + boost::contract::old_ptr const old_instances) const { BOOST_CONTRACT_ASSERT(size() == count); BOOST_CONTRACT_ASSERT(instances() == *old_instances + 1); } virtual ~array(); - void destructor_old( - boost::shared_ptr& old_instances) const { + void destructor_old(boost::contract::old_ptr& old_instances) + const { old_instances = BOOST_CONTRACT_OLDOF(instances()); } - static void destructor_postcondition( - boost::shared_ptr old_instances) { + static void destructor_postcondition(boost::contract::old_ptr const + old_instances) { BOOST_CONTRACT_ASSERT(instances() == *old_instances - 1); } - void push_back(T const& value); - void push_back_precondition(T const& value) const { + virtual void push_back(T const& value, boost::contract::virtual_* v = 0); + void push_back_precondition() const { BOOST_CONTRACT_ASSERT(size() < MaxSize); } - void push_back_old(boost::shared_ptr& old_size) - const { - old_size = BOOST_CONTRACT_OLDOF(size()); + void push_back_old(boost::contract::virtual_* v, + boost::contract::old_ptr& old_size) const { + old_size = BOOST_CONTRACT_OLDOF(v, size()); } - void push_back_postcondition(T const& value, - boost::shared_ptr old_size) const { + void push_back_postcondition( + boost::contract::old_ptr const old_size) const { BOOST_CONTRACT_ASSERT(size() == *old_size + 1); } - std::size_t size() const; // Check invariants. + unsigned size() const; - static int instances(); // Check invariants. + static int instances(); private: T* values_; - std::size_t size_; + unsigned size_; static int instances_; }; //] +#endif // #include guard + diff --git a/example/features/no_lambdas_local_func.cpp b/example/features/no_lambdas_local_func.cpp new file mode 100644 index 0000000..ebd1e4c --- /dev/null +++ b/example/features/no_lambdas_local_func.cpp @@ -0,0 +1,108 @@ + +#include +#include +#include + +//[no_lambdas_local_func +#include + +template +class array : + private boost::contract::constructor_precondition > { +public: + static void static_invariant() { + BOOST_CONTRACT_ASSERT(instances() >= 0); + } + + void invariant() const { + BOOST_CONTRACT_ASSERT(size() <= MaxSize); + } + + static void constructor_pre(unsigned const count) { + BOOST_CONTRACT_ASSERT(count <= MaxSize); + } + explicit array(unsigned count) : + boost::contract::constructor_precondition(boost::bind( + &array::constructor_pre, count)), + values_(new T[MaxSize]) + { + boost::contract::old_ptr old_instances; + void BOOST_LOCAL_FUNCTION_TPL(bind& old_instances) { + old_instances = BOOST_CONTRACT_OLDOF(array::instances()); + } BOOST_LOCAL_FUNCTION_NAME_TPL(old) + void BOOST_LOCAL_FUNCTION_TPL(const bind this_, const bind& count, + const bind& old_instances) { + BOOST_CONTRACT_ASSERT(this_->size() == count); + BOOST_CONTRACT_ASSERT(this_->instances() == *old_instances + 1); + } BOOST_LOCAL_FUNCTION_NAME_TPL(post) + boost::contract::guard c = boost::contract::constructor(this) + .old(old).postcondition(post); + + for(unsigned i = 0; i < count; ++i) values_[i] = T(); + size_ = count; + ++instances_; + } + + virtual ~array() { + boost::contract::old_ptr old_instances; + void BOOST_LOCAL_FUNCTION_TPL(const bind this_, bind& old_instances) { + old_instances = BOOST_CONTRACT_OLDOF(this_->instances()); + } BOOST_LOCAL_FUNCTION_NAME_TPL(old) + void BOOST_LOCAL_FUNCTION_TPL(const bind& old_instances) { + BOOST_CONTRACT_ASSERT(array::instances() == *old_instances - 1); + } BOOST_LOCAL_FUNCTION_NAME_TPL(post) + boost::contract::guard c = boost::contract::destructor(this) + .old(old).postcondition(post); + + delete[] values_; + --instances_; + } + + virtual void push_back(T const& value, boost::contract::virtual_* v = 0) { + boost::contract::old_ptr old_size; + void BOOST_LOCAL_FUNCTION_TPL(const bind this_) { + BOOST_CONTRACT_ASSERT(this_->size() < MaxSize); + } BOOST_LOCAL_FUNCTION_NAME_TPL(pre) + void BOOST_LOCAL_FUNCTION_TPL(const bind v, const bind this_, + bind& old_size) { + old_size = BOOST_CONTRACT_OLDOF(v, this_->size()); + } BOOST_LOCAL_FUNCTION_NAME_TPL(old) + void BOOST_LOCAL_FUNCTION_TPL(const bind this_, const bind& old_size) { + BOOST_CONTRACT_ASSERT(this_->size() == *old_size + 1); + } BOOST_LOCAL_FUNCTION_NAME_TPL(post) + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition(pre).old(old).postcondition(post); + + values_[size_++] = value; + } + + unsigned size() const { + // Check invariants. + boost::contract::guard c = boost::contract::public_function(this); + return size_; + } + + static int instances() { + // Check static invariants. + boost::contract::guard c = boost::contract::public_function(); + return instances_; + } + +private: + T* values_; + unsigned size_; + static int instances_; +}; + +template +int array::instances_ = 0; +//] + +int main() { + array a(2); + assert(a.size() == 2); + a.push_back('x'); + assert(a.size() == 3); + return 0; +} + diff --git a/example/features/no_macros.cpp b/example/features/no_macros.cpp deleted file mode 100644 index 9273856..0000000 --- a/example/features/no_macros.cpp +++ /dev/null @@ -1,81 +0,0 @@ - -//[no_oldof_macro -template -class vector { -public: - void push_back(T const& value, boost::contract::virtual_* v = 0) - // Program old-value without macros (pass extra `v` if virtual). - boost::contract::old_ptr old_size = boost::contract::make_old( - boost::contract::copy_old() ? size() : boost::contract::null_old() - ); - - boost::contract::guard c = boost::contract::public_function(this) - .postcondition([&] { - BOOST_CONTRACT_ASSERT(size() == *old_size + 1); - }) - ; - - vect_.push_back(id); // Function body. - } - - /* ... */ - - //] - - void invariant() const { - BOOST_CONTRACT_ASSERT(size() >= 0); - } - -private: - std::vector vect_; -}; - -//[no_virtual_oldof_macro -class identifiers { -public: - virtual void push_back(int id, boost::contract::virtual_* v = 0) - // Program old-value without macros but with extra `v`. - boost::contract::old_ptr old_size = boost::contract::make_old(v, - boost::contract::copy_old(v) ? size() : boost::contract::null_old() - ); - - boost::contract::guard c = boost::contract::public_function(this) - .postcondition([&] { - BOOST_CONTRACT_ASSERT(size() == *old_size + 1); - }) - ; - - vect_.push_back(id); // Function body. - } - - /* ... */ - - //] - - void invariant() const { - BOOST_CONTRACT_ASSERT(size() >= 0); - } - -private: - std::vector vect_; -}; - - -//[no_base_types_macro -#include - -class multi_identifiers : - private boost::contract::constructor_precondition, - public identifiers, - public virtual pushable, - protected sizer, - private capacitor -{ -public: - // Program `base_types` without macros (list only public bases). - typedef boost::mpl::vector base_types; - - /* ... */ -//] -}; - diff --git a/example/features/noncopyable_old.cpp b/example/features/noncopyable_old.cpp new file mode 100644 index 0000000..f06ffc6 --- /dev/null +++ b/example/features/noncopyable_old.cpp @@ -0,0 +1,48 @@ + +#include +#include + +//[noncopyable_old +template +void accumulate(T& total, T const& x) { + // No compiler error if T has no copy constructor... + boost::contract::noncopyable_old_ptr old_total = + BOOST_CONTRACT_OLDOF(total); + boost::contract::guard c = boost::contract::function() + .postcondition([&] { + // ...but old value null if T has no copy constructor. + if(old_total) BOOST_CONTRACT_ASSERT(total == *old_total + x); + }) + ; + + total += x; +} +//] + +struct n { + int value; + + n() : value(0) {} + n operator+(n const& r) const { n x; x.value = value + r.value; return x; } + n& operator+=(n const& r) { value = value + r.value; return *this; } + bool operator==(n const& r) const { return value == r.value; } +private: + n(n const&) {} // Hide copy constructor (non copy-constructible). +}; + +// Specialize `boost::is_copy_constructible` trait (not needed on C++11): + +int main() { + n j, k; + j.value = 1; + k.value = 2; + accumulate(j, k); + assert(j.value == 3); + + int i = 1; + accumulate(i, 2); + assert(i == 3); + + return 0; +} + diff --git a/example/features/old.cpp b/example/features/old.cpp index 43c6554..eda6f87 100644 --- a/example/features/old.cpp +++ b/example/features/old.cpp @@ -2,28 +2,36 @@ #include #include #include +#include //[old -char replace(std::string& s, std::size_t index, char c) { +char replace(std::string& s, std::size_t index, char x) { char result; - boost::shared_ptr old_c; // But old values copied later... - auto c = boost::contract::function() + boost::contract::old_ptr old_y; // But old value copied later... + boost::contract::guard c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(index < s.size()); }) - .old([&] { // ...here, after preconditions (and invariants) checked. - old_c = BOOST_CONTRACT_OLDOF(s[index]); + .old([&] { // ...after preconditions (and invariants) checked. + old_y = BOOST_CONTRACT_OLDOF(s[index]); }) .postcondition([&] { - BOOST_CONTRACT_ASSERT(s[index] == c); - BOOST_CONTRACT_ASSERT(result == *old_c); + BOOST_CONTRACT_ASSERT(s[index] == x); + BOOST_CONTRACT_ASSERT(result == *old_y); }) ; - // Function body. result = s[index]; - s[index] = c; + s[index] = x; return result; } //] +int main() { + std::string s = "abc"; + char r = replace(s, 1, '_'); + assert(s == "a_c"); + assert(r == 'b'); + return 0; +} + diff --git a/example/features/old_no_macros.cpp b/example/features/old_no_macros.cpp new file mode 100644 index 0000000..cd31b15 --- /dev/null +++ b/example/features/old_no_macros.cpp @@ -0,0 +1,41 @@ + +#include +#include +#include + +//[old_no_macros +template +class vector { +public: + virtual void push_back(T const& value, boost::contract::virtual_* v = 0) { + // Program old value instead of using `OLDOF(size())` macro. + boost::contract::old_ptr old_size = + boost::contract::make_old(v, boost::contract::copy_old(v) ? + size() : boost::contract::null_old()) + ; + + boost::contract::guard c = boost::contract::public_function(v, this) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(size() == *old_size + 1); + }) + ; + + vect_.push_back(value); + } + + /* ... */ +//] + + unsigned size() const { return vect_.size(); } + +private: + std::vector vect_; +}; + +int main() { + vector vect; + vect.push_back(123); + assert(vect.size() == 1); + return 0; +} + diff --git a/example/features/optional_result.cpp b/example/features/optional_result.cpp index c9d7127..d95686f 100644 --- a/example/features/optional_result.cpp +++ b/example/features/optional_result.cpp @@ -29,9 +29,9 @@ surface square_surface(int edge) { //] int main() { - surface square = square_surface(10); - assert(square.area == 100); - assert(square.perimeter == 40); + surface s = square_surface(10); + assert(s.area == 100); + assert(s.perimeter == 40); return 0; } diff --git a/example/features/overload.cpp b/example/features/overload.cpp new file mode 100644 index 0000000..48522e1 --- /dev/null +++ b/example/features/overload.cpp @@ -0,0 +1,177 @@ + +#include +#include +#include +#include + +class lines { +public: + virtual std::string str(boost::contract::virtual_* v = 0) const = 0; + virtual std::string& str(boost::contract::virtual_* v = 0) = 0; + + virtual void put(std::string const& x, + boost::contract::virtual_* v = 0) = 0; + + virtual void put(char x, boost::contract::virtual_* v = 0) = 0; + + virtual void put(int x, bool tab = false, + boost::contract::virtual_* v = 0) = 0; +}; + +std::string lines::str(boost::contract::virtual_* v) const { + std::string result; + boost::contract::guard c = boost::contract::public_function(v, result, this) + .postcondition([&] (std::string const& result) { + if(result != "") BOOST_CONTRACT_ASSERT(*result.rbegin() == '\n'); + }) + ; + assert(false); + return result; +} + +std::string& lines::str(boost::contract::virtual_* v) { + boost::optional result; + boost::contract::guard c = boost::contract::public_function(v, result, this) + .postcondition([&] (boost::optional const& result) { + if(*result != "") BOOST_CONTRACT_ASSERT(*result->rbegin() == '\n'); + }) + ; + assert(false); + return *result; +} + +void lines::put(std::string const& x, boost::contract::virtual_* v) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(*x.rbegin() != '\n'); + }) + ; + assert(false); +} + +void lines::put(char x, boost::contract::virtual_* v) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(x != '\n'); + }) + ; + assert(false); +} + +void lines::put(int x, bool tab, + boost::contract::virtual_* v) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(x >= 0); + }) + ; + assert(false); +} + +//[overload +class string_lines + #define BASES public lines + : BASES +{ +public: + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + BOOST_CONTRACT_OVERRIDES(str) // Used only once for all `str` overloads. + + std::string str(boost::contract::virtual_* v = 0) const /* override */ { + std::string result; + boost::contract::guard c = boost::contract::public_function< + override_str + // Note the use of `static_cast` (and same in other overloads below). + >(v, result, static_cast(&string_lines::str), this); + + return result = str_; + } + + // Overload on (absence of) `const` qualifier. + std::string& str(boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function< + override_str + >(v, str_, static_cast(&string_lines::str), this); + + return str_; + } + + BOOST_CONTRACT_OVERRIDES(put) // Used only once for all `put` overloads. + + void put(std::string const& x, + boost::contract::virtual_* v = 0) /* override */ { + boost::contract::old_ptr old_str = + BOOST_CONTRACT_OLDOF(v, str()); + boost::contract::guard c = boost::contract::public_function< + override_put + >(v, static_cast(&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 old_str = + BOOST_CONTRACT_OLDOF(v, str()); + boost::contract::guard c = boost::contract::public_function< + override_put + >(v, static_cast(&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 old_str = + BOOST_CONTRACT_OLDOF(v, str()); + boost::contract::guard c = boost::contract::public_function< + override_put + >(v, static_cast(&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_; +}; +//] + +int main() { + string_lines s; + s.put("abc"); + assert(s.str() == "abc\n"); + s.put('x'); + assert(s.str() == "abc\nx\n"); + s.put(10); + assert(s.str() == "abc\nx\n10\n"); + s.put(20, true); + lines const& l = s; + assert(l.str() == "abc\nx\n10\n\t20\n"); + return 0; +} + diff --git a/example/features/override_overload.cpp b/example/features/override_overload.cpp deleted file mode 100644 index a2a90ba..0000000 --- a/example/features/override_overload.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -//[override_overload -class a - #define BASES public b - : BASES -{ -public: - typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; - #undef BASES - - void f(int x, boost::contract::virtual_* v = 0) /* override */ { - auto c = boost::contract::public_function( - v, &a::f, this, x); - /* ... */ - } - - void f(double x, boost::contract::virtual_* v = 0) /* override */ { - auto c = boost::contract::public_function( - v, &a::f, this, x); - /* ... */ - } - -private: - BOOST_CONTRACT_OVERRIDE(f) -}; -//] - diff --git a/example/features/public.cpp b/example/features/public.cpp index a9a26eb..6e9dea9 100644 --- a/example/features/public.cpp +++ b/example/features/public.cpp @@ -68,7 +68,7 @@ public: .precondition([&] { BOOST_CONTRACT_ASSERT(!find(id)); }) - .postcondition([&] (int result) { + .postcondition([&] (int const result) { if(!*old_find) { BOOST_CONTRACT_ASSERT(find(id)); BOOST_CONTRACT_ASSERT(size() == *old_size + 1); @@ -115,7 +115,7 @@ public: .precondition([&] { // Check in OR with bases. BOOST_CONTRACT_ASSERT(find(id)); }) - .postcondition([&] (int result) { // Check in AND with bases. + .postcondition([&] (int const result) { // Check in AND with bases. if(*old_find) BOOST_CONTRACT_ASSERT(size() == *old_size); }) ; diff --git a/example/features/pure_virtual_public.cpp b/example/features/pure_virtual_public.cpp index b92a9b1..abfa5f5 100644 --- a/example/features/pure_virtual_public.cpp +++ b/example/features/pure_virtual_public.cpp @@ -3,7 +3,8 @@ #include #include -class surface { +//[pure_virtual_public +struct surface { int area; int perimeter; @@ -11,24 +12,22 @@ class surface { surface(int area, int perimeter) : area(area), perimeter(perimeter) {} }; -//[pure_virtual_public class shape { public: - virtual surface area(boost::contract::virtual_* v = 0) const = 0; + virtual surface get_surface(boost::contract::virtual_* v = 0) const = 0; }; -// Pure-virtual function definitions (so also contracts) out-of-line in C++. -surface shape::area(boost::contract::virtual_* v) const { +// Pure-virtual function definition (and contract) out-of-line (usual in C++). +surface shape::get_surface(boost::contract::virtual_* v) const { boost::optional result; boost::contract::guard c = boost::contract::public_function(v, result, this) - .postcondition([&] (boost::optional const& result) { - BOOST_CONTRACT_ASSERT(result.area > 0); - BOOST_CONTRACT_ASSERT(result.perimeter > 0); + .postcondition([&] (boost::optional const& result) { + BOOST_CONTRACT_ASSERT(result->area > 0); + BOOST_CONTRACT_ASSERT(result->perimeter > 0); }) ; - // Pure function body (will never be executed by this library). - assert(false); + assert(false); // Pure function body (never executed by this library). return *result; } @@ -41,11 +40,11 @@ public: typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; #undef BASES - surface area(boost::contract::virtual_* v = 0) const /* override */ { + surface get_surface(boost::contract::virtual_* v = 0) const /* override */ { boost::optional result; boost::contract::guard c = boost::contract::public_function< - override_area>(v, result, &square::area, this) - .postcondition([&] (boost::optional const& result) { + override_get_surface>(v, result, &square::get_surface, this) + .postcondition([&] (boost::optional const& result) { BOOST_CONTRACT_ASSERT(result->area == edge() * edge()); BOOST_CONTRACT_ASSERT(result->perimeter == edge() * 4); }) @@ -53,13 +52,13 @@ public: return *(result = surface(edge() * edge(), edge() * 4)); } - BOOST_CONTRACT_OVERRIDE(area) + BOOST_CONTRACT_OVERRIDE(get_surface) /* ... */ //] explicit square(int edge) : - boost::contract::constructor_precondition([&] { + boost::contract::constructor_precondition([&] { BOOST_CONTRACT_ASSERT(edge > 0); }), edge_(edge) @@ -68,7 +67,7 @@ public: boost::contract::guard c = boost::contract::constructor(this); } - virtual ~square() { + ~square() { // Check invariants. boost::contract::guard c = boost::contract::destructor(this); } @@ -88,7 +87,10 @@ private: }; int main() { - + square sq(10); + surface s = sq.get_surface(); + assert(s.area == 100); + assert(s.perimeter == 40); return 0; } diff --git a/example/features/separate_body.cpp b/example/features/separate_body.cpp index ffaaa45..85c3283 100644 --- a/example/features/separate_body.cpp +++ b/example/features/separate_body.cpp @@ -1,20 +1,33 @@ #include "separate_body.hpp" +#include //[separate_body_cpp -template -void array::constructor_body(std::size_t count) { - for(std::size_t i = 0; i < count; ++i) values_[i] = T(); +template +void array::constructor_body(unsigned count) { + for(unsigned i = 0; i < count; ++i) values_[i] = T(); size_ = count; } -template -void array::destructor_body() { delete[] values_; } +template +void array::destructor_body() { delete[] values_; } -template -std::size_t array::size_body() const { return size_; } +template +void array::push_back_body(T const& value) { + values_[size_++] = value; +} -template -void array::push_back_body(T const& value) { values_[size_++] == value; } +/* ... */ //] +template +unsigned array::size_body() const { return size_; } + +int main() { + array a(2); + assert(a.size() == 2); + a.push_back('x'); + assert(a.size() == 3); + return 0; +} + diff --git a/example/features/separate_body.hpp b/example/features/separate_body.hpp index 635d47d..da28e68 100644 --- a/example/features/separate_body.hpp +++ b/example/features/separate_body.hpp @@ -1,43 +1,41 @@ +#ifndef SEPARATE_BODY_HPP_ +#define SEPARATE_BODY_HPP_ + #include //[separate_body_hpp -template -class array - : private boost::contract::constructor_precondition -{ +template +class array : + private boost::contract::constructor_precondition > { public: void invariant() const { BOOST_CONTRACT_ASSERT(size() <= MaxSize); } - explicit array(std::size_t count) : - boost::contract::constructor_precondition([&] { - BOOST_CONTRACT_ASSERT(count <= MaxSize) + explicit array(unsigned count) : + boost::contract::constructor_precondition([&] { + BOOST_CONTRACT_ASSERT(count <= MaxSize); }), - values_(new T[MaxSize]) // But must member initializations here. + values_(new T[MaxSize]) // Still, member initializations must be here. { - auto c = boost::contract::constructor(this) + boost::contract::guard c = boost::contract::constructor(this) .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == count); }) ; - constructor_body(count); + constructor_body(count); // Separate constructor body implementation. } virtual ~array() { - auto c = boost::contract::destructor(this); // Check invariants. - destructor_body(); + boost::contract::guard c = boost::contract::destructor(this); // Inv. + destructor_body(); // Separate destructor body implementation. } - std::size_t size() const { - auto c = boost::contract::public_function(this); // Check invariants. - return size_body(); - } - - void push_back(T const& value) { - auto old_size = BOOST_CONTRACT_OLDOF(v, size()); - auto c = boost::contract::public_function(v, this) + virtual void push_back(T const& value, boost::contract::virtual_* v = 0) { + boost::contract::old_ptr old_size = + BOOST_CONTRACT_OLDOF(v, size()); + boost::contract::guard c = boost::contract::public_function(v, this) .precondition([&] { BOOST_CONTRACT_ASSERT(size() < MaxSize); }) @@ -45,18 +43,31 @@ public: BOOST_CONTRACT_ASSERT(size() == *old_size + 1); }) ; - push_back_body(); + push_back_body(value); + } + +private: + // Contracts in class declaration (above), but body implementations are not. + void constructor_body(unsigned count); + void destructor_body(); + void push_back_body(T const& value); + + /* ... */ +//] + +public: + unsigned size() const { + // Check invariants. + boost::contract::guard c = boost::contract::public_function(this); + return size_body(); } private: - // Contracts in class declaration (above), but body implementations are not. - void construtor_body(std::size_t count); - void destructor_body(); - std::size_t size_body() const; - void push_back_body(T const& value); + unsigned size_body() const; T* values_; - std::size_t size_; + unsigned size_; }; -//] + +#endif // #include guard diff --git a/example/features/static_if_cxx14.cpp b/example/features/static_if_cxx14.cpp deleted file mode 100644 index f8a5d42..0000000 --- a/example/features/static_if_cxx14.cpp +++ /dev/null @@ -1,110 +0,0 @@ - -// Test possible impl. advance() in single function with C++14 generic lambdas. - -#include -#include -#include -#include -#include // std::bind for generic lambdas. -#include -#include -#include - -template -struct is_random_access_iterator : std::is_same< - typename std::iterator_traits::iterator_category, - std::random_access_iterator_tag -> {}; - -template -struct is_bidirectional_iterator : std::is_same< - typename std::iterator_traits::iterator_category, - std::bidirectional_iterator_tag -> {}; - -template -struct is_input_iterator : std::is_same< - typename std::iterator_traits::iterator_category, - std::input_iterator_tag -> {}; - -//[static_if_cxx14 -template -void myadvance(Iter& i, Dist n) { - Iter *p = &i; // So captures change actual pointed iterator value. - boost::contract::call_if >( - std::bind([] (auto p, auto n) { - *p += n; - }, p, n) - ).template else_if >( - std::bind([] (auto p, auto n) { - if(n >= 0) while(n--) ++*p; - else while(n++) --*p; - }, p, n) - ).template else_if >( - std::bind([] (auto p, auto n) { - while(n--) ++*p; - }, p, n) - ).else_( - std::bind([] (auto false_) { - static_assert(false_, "requires input iter"); - }, std::false_type()) // Use constexpr value. - ); -} -//] - -struct x {}; // Test not an iterator (static_assert failure in else_ above). - -namespace std { - template<> - struct iterator_traits { - typedef void iterator_category; - }; -} - -int main() { - std::ostringstream ok; - - std::vector v; - v.push_back('a'); - v.push_back('b'); - v.push_back('c'); - v.push_back('d'); - std::vector::iterator r = v.begin(); // Random iterator. - out.str(""); - myadvance(r, 1); - out << *r << std::endl; - ok.str(""); ok - << "random iterator" << std::endl - << "b" << std::endl - ; - BOOST_TEST(out.eq(ok.str())); - - std::list l(v.begin(), v.end()); - std::list::iterator b = l.begin(); // Bidirectional iterator. - out.str(""); - myadvance(b, 2); - out << *b << std::endl; - ok.str(""); ok - << "bidirectional iterator" << std::endl - << "c" << std::endl - ; - BOOST_TEST(out.eq(ok.str())); - - std::istringstream s("a b c d"); - std::istream_iterator i(s); - out.str(""); - myadvance(i, 3); - out << *i << std::endl; - ok.str(""); ok - << "input iterator" << std::endl - << "d" << std::endl - ; - BOOST_TEST(out.eq(ok.str())); - - // x xx; - // myadvance(xx, 0); // Error (correctly because x not even input iter). - - return boost::report_errors(); -} - diff --git a/example/features/volatile.cpp b/example/features/volatile.cpp new file mode 100644 index 0000000..3173689 --- /dev/null +++ b/example/features/volatile.cpp @@ -0,0 +1,75 @@ + +#include +#include + +//[volatile +class a { +public: + void invariant() const volatile; // Invariants cv qualified. + void invariant() const; // Invariants const qualified. + + a() { // Check both cv and const invariant (at exit if no throw). + boost::contract::guard c= boost::contract::constructor(this); + } + + ~a() { // Check both cv and const invariant (at entry). + boost::contract::guard c = boost::contract::destructor(this); + } + + void m() { // Check const invariant (at entry and exit if no throw). + boost::contract::guard c = boost::contract::public_function(this); + } + + void c() const { // Check const invariant (at entry and exit if no throw). + boost::contract::guard c = boost::contract::public_function(this); + } + + void v() volatile { // Check cv invariant (at entry and exit if no throw). + boost::contract::guard c = boost::contract::public_function(this); + } + + void cv() const volatile { // Check cv inv. (at entry and exit if no throw). + boost::contract::guard c = boost::contract::public_function(this); + } +}; +//] + +bool cv_invariant_checked, const_invariant_checked; +void a::invariant() const volatile { cv_invariant_checked = true; } +void a::invariant() const { const_invariant_checked = true; } + +int main() { + { + cv_invariant_checked = const_invariant_checked = false; + a x; + assert(cv_invariant_checked); + assert(const_invariant_checked); + + cv_invariant_checked = const_invariant_checked = false; + x.m(); + assert(!cv_invariant_checked); + assert(const_invariant_checked); + + cv_invariant_checked = const_invariant_checked = false; + x.c(); + assert(!cv_invariant_checked); + assert(const_invariant_checked); + + cv_invariant_checked = const_invariant_checked = false; + x.v(); + assert(cv_invariant_checked); + assert(!const_invariant_checked); + + cv_invariant_checked = const_invariant_checked = false; + x.cv(); + assert(cv_invariant_checked); + assert(!const_invariant_checked); + + cv_invariant_checked = const_invariant_checked = false; + } // Call destructor. + assert(cv_invariant_checked); + assert(const_invariant_checked); + + return 0; +} + diff --git a/include/boost/contract.hpp b/include/boost/contract.hpp index 1cd3861..ce625af 100644 --- a/include/boost/contract.hpp +++ b/include/boost/contract.hpp @@ -21,6 +21,8 @@ #include #include +// TODO: Add a test for all operators (member and non-member ones). In theory nothing special should be needed for operators... but test it. For member operators, test them with public_function virtual overrides. + // TODO: Document compile and run time performances (after creating specific tests to measure them). // TODO: Add all copyright and licencing info (to all files, etc.). diff --git a/include/boost/contract/old.hpp b/include/boost/contract/old.hpp index abc04ab..75175ce 100644 --- a/include/boost/contract/old.hpp +++ b/include/boost/contract/old.hpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #if !BOOST_PP_VARIADICS @@ -53,7 +55,7 @@ BOOST_CONTRACT_ERROR_macro_OLDOF_requires_variadic_macros_otherwise_manually_pro BOOST_CONTRACT_OLDOF_AUTO_TYPEOF_(value)(boost::contract::make_old( \ boost::contract::copy_old() ? (value) : boost::contract::null_old() \ )) - + /* PUBLIC */ // NOTE: Leave this #defined the same regardless of ..._POSTCONDITIONS. @@ -68,6 +70,39 @@ BOOST_CONTRACT_ERROR_macro_OLDOF_requires_variadic_macros_otherwise_manually_pro #endif // variadics +/* PRIVATE */ + +#ifdef BOOST_CONTRACT_NO_POSTCONDITIONS + #define BOOST_CONTRACT_OLD_POSTCONDITIONS_ 0 +#else + #define BOOST_CONTRACT_OLD_POSTCONDITIONS_ 1 +#endif + +// Used to avoid exposing entire shared_ptr API (e.g., client stealing +// pointer ownership could break this library). +#define BOOST_CONTRACT_OLD_PTR_DEF_(pointer_name, pointed_type) \ + public: \ + typedef pointed_type element_type; \ + \ + pointer_name() {} \ + \ + /* only const access (contracts should not change old values) */ \ + \ + pointed_type const* operator->() const { return ptr_.operator->(); } \ + \ + BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(pointer_name, \ + !!ptr_) \ + \ + private: \ + BOOST_PP_EXPR_IIF(BOOST_CONTRACT_OLD_POSTCONDITIONS_, \ + explicit pointer_name(boost::shared_ptr ptr) : \ + ptr_(ptr) {} \ + ) \ + \ + boost::shared_ptr ptr_; \ + \ + friend class convertible_old; + /* CODE */ // Specialization because `unconvertible_old` incomplete type when trait used. @@ -82,32 +117,41 @@ namespace boost { namespace boost { namespace contract { -// Used to avoid exposing entire shared_ptr API (e.g., client stealing -// ownership of this pointer could break this lib). template -class old_ptr { // Copyable (as *). -public: - explicit old_ptr() {} +class noncopyable_old_ptr; - // Only const access (because contracts should not change program state). - +template +class old_ptr { /* copyable (as *) */ + BOOST_CONTRACT_OLD_PTR_DEF_(old_ptr, T) + +public: T const& operator*() const { + // Compiler error if old_ptr::operator* and non-copyable T. + BOOST_STATIC_ASSERT_MSG( + boost::is_copy_constructible::value, +"old_ptr requires T copy constructor, otherwise use noncopyable_old_ptr" + ); BOOST_CONTRACT_DETAIL_DEBUG(ptr_); return *ptr_; } - T const* operator->() const { return ptr_.operator->(); } + friend class noncopyable_old_ptr; +}; - BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(old_ptr, !!ptr_) +template +class noncopyable_old_ptr { /* copyable (as *) */ + BOOST_CONTRACT_OLD_PTR_DEF_(noncopyable_old_ptr, T) + +public: + // Required to assign to OLDOF (as OLDOF returns old_ptr for auto decl). + /* implicit */ noncopyable_old_ptr(old_ptr const& other) : + ptr_(other.ptr_) {} -private: - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - explicit old_ptr(boost::shared_ptr ptr) : ptr_(ptr) {} - #endif - - boost::shared_ptr ptr_; - - friend class convertible_old; + T const& operator*() const { + // No static assert is_copy_constructible so T can be non-copyable here. + BOOST_CONTRACT_DETAIL_DEBUG(ptr_); + return *ptr_; + } }; class unconvertible_old { // Copyable (as *). @@ -144,21 +188,42 @@ private: class convertible_old { // Copyable (as *). public: - // Implicitly called by constructor init `old_ptr old_x = ...`. + // Implicitly called by ctor init `noncopyable_old_ptr old_x = ...`. + template + /* implicit */ operator noncopyable_old_ptr() { + return ptr >(); + } + + // Implicitly called by ctor init `old_ptr old_x = ...`. template /* implicit */ operator old_ptr() { + return ptr >(); + } + +private: + explicit convertible_old(virtual_* v, unconvertible_old const& old) #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - if(!boost::is_copy_constructible::value) { + : v_(v), ptr_(old.ptr_) + #endif + {} + + template + Ptr ptr() { + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + if(!boost::is_copy_constructible< + typename Ptr::element_type>::value) { BOOST_CONTRACT_DETAIL_DEBUG(!ptr_); // Non-copyable so no old... - return old_ptr(); // ...and return null. + return Ptr(); // ...and return null. } else if(!v_ && boost::contract::detail::check_guard::checking()) { // Return null shared ptr (see after if statement). } else if(!v_) { BOOST_CONTRACT_DETAIL_DEBUG(ptr_); - boost::shared_ptr old = - boost::static_pointer_cast(ptr_); + boost::shared_ptr old = + boost::static_pointer_cast< + typename Ptr::element_type const>(ptr_) + ; BOOST_CONTRACT_DETAIL_DEBUG(old); - return old_ptr(old); + return Ptr(old); } else if(v_->action_ == boost::contract::virtual_::push_old_init || v_->action_ == boost::contract::virtual_::push_old_copy) { BOOST_CONTRACT_DETAIL_DEBUG(ptr_); @@ -167,7 +232,7 @@ public: } else { v_->old_copies_.push(ptr_); } - return old_ptr(); + return Ptr(); } else if(v_->action_ == boost::contract::virtual_::pop_old_init || v_->action_ == boost::contract::virtual_::pop_old_copy) { BOOST_CONTRACT_DETAIL_DEBUG(!ptr_); @@ -183,23 +248,18 @@ public: } else { v_->old_copies_.pop(); } - boost::shared_ptr old = - boost::static_pointer_cast(ptr); + boost::shared_ptr old = + boost::static_pointer_cast< + typename Ptr::element_type const>(ptr) + ; BOOST_CONTRACT_DETAIL_DEBUG(old); - return old_ptr(old); + return Ptr(old); } BOOST_CONTRACT_DETAIL_DEBUG(!ptr_); #endif - return old_ptr(); + return Ptr(); } -private: - explicit convertible_old(virtual_* v, unconvertible_old const& old) - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - : v_(v), ptr_(old.ptr_) - #endif - {} - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS virtual_* v_; boost::shared_ptr ptr_; diff --git a/include/boost/contract/override.hpp b/include/boost/contract/override.hpp index 9cf5609..62abd1a 100644 --- a/include/boost/contract/override.hpp +++ b/include/boost/contract/override.hpp @@ -8,6 +8,7 @@ // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html #include +#include #include /* PRIVATE */ @@ -15,7 +16,6 @@ #ifndef BOOST_CONTRACT_NO_PUBLIC_FUNCTIONS #include #include - #include #include #define BOOST_CONTRACT_OVERRIDE_CALL_BASE_(z, arity, arity_compl, f) \ @@ -67,20 +67,39 @@ /* PUBLIC */ - #define BOOST_CONTRACT_OVERRIDE_TRAIT(trait, f) \ - struct trait { \ + #define BOOST_CONTRACT_NAMED_OVERRIDE(name, f) \ + struct name { \ BOOST_CONTRACT_DETAIL_INTROSPECTION_HAS_MEMBER_FUNCTION( \ BOOST_CONTRACT_DETAIL_NAME1(has_member_function), f) \ \ BOOST_CONTRACT_OVERRIDE_CALL_BASE_DECL_(f) \ }; #else - #define BOOST_CONTRACT_OVERRIDE_TRAIT(trait, f) \ - struct trait {}; /* empty trait type (won't be actually used) */ + // Empty type (not actually used) just needed to compile user contract code. + #define BOOST_CONTRACT_NAMED_OVERRIDE(name, f) struct name {}; #endif #define BOOST_CONTRACT_OVERRIDE(f) \ - BOOST_CONTRACT_OVERRIDE_TRAIT(BOOST_PP_CAT(override_, f), f) + BOOST_CONTRACT_NAMED_OVERRIDE(BOOST_PP_CAT(override_, f), f) + +#if BOOST_CONTRACT_DETAIL_TVARIADIC + #include + #include + + /* PRIVATE */ + + #define BOOST_CONTRACT_OVERRIDES_SEQ_(r, unused, f) \ + BOOST_CONTRACT_OVERRIDE(f) + + /* PUBLIC */ + + #define BOOST_CONTRACT_OVERRIDES(...) \ + BOOST_PP_SEQ_FOR_EACH(BOOST_CONTRACT_OVERRIDES_SEQ_, ~, \ + BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) +#else + #define BOOST_CONTRACT_OVERRIDES \ +BOOST_CONTRACT_ERROR_macro_OVERRIDES_requires_variadic_macros_otherwise_manually_repeat_OVERRIDE_macro +#endif #endif // #include guard diff --git a/test/call_if/advance_cxx14.cpp b/test/call_if/advance_cxx14.cpp index 9a9b1f4..6c806fb 100644 --- a/test/call_if/advance_cxx14.cpp +++ b/test/call_if/advance_cxx14.cpp @@ -11,7 +11,7 @@ #include #include -boost::contract::test::detail::oteststream out; +boost::contract::test::detail::oteststream out; // For testing only. template struct is_random_access_iterator : std::is_same< @@ -31,11 +31,12 @@ struct is_input_iterator : std::is_same< std::input_iterator_tag > {}; +//[advance_cxx14 template void myadvance(Iter& i, Dist n) { Iter *p = &i; // So captures change actual pointed iterator value. boost::contract::call_if >( - std::bind([] (auto p, auto n) { + std::bind([] (auto p, auto n) { // C++14 generic lambda. out << "random iterator" << std::endl; *p += n; }, p, n) @@ -56,6 +57,7 @@ void myadvance(Iter& i, Dist n) { }, std::false_type()) // Use constexpr value. ); } +//] struct x {}; // Test not an iterator (static_assert failure in else_ above).