From 21f228d80b6bfe0d01bb5956ff5fdffbaa794fee Mon Sep 17 00:00:00 2001 From: Lorenzo Caminiti Date: Sat, 4 Jun 2016 09:41:03 -0700 Subject: [PATCH] resolved most todo and recompiled most examples and tests --- doc/advanced_topics.qbk | 55 +++- doc/contract_programming_overview.qbk | 14 +- doc/introduction.qbk | 2 +- doc/tutorial.qbk | 44 ++- example/n1962/vector.cpp | 2 +- example/n1962/vector_n1962.hpp | 2 +- include/boost/contract.hpp | 14 +- include/boost/contract/base_types.hpp | 6 +- include/boost/contract/constructor.hpp | 2 - include/boost/contract/core/access.hpp | 8 +- include/boost/contract/core/config.hpp | 8 +- include/boost/contract/core/exception.hpp | 2 +- include/boost/contract/core/virtual.hpp | 3 +- include/boost/contract/detail/check_guard.hpp | 1 - .../contract/detail/condition/check_base.hpp | 2 - .../detail/inlined/core/exception.hpp | 4 +- include/boost/contract/old.hpp | 2 - include/boost/contract/override.hpp | 4 +- include/boost/contract/public_function.hpp | 30 +- test/Jamfile.v2 | 37 +-- test/{function => call_if}/check_if.cpp | 0 .../no_check_if_error.cpp | 0 test/disable/lib_a_inlined.hpp | 12 +- test/disable/lib_ab.hpp | 24 +- test/disable/lib_b_inlined.hpp | 20 +- test/disable/prog_pre_disable_nothing.cpp | 2 +- test/misc/mutex.cpp | 258 ++++++++++++++++++ test/{set => specify}/no_guard.cpp | 0 test/{set => specify}/nothing.cpp | 0 test/{set => specify}/old.cpp | 0 test/{set => specify}/old_post.cpp | 0 test/{set => specify}/old_pre_error.cpp | 0 test/{set => specify}/post.cpp | 0 test/{set => specify}/post_old_error.cpp | 0 test/{set => specify}/post_pre_error.cpp | 0 test/{set => specify}/pre.cpp | 0 test/{set => specify}/pre_old.cpp | 0 test/{set => specify}/pre_old_post.cpp | 0 test/{set => specify}/pre_post.cpp | 0 39 files changed, 430 insertions(+), 128 deletions(-) rename test/{function => call_if}/check_if.cpp (100%) rename test/{function => call_if}/no_check_if_error.cpp (100%) create mode 100644 test/misc/mutex.cpp rename test/{set => specify}/no_guard.cpp (100%) rename test/{set => specify}/nothing.cpp (100%) rename test/{set => specify}/old.cpp (100%) rename test/{set => specify}/old_post.cpp (100%) rename test/{set => specify}/old_pre_error.cpp (100%) rename test/{set => specify}/post.cpp (100%) rename test/{set => specify}/post_old_error.cpp (100%) rename test/{set => specify}/post_pre_error.cpp (100%) rename test/{set => specify}/pre.cpp (100%) rename test/{set => specify}/pre_old.cpp (100%) rename test/{set => specify}/pre_old_post.cpp (100%) rename test/{set => specify}/pre_post.cpp (100%) diff --git a/doc/advanced_topics.qbk b/doc/advanced_topics.qbk index ecfdbef..0b65508 100644 --- a/doc/advanced_topics.qbk +++ b/doc/advanced_topics.qbk @@ -14,6 +14,13 @@ There can be cases in which the expression passed to [macroref BOOST_CONTRACT_OL 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: +[footnote +*Rationale:* +Functors for preconditions, old value assignments, and postconditions are all optional but when specified, they must be specified in this order. +Such order is enforced by the fact that [classref boost::contract::specify_precondition_old_postconditions], [classref boost::contract::specify_old_postcondition], [classref boost::contract::specify_postcondition_only], and [classref boost::contract::specify_nothing] provide a progressively decreasing subset of the `.precondition(...)`, `.old(...)` and `.postcondition(...)` member functions. +The enforced order of preconditions, old value assignments, and postconditions is logical because it reflects the order in which they are executed at run-time. +Other contract programming frameworks allow to mix this order, that could have been implemented for this library too but it would have complicated a somewhat the library implementation while adding no real value (arguably creating confusion because allowing for less logical orderings). +] boost::contract::old_ptr<...> old_``[^['name]]``; // Use default constructor. boost::contract::guard c = boost::contract::function() // Same for all other contracts. @@ -37,13 +44,21 @@ The functor passed to `.old(...)` should capture all variables it needs to evalu 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__). +This library will automatically call [funcref boost::contract::postcondition_failure] if calling the functor specified via `.old(...)` 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.). +[footnote +*Rationale:* +If old value pointers are assigned at the point of their construction instead of using `.old(...)` then an exception thrown by the old value expression [macroref BOOST_CONTRACT_OLDOF] or more in general any exception thrown by the old value pointer initialization will result in that exception being thrown up the stack by the contracted function. +This is arguably less correct than calling [funcref boost::contract::postcondition_failure] because an exception thrown by an old value copy causes the program to fail checking its postconditions and should not technically causes the contracted function to thrown an exception (however, this might not be a significant difference in practice). +Note that while it would be possible to wrap all old value operations ([refclass boost::contract::old_ptr] copy constructor, [funcref boost::contract::make_old], etc.) in try-catch statements so this library will call [funcref boost::contract::postcondition_failure] also when old values are copied when they are constructed outside `.old(...)`, that will prevent this library from knowing the [enumref boost::contract::from] parameter which is not acceptable (specifically because destructors can have postconditions). +] + [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). [footnote -*Rationale.* +*Rationale:* `boost::optional` was used instead of `std::optional` because this library is designed to work well with Boost and because `std::optional` is not part of the C++ standard yet. ] For example (see also [@../../example/features/optional_result.cpp =optional_result.cpp=]): @@ -84,7 +99,7 @@ This this case the functor passed to `.postcondition(...)` takes a parameter of 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.* +*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). ] @@ -124,7 +139,7 @@ Therefore, no contract is ever programmed for a private or protected pure virtua 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.* +*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). @@ -144,7 +159,7 @@ Therefore, [macroref BOOST_CONTRACT_OVERRIDE] only needs to be invoked once for 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.* +*Rationale:* 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__). ] @@ -176,11 +191,17 @@ Thus both the move constructor and the move assignment operator need to maintain [import ../example/features/move.cpp] [move] -This example requires that it is possible to call the public member function `move()` on the moved-from object. +This example assumes that it is possible to call the public member function `move()` on the moved-from object. This allows to make explicit the precondition that except for destructor, copy and move assignments all other public member functions cannot be called on a moved-from object. -This assumption is usually implicit in C++ (i.e., documented by the standard but not checked by the language at run-time). +This precondition is usually implicit in C++ (i.e., documented by the standard but not checked by the language at run-time). If it is is not possible (e.g., due to some optimized implementation of the move operations) to have such a public `move()` member function, the private `moved_` member (or similar) can be used to program class invariants and preconditions (and that will just relay on the usual implicit C++ assumption on moved-from object because users will not be able to fully check preconditions and class invariants before calling functions of a moved-from object). +[note +The default move constructor and move assignment operator automatically generated by C++ will not check contracts. +Therefore, unless these operations are not public or they have no preconditions, no postconditions, and the class has no invariants, programmers should manually define them using [funcref boost::contract::constructor], [classref boost::contract::constructor_precondition], and [funcref boost::contract::public_function]. +(Same for all other automatically generated operations.) +] + [endsect] [section Unions] @@ -202,7 +223,7 @@ These /volatile class invariants/ are programmed in a public member function `co 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.* +*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. @@ -221,7 +242,7 @@ For example (see also [@../../example/features/volatile.cpp =volatile.cpp=]): 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.* +*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. @@ -257,7 +278,7 @@ More precisely, dereferencing an old value pointer of type [classref boost::cont * 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*. +*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::old_ptr_noncopyable]. ] * 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. @@ -366,7 +387,7 @@ As seen so far, programmers are required to decorate their classes declaring ext * 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.* +*Rationale:* 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.) ] @@ -415,6 +436,16 @@ Instead, the function bodies (function implementations) is programmed in a separ The same technique can be used for non-member, private, and protected functions. +[note +When contracts are programmed in a separate =.cpp= files and also /all/ these library headers are `#include`d only from =.cpp= files, then the =.cpp= files can be compiled disabling specific contract checking (for example, [macroref BOOST_CONTRACT_NO_POSTCONDITIONS] and [macroref BOOST_CONTRACT_NO_EXIT_INVARIANTS], see __Disable_Contract_Checking__). +The user code that will link to these =.cpp= files not be able to +Then the code in these =.cpp= files will always have such contract checking disabled even when linked to some other user code that might have been compiled with a different set of disable contracts (i.e., a different `BOOST_CONTRACT_NO_...` defined). +This technique might be useful to ship a pre-compiled set of object files (e.g., for a library) that will never check some contracts (e.g., postconditions and exit invariants) regardless of the definition of the `BOOST_CONTRACT_NO_...` macros used to compile code that uses such object files. + +On the contrary, if contracts are programmed in header files and this library headers are `#include`d in header files that are being shipped, then end users enable or disables contracts of the shipped code by defining `BOOST_CONTRACT_NO_...` when they compiled the shipped header files as part of their code. +This technique might be useful in other situations when programmers that ship code want instead to leave it up the their end users to decide which contracts in the shipped code should be checked at run-time. +] + [endsect] [section Throw on Failure] @@ -423,7 +454,7 @@ If a condition checked using [macroref BOOST_CONTRACT_ASSERT] is `false` or if c By default, the contract failure handler functions print a message to the standard error `std::cerr` and then terminate the program calling `std::terminate`. [footnote -*Rationale.* +*Rationale:* In general, when a contract fails the only safe thing to do is to terminate the program execution (because the contract failure indicates a bug in the program, and in general the program is in a state for which no operation can be successfully performed, so the program should be stopped). Therefore, this library terminates the program by default. However, for specific applications, programmers could implement some fail-safe mechanism for which some mission-critical operation can always be performed upon handling failures so this library allows programmers to override the default contract failure handlers to fully customize how to handle contract failures. @@ -504,7 +535,7 @@ Therefore, the following can be considered mainly a curiosity because programmer 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.* +*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. diff --git a/doc/contract_programming_overview.qbk b/doc/contract_programming_overview.qbk index 50d6e71..5059127 100644 --- a/doc/contract_programming_overview.qbk +++ b/doc/contract_programming_overview.qbk @@ -24,7 +24,7 @@ Postconditions can usually access the function return value (for non-void functi It is possible to specify a different set of class invariants for volatile member functions, namely /volatile class invariants/. It is also possible to specify /static class invariants/ which are excepted to be true before and after the execution of any constructor, destructor (even if it does not throw an exception), and public member function (even if static). [footnote -*Rationale.* +*Rationale:* Static and volatile class invariants were first introduced by this library to reflect the fact that C++ supports both static and volatile member functions. Static and volatile class invariants are not part of __N1962__. ] @@ -279,7 +279,7 @@ Furthermore, contracts are most useful when they assert conditions only using pu For example, the caller of a public member function cannot in general make sure that the function preconditions are satisfied if the precondition assertions use private members that are not callable by the caller (therefore, a failure in the preconditions will not necessarily indicate a bug in the caller given that the caller was made unable to fully check the preconditions in the first place). However, given that C++ provides programmers ways around access level restrictions (`friend`, function pointers, etc.), this library leaves it up to the programmers to make sure that only public members are used in contract assertions (__N1962__ follows the same approach not restricting contracts to only use public members, Eiffel instead generates a compiler error if precondition assertions use non-public members). [footnote -*Rationale.* +*Rationale:* In theory, if C++ [@http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45 defect 45] did not get fixed, this library could have been implemented in a way that generates a compile-time error when precondition assertions use non-public members (but still at the expense of programmers writing extra boiler-plate code). ] @@ -292,7 +292,7 @@ If either preconditions, postconditions, or class invariants throw exceptions or By default, these failure handler functions print a text message to the standard error `std::cerr` (with detailed information about the failure) and then terminate the program calling `std::terminate`. However, using [funcref boost::contract::set_precondition_failure], [funcref boost::contract::set_postcondition_failure], [funcref boost::contract::set_invariant_failure], etc. programmers can define their own failure handler functions that can take any desired action (throw an exception, exit the program with an error code, etc., see __Throw_on_Failure__). [footnote -*Rationale.* +*Rationale:* This customizable failure handling mechanism is similar to the one used by `std::terminate` and also proposed by __N1962__. ] @@ -370,7 +370,7 @@ Yes, also support subcontracting for multiple inheritance ([macroref BOOST_CONTR [ Yes, also support subcontracting for multiple inheritance. Only base classes can specify preconditions. [footnote -*Rationale.* +*Rationale:* The authors of __N1962__ decided to forbid derived classes from subcontracting preconditions because they found such a feature rarely if ever used (see [@http://lists.boost.org/Archives/boost/2010/04/164862.php Re: \[boost\] \[contract\] diff n1962]). Still, it should be noted that even in __N1962__ if a derived class overrides two functions with preconditions coming from two different base classes via multiple inheritance, the overriding function contract will check preconditions from its two base function in __OR__ (so even in __N1962__ preconditions can indirectly be subcontracted by the derived class when multiple inheritance is used). The authors of this library found that confusing about __N1962__. @@ -409,7 +409,7 @@ Furthermore, subcontracting preconditions is soundly defined by the __substituti [ Yes, but use [macroref BOOST_CONTRACT_PRECONDITIONS_DISABLE_NO_ASSERTION] to disable no assertion while checking preconditions. [footnote -*Rationale.* +*Rationale:* Theoretically, it can be shown that an incorrect argument might be passed to the function body when assertion checking is disabled while checking preconditions (see [@http://lists.boost.org/Archives/boost/2010/04/164862.php Re: \[boost\] \[contract\] diff n1962]). Therefore, __N1962__ does not disable any assertion while checking preconditions. However, that makes it possible to have infinite recursion while checking preconditions, plus Eiffel disables assertion checking also while checking preconditions. @@ -425,7 +425,7 @@ Therefore, this library by default disables assertion checking also while checki [ Disable nothing. [footnote -*Rationale.* +*Rationale:* Older versions of this library defined a data member in the user class that was automatically used to disable checking of class invariants within nested member function calls (similarly to Eiffel). This feature was also required by older revisions of __N1962__ but it is no longer required by __N1962__. Furthermore, in multi-threaded programs this feature would introduce a lock that synchronizes all member functions calls for a given object. @@ -460,7 +460,7 @@ This supports class invariants and old values but it does not support subcontrac [ [__Tandin04__] [C++] [ Interestingly, these contract macros automatically generate Doxygen documentation [footnote -*Rationale.* +*Rationale:* Older versions of this library used to automatically generate Doxygen documentation from contract definition macros. This functionality was abandoned for a number of reasons: this library no longer uses macros to program contracts; even before that, this library macros became too complex and the Doxygen preprocessor was no longer able to expand them; the Doxygen documentation was just a repeat of the contract code (so programmers could directly look at contracts in the source code); Doxygen might not necessarily be the documentation tool used by all C++ programmers. ] diff --git a/doc/introduction.qbk b/doc/introduction.qbk index 53b83d0..edf0b5e 100644 --- a/doc/introduction.qbk +++ b/doc/introduction.qbk @@ -52,7 +52,7 @@ Finally, if the class invariant check failed on entering the `push_back` functio By default, when an assertion fails this library prints an error message to the standard error `std::cerr` and terminates the program calling `std::terminate` (this behaviour can be customized to take any user-defined action including throwing exceptions, see the __Throw_on_Failure__ section). Note that the error message printed by this library contains information to easily and uniquely identify the point in the program at which the assertion failed. [footnote -*Rationale.* +*Rationale:* The assertion failure messages generated by this library follows a format similar to the messages printed by Clang when the C-style `assert` macro fails. ] diff --git a/doc/tutorial.qbk b/doc/tutorial.qbk index c547785..12ad26d 100644 --- a/doc/tutorial.qbk +++ b/doc/tutorial.qbk @@ -29,7 +29,13 @@ The [funcref boost::contract::function] function returns an RAII object that mus The name of this local variable is arbitrary, but `c` is often used in this documentation. ] The function body is programmed right after the declaration of this RAII object. -At construction, this RAII object does the following: + +[note +In some cases, it might be necessary to program some code before the contract. +For example for acquiring resources that will be used while checking the contract like old values, but also to lock mutexes (or other synchronization mechanisms) in multi-threaded programs (as usual with C++, in these cases it is generally preferred to use RAII objects to automatically control acquisition and release of the resources). +] + +At construction, the RAII object returned by [funcref boost::contract::function] does the following: # Check preconditions, by calling the nullary functor [^['f]]`()` passed to `.precondition(`[^['f]]`)`. @@ -80,7 +86,7 @@ It is also recommended to use the [macroref BOOST_CONTRACT_ASSERT] macro to prog // Or, if condition has commas `,` not already within parenthesis `(...)`. BOOST_CONTRACT_ASSERT((``/boolean-condition/``)) -This library will automatically call [funcref boost::contract::precondition_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] macro conditions are `false` and also if the precondition 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.). +This library will automatically call [funcref boost::contract::precondition_failure] if any of the [macroref BOOST_CONTRACT_ASSERT] macro conditions are `false` and also if calling the functor specified via `.precondition(...)` 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.). [note Contracts are most useful when their assertions only use public members that are accessible to the caller so the caller can properly check and use the contract. @@ -120,7 +126,7 @@ It is also recommended to use the [macroref BOOST_CONTRACT_ASSERT] macro to prog // Or, if condition has commas `,` not already within parenthesis `(...)`. BOOST_CONTRACT_ASSERT((``/boolean-condition/``)) -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.). +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 calling the functor specified via `.postcondition(...)` 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__). @@ -274,7 +280,7 @@ However, in most production code it might not be acceptable to augment the publi ] Set the [macroref BOOST_CONTRACT_STATIC_INVARIANT] macro to use a name different from `static_invariant` (e.g., because `static_invariant` clashes with other names in the user-defined class). [footnote -*Rationale.* +*Rationale:* In C++, it is not possible to overload a member function based on the `static` classifier. Therefore, different function names have to be used for member functions checking static and non-static class invariants, namely `invariant` and `static_invariant`. ] @@ -296,12 +302,16 @@ Constructor preconditions are specified using the [classref boost::contract:cons Programmes should not access the object `this` from constructor preconditions (because the object does not exists yet before the constructor body is executed, see also __No_Lambda_Functions__). Constructors without preconditions simply do not explicitly initialize the [classref boost::contract::constructor_precondition] base (because [classref boost::contract::constructor_precondition] default constructor checks no contract). When [classref boost::contract::constructor_precondition] is used: +[footnote +There is a MSVC bug that was fixed in MSVC 2013 for which lambdas cannot be used in constructor member initialization lists for templates. +On MSVC compilers with that bug, an external (static member) function can be used (together with `bind` and `cref` as needed) to program constructor preconditions instead of using lambdas. +] * It should be specified as the /first/ class in the inheritance list (so constructor preconditions are checked before initializing any other base or member). * Its inheritance level should always be `private` (so this extra base class does not alter the public inheritance tree of the derived class). * It takes the derived class as template parameter (the Curiously Recursive Template Pattern (CRTP) is used here to avoid ambiguity errors with multiple inheritance). [footnote -*Rationale.* +*Rationale:* The [classref boost::contract::constructor_precondition] takes the derived class as its template parameter so the instantiated template type is unique for each derived class always avoiding base class ambiguities even in case of multiple inheritance. Virtual inheritance cannot be used resolve such ambiguities because virtual bases are initialized only once by the out-most derived class, and that would not allow to properly check preconditions of all base classes. ] @@ -328,6 +338,10 @@ This together with C++ object construction mechanism of base classes and the use [note A constructor can avoid calling [funcref boost::contract::constructor] for efficiency but only when it has no postconditions and its class has no invariants. (Even if [funcref boost::contract::constuctor] is not used by a derived class, contracts of base classes will still be correctly checked by C++ object construction mechanism.) + +The default constructor and copy constructor automatically generated by C++ will not check contracts. +Therefore, unless these constructors are not public or they have no preconditions, no postconditions, and the class has no invariants, programmers should manually define them using [funcref boost::contract::constructor] and [classref boost::contract::constructor_precondition]. +(Same for all other automatically generated operations.) ] Private and protected constructors can omit [funcref boost::contract::constructor] because they are not part of the public interface of the class so they are not required to check class invariants (see also __Constructor_Calls__). @@ -366,6 +380,10 @@ This together with C++ object destruction mechanism of base classes ensures that [note A destructor can avoid calling [funcref boost::contract::destructor] for efficiency but only when it has no postconditions and its class has no invariants. (Even if [funcref boost::contract::destructor] is not used by a derived class, contracts of base classes will still be correctly checked by C++ object destruction mechanism.) + +The default destructor automatically generated by C++ will not check contracts. +Therefore, unless the destructor is not public or it has no postconditions and the class has no invariants, programmers should manually define it using [funcref boost::contract::destructor]. +(Same for all other automatically generated operations.) ] Private and protected destructors can omit [funcref boost::contract::destructor] because they are not part of the public interface of the class so they are not required to check class invariants (see also __Destructor_Calls__). @@ -402,6 +420,10 @@ This ensures that public member function contracts are correctly checked at run- [note A public member function can avoid calling [funcref boost::contract::public_function] for efficiency but only when it has no preconditions and no postconditions, it is not virtual, it does not override any virtual function, and its class has no invariant. + +The default copy assignment operator automatically generated by C++ will not check contracts. +Therefore, unless this operator is not public or it has no preconditions, no postconditions, and the class has no invariants, programmers should manually define it using [funcref boost::contract::public_function]. +(Same for all other automatically generated operations.) ] [endsect] @@ -421,7 +443,7 @@ This extra parameter is the last parameter and it has a default argument so it d Callers will rarely have to explicitly deal with this extra parameter (a part from when manipulating the virtual function type directly as a function pointer, for function pointer type-casts, etc.). Programmers must pass the extra virtual parameter as the very first argument to all [macroref BOOST_CONTRACT_OLDOF] and [funcref boost::contract::public_function] calls in the virtual function. [footnote -*Rationale.* +*Rationale:* The [classref boost::contract::virtual_]`*` optional parameter is used by this library to determine that a function is virtual (in C++ it is not possible to introspect if a function has been declared `virtual`). Furthermore, this parameter is internally used by this library to pass result and old values that are evaluated by the overriding function to overridden virtual functions, and also to check preconditions and postconditions of overridden virtual functions when subcontracting (but without executing overridden function bodies). ] @@ -430,7 +452,7 @@ The [funcref boost::contract::public_function] function takes `this` as a parame As shown in the example above, when the public virtual function has a non-void return type programmers must pass a reference to the function return value as the second argument to [funcref boost::contract::public_function]. In this case the functor specified to `.postcondition(...)` takes a single parameter for the return value (possibly as a constant reference `const&` to avoid extra copies of the return value). [footnote -*Rationale.* +*Rationale:* The functor passed to `.postcondition(...)` takes the extra return value parameter because that is used by this library to pass the return value evaluated by the overriding function to all its overridden virtual functions when subcontracting. ] @@ -438,7 +460,7 @@ The functor passed to `.postcondition(...)` takes the extra return value paramet It is the responsibility of the programmers to pass the extra parameter `v` to all [macroref BOOST_CONTRACT_OLDOF] and [funcref boost::contract::public_function] calls within public virtual functions, and also to pass the return value reference after `v` to [funcref boost::contract::public_function] for non-void public virtual functions. This library cannot automatically generate compile-time errors if programmers fail to do so (but in general contract checking will not correctly work at run-time). [footnote -*Rationale.* +*Rationale:* This library does not require the function type when using [funcref boost::contract::public_function] for non-overriding virtual functions. Therefore, this library does not know if the enclosing function has a non-void return type so it cannot check if the return value reference is passed as required for non-overriding virtual functions. Instead this library requires the function type for overriding virtual functions thus it gives a compile-time error if the return value reference is missing in those cases. @@ -490,13 +512,13 @@ Programmers must pass the extra virtual parameter as the very first argument to When called from overriding public functions, [funcref boost::contract::public_function] also takes a pointer to the enclosing function, the object `this` (because overriding public functions check class invariants), and references to each function argument in the order they appear in the function declaration. [footnote -*Rationale.* +*Rationale:* The object `this` is passed after the function pointer to follow `bind`'s syntax. The function pointer and references to all function arguments are needed for overriding virtual public functions because this library has to call overridden virtual public functions to check their contracts for subcontracting (even if this library will not actually execute the bodies of the overridden functions). ] As shown in the example above, when the overriding public function has a non-void return type, programmers must pass a reference to the function return value as the second argument to [funcref boost::contract::public_function] (this library will generate a compile-time error otherwise). [footnote -*Rationale.* +*Rationale:* As for non-overriding public virtual functions, also overriding functions use the extra return value parameter to pass it to the overridden functions when subcontracting. In the case of overriding functions this library also has the function pointer so it will generate a compile-time error if the function is non-void and programmers forget to specify the extra return value parameter (this extra error checking is not possible instead for non-overriding public virtual functions because their contracts do not have to specify the function pointer, see also __Virtual_Public_Functions__). ] @@ -548,7 +570,7 @@ When the extra base [classref boost::contract::constructor_precondition] is used [important Each base passed to [macroref BOOST_CONTRACT_BASE_TYPES] must /explicitly/ specify its inheritance access level `public`, `protected`, or `private` (`virtual` is optional and can be specified either before or after the access level as usual in C++). [footnote -*Rationale.* +*Rationale:* This library explicitly requires the inheritance access level because derived classes must subcontract only from public bases, but not from protected or private bases (see __Public_Function_Calls__). Therefore, [macroref BOOST_CONTRACT_BASE_TYPES] inspect each inheritance access level (using preprocessor meta-programming) and removes non-public bases from the list bases `base_types` to consider for subcontracting. ] diff --git a/example/n1962/vector.cpp b/example/n1962/vector.cpp index d151eb9..52ef7cb 100644 --- a/example/n1962/vector.cpp +++ b/example/n1962/vector.cpp @@ -53,7 +53,7 @@ public: vector() : vect_() { boost::contract::guard c = boost::contract::constructor(this) .postcondition([&] { - BOOST_CONTRACT_ASSERT(!empty()); + BOOST_CONTRACT_ASSERT(empty()); }) ; } diff --git a/example/n1962/vector_n1962.hpp b/example/n1962/vector_n1962.hpp index 91ea572..eaca2d5 100644 --- a/example/n1962/vector_n1962.hpp +++ b/example/n1962/vector_n1962.hpp @@ -52,7 +52,7 @@ public: vector() postcondition { - !empty(); + empty(); } : vect_() {} diff --git a/include/boost/contract.hpp b/include/boost/contract.hpp index 52481b2..4524ced 100644 --- a/include/boost/contract.hpp +++ b/include/boost/contract.hpp @@ -37,21 +37,9 @@ never be used directly by programmers. #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: Review all warnings for examples, tests, and also lib compilation... // TODO: Add all copyright and licencing info (to all files, etc.). -// TODO: Document that default generated functions (generated default constructors, copy constructors, copy operators, destructors, move constructors, move operators, etc.) will not check contracts. Programmer have to program them in order to check contracts. Also default special operations might not be able to establish class invariants (default ctor, etc.) and/or might break it (default move operations). - -// TODO: Document an example that uses moved() to fully specify move semantics using contracts (similar to "test/move/contracts.cpp"). - -// TODO: Document that there is a MSVC 2010 bug for which lambdas cannot be used in template constructor intialization list (this was fixed in MSVC 2013). Therefore, an external (static member) function must be used (possibly with bind and cref) to program constructor preconditions on MSVC 2010 instead of using lambdas. - -// TODO: Document that users could program (scoped) locks at top of function definitions and before the contract code so to deal with contracts in multi-threaded situations. Would these locks need to be recursive for virtual calls? Test that... - -// TODO: Document pre > old > post are all optional but when they are specified they must be specified in this order (rationale: this is the logical order in which to specify them, other contract programming framework allow to mix this order, that could have been implemented in this lib too but it would complicated a bit the implementation to then allow for mixed orders that will be less clear logically). - #endif // #include guard diff --git a/include/boost/contract/base_types.hpp b/include/boost/contract/base_types.hpp index ddcd9ef..b6b1297 100644 --- a/include/boost/contract/base_types.hpp +++ b/include/boost/contract/base_types.hpp @@ -7,8 +7,6 @@ // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt). // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html -// TODO: Document the max number of bases is 20 because of Boost.MPL vector limit. If Boost.MPL vector and related alg impl was to change to use variadic templates in the future there will be not limit to max bases (but for now this high limit is better than the extra complexity of reimpl all Boost.MPL vector, etc. within this lib with variadic templates). - /** @file Facility to specify inheritance form base classes (to support subcontracting). */ @@ -38,7 +36,9 @@ base classes @c typedef can be programmed manually without using this macro (see @param ... Comma separated list of base classes. Each listed base must explicitly list its access specifier @c public, @c protected, or @c private, and @c virtual if present (this not always required in - C++ instead). + C++ instead). There is an intrinsic limit around 20 on the maximum + number of supported bases (because of similar limits for some Boost + libraries like Boost.MPL internally used by this library). */ #define BOOST_CONTRACT_BASE_TYPES(...) void /* dummy type for typedef */ diff --git a/include/boost/contract/constructor.hpp b/include/boost/contract/constructor.hpp index b03cd33..b315d58 100644 --- a/include/boost/contract/constructor.hpp +++ b/include/boost/contract/constructor.hpp @@ -50,7 +50,6 @@ specify_old_postcondition<> constructor(Class* obj) { #endif } -// TODO: Document that constructor_precondition for unions must be called at the very beginning of ctor body before `boost::contract::guard c = ...` (because unions cannot have base classes, not even in C++11). /** Program preconditions for constructors. This class must be the very first base class of the contracted class. Also the @@ -61,7 +60,6 @@ Unions cannot have base classes in C++ so this class can be used to declare a local object within the constructor function just before @RefFunc{boost::contract::constructor} is used (see @RefSect{advanced_topics, Advanced Topics}). - @see @RefSect{tutorial, Tutorial} @tparam Class Class of contracted constructor. */ diff --git a/include/boost/contract/core/access.hpp b/include/boost/contract/core/access.hpp index c08e927..74b553a 100644 --- a/include/boost/contract/core/access.hpp +++ b/include/boost/contract/core/access.hpp @@ -25,10 +25,6 @@ Facility to declare invariants, base types, etc all as private members. #endif #include -// TODO: Review all warnings for examples, tests, and also lib compilation... - -// TODO: Document (in a rationale) that using friend to limit lib's public API does not increase compilation times at all. I compiled with friends. Then I removed all friends, made related APIs all public and the compilation times of all test/public_function/* where exactly the same for all compilers (msvc 37 min, gcc 70 min, clang 46 min). So there is not reason at all to not use friends (plus not using friend will complicate the internal APIs because contractor names cannot be wrapped using DETAIL_NAME so they will still be made private and accessed via some sort of static DETAIL_NAME(make) member function...). - namespace boost { namespace contract { class virtual_; @@ -136,6 +132,10 @@ class access : private boost::noncopyable { // Friends (used to limit library's public API). + // NOTE: Using friends here and in all other places in this library does not + // increase compilation times (I experimented replacing all friends with + // public and got the same compilation times). + BOOST_CONTRACT_DETAIL_DECL_DETAIL_CHECK_SUBCONTRACTED_PRE_POST_INV_Z(1, /* is_friend = */ 1, OO, RR, FF, CC, AArgs); diff --git a/include/boost/contract/core/config.hpp b/include/boost/contract/core/config.hpp index 4fd5009..60f47ef 100644 --- a/include/boost/contract/core/config.hpp +++ b/include/boost/contract/core/config.hpp @@ -16,8 +16,6 @@ Facilities to configure this library compile-time and run-time behaviour. // headers after that depending on the contract 0/1 macros below ensuring no // compilation overhead. -// TODO: Document that when contracts are programmed in .cpp and all these lib headers are #include only from within .cpp, then a given lib can be compiled for example without inv/post, only with pre. The code that will link to that lib will not be able to enable inv/post, or disable the pre. However, if contracts are programmed in .hpp and this lib headers are #included in .hpp that are shipped to users with a given lib, users of that lib can turn on/off all contracts for the shipped lib as well. - #ifdef DOXYGEN /** Define this macro to compile this library as a shared library or DLL @@ -51,8 +49,6 @@ Facilities to configure this library compile-time and run-time behaviour. #define BOOST_CONTRACT_HEADER_ONLY #endif -// TODO: Document that no code should in general be programmed before the contract in function definitions, but for example mutex locks can be programmed before contracts if necessary for synchronization. - #ifdef DOXYGEN /** Define this macro to not lock internal library data for thread safety @@ -80,8 +76,10 @@ Facilities to configure this library compile-time and run-time behaviour. to support a maximum number of arguments different than @c 10 for overriding public functions (contracted via @RefFunc{boost::contract::public_function}). + (Compilation times of this library were measured to be comparable between + compilers that support variadic macros and compilers that do not.) @note Regardless of the value of this macro and of compiler support for - variadic macros, there is an intrinsic limit around 19 arguments + variadic macros, there is an intrinsic limit around 18 arguments for overriding public functions (because of similar limits for some Boost libraries like Boost.MPL and Boost.FunctionTypes internally used by this library). diff --git a/include/boost/contract/core/exception.hpp b/include/boost/contract/core/exception.hpp index bee3228..ec43544 100644 --- a/include/boost/contract/core/exception.hpp +++ b/include/boost/contract/core/exception.hpp @@ -387,7 +387,7 @@ This is equivalent to calling both */ void /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ set_invariant_failure(assertion_failure_handler const& f) - /** @cond */ OOST_NOEXCEPT_OR_NOTHROW /** @endcond */; + /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; /** Set the all contract failure handlers at once (for convenience). diff --git a/include/boost/contract/core/virtual.hpp b/include/boost/contract/core/virtual.hpp index fa4d50a..26e6fc5 100644 --- a/include/boost/contract/core/virtual.hpp +++ b/include/boost/contract/core/virtual.hpp @@ -120,8 +120,7 @@ class virtual_ : private boost::noncopyable { // Avoid copy queue, stack, etc. // Friends (used to limit library's public API). friend bool copy_old(virtual_*); - - friend class convertible_old; + friend class old_pointer; BOOST_CONTRACT_DETAIL_DECL_DETAIL_CHECK_SUBCONTRACTED_PRE_POST_INV_Z(1, /* is_friend = */ 1, OO, RR, FF, CC, AArgs); diff --git a/include/boost/contract/detail/check_guard.hpp b/include/boost/contract/detail/check_guard.hpp index 5e91a79..9b40c2c 100644 --- a/include/boost/contract/detail/check_guard.hpp +++ b/include/boost/contract/detail/check_guard.hpp @@ -26,7 +26,6 @@ public: private: static bool checking_; - // TODO: Document this (and also exception handler mutexes) introduce global locks when checking contracts... #ifndef BOOST_CONTRACT_DISABLE_THREADS static boost::mutex mutex_; #endif diff --git a/include/boost/contract/detail/condition/check_base.hpp b/include/boost/contract/detail/condition/check_base.hpp index a89840a..2f9977f 100644 --- a/include/boost/contract/detail/condition/check_base.hpp +++ b/include/boost/contract/detail/condition/check_base.hpp @@ -89,7 +89,6 @@ protected: #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS void copy_old() { if(failed()) return; - // TODO: Document that when old copies throw, using .old() calls post failure handler (more correct), while using = OLDOF makes enclosing user function throw (less correct). Plus of course using .old() makes old copies after inv and pre are checked, while using = OLDOF makes old copies before inv and pre checking (this is less correct in theory, but it should not really matter in most practical cases unless the old copy are programmed assuming inv and pre are satisfied). Also document in a rationale that it would be possible to wrap all old.hpp operations (old_ptr copy constructor, make_old, etc.) in try-catch statements so for this lib to call postcondition_failure handler also when ... = OLDOF is used. However, in that case this lib cannot populate the from parameter (and destructors can have postconditions so from would be necessary for ... = OLDOF used in a destructor) so the authors decided to not do that and leave that in the hands of the programmers (that can manually wrap ... = OLDOF with a try-catch in their user code if necessary, or better just use .old(...) when calling the failure handler for old value copies is important). try { if(old_) old_(); } catch(...) { fail(&boost::contract::postcondition_failure); } } @@ -109,7 +108,6 @@ protected: #endif private: - // TODO: Document all BOOST_CONTRACT_ERROR_... and BOOST_STATIC_ASSERT_MSG errors (in an annex...). bool BOOST_CONTRACT_ERROR_missing_guard_declaration; bool guard_asserted_; // Avoid throwing twice from dtors (undef behavior). #if !defined(BOOST_CONTRACT_NO_PRECONDITIONS) || \ diff --git a/include/boost/contract/detail/inlined/core/exception.hpp b/include/boost/contract/detail/inlined/core/exception.hpp index 08a77e1..1969bb7 100644 --- a/include/boost/contract/detail/inlined/core/exception.hpp +++ b/include/boost/contract/detail/inlined/core/exception.hpp @@ -61,7 +61,7 @@ bad_virtual_result_cast::bad_virtual_result_cast(char const* from_type_name, bad_virtual_result_cast::~bad_virtual_result_cast() {} -char const* bad_virtual_result_cast::what() const BOOST_NOEXCEPT { +char const* bad_virtual_result_cast::what() const BOOST_NOEXCEPT_OR_NOTHROW { return what_.c_str(); } @@ -76,7 +76,7 @@ assertion_failure::assertion_failure(char const* const code) : assertion_failure::~assertion_failure() {} -char const* assertion_failure::what() const BOOST_NOEXCEPT { +char const* assertion_failure::what() const BOOST_NOEXCEPT_OR_NOTHROW { return what_.c_str(); } diff --git a/include/boost/contract/old.hpp b/include/boost/contract/old.hpp index dab0dc5..d789603 100644 --- a/include/boost/contract/old.hpp +++ b/include/boost/contract/old.hpp @@ -324,7 +324,6 @@ private: #endif friend class old_pointer; - friend old_value null_old(); /** @endcond */ }; @@ -433,7 +432,6 @@ private: #endif friend old_pointer make_old(old_value const&); - friend old_pointer make_old(virtual_*, old_value const&); /** @endcond */ }; diff --git a/include/boost/contract/override.hpp b/include/boost/contract/override.hpp index 83c0b98..13bc58e 100644 --- a/include/boost/contract/override.hpp +++ b/include/boost/contract/override.hpp @@ -76,7 +76,7 @@ subcontracting). /* PUBLIC */ #define BOOST_CONTRACT_NAMED_OVERRIDE(override_name, function_name) \ - struct name { \ + struct override_name { \ BOOST_CONTRACT_DETAIL_INTROSPECTION_HAS_MEMBER_FUNCTION( \ BOOST_CONTRACT_DETAIL_NAME1(has_member_function), \ function_name \ @@ -97,7 +97,7 @@ subcontracting). @RefSect{advanced_topics, Advanced Topics}). */ #define BOOST_CONTRACT_NAMED_OVERRIDE(override_name, function_name) \ - struct name {}; /* empty type (not used) just to compile code */ + struct override_name {}; /* empty (not used), just to compile */ #endif /* PUBLIC */ diff --git a/include/boost/contract/public_function.hpp b/include/boost/contract/public_function.hpp index bda0bbd..a94c2fa 100644 --- a/include/boost/contract/public_function.hpp +++ b/include/boost/contract/public_function.hpp @@ -13,10 +13,6 @@ Overloads are provided to handle static, virtual void, virtual non-void, overriding void, and override non-void public functions. */ -// TODO: Document that even with variadic templates there's a hard limit to function max args (18 works, but MAX_ARGS=19 does not). This limit comes from Boost.MPL (vector, push_back, etc.), Boost.FunctionTypes, and other Boost algorithm that do not currently have a variadic template implementation. However, re-impl all these Boost alg would be too much work for this lib, plus the 19 max args limit seems high enough, and it would eventually be removed if Boost.MPL, Boost.FunctionTypes are ever ported to impl that use variadic templates. - -// TODO: Document that not using variadic templates (i.e., using pp meta-programming impl instead) does not increase compilation times (I measured this with the max_arg test program). - #include #include #include @@ -149,7 +145,6 @@ specify_precondition_old_postcondition<> public_function(Class* obj) { #define BOOST_CONTRACT_PUBLIC_FUNCTIONS_ 1 #endif -// TODO: Document no F here so cannot check consistency between VirtualResult and actual func's result type... up to user to pass the right VirtualResult... // For non-static, virtual, and non-overriding public functions (PRIVATE macro). #define BOOST_CONTRACT_PUBLIC_FUNCTION_VIRTUAL_NO_OVERRIDE_( \ has_virtual_result) \ @@ -253,10 +248,18 @@ specify_precondition_old_postcondition<> public_function(Class* obj) { @param v The contracted virtual function extra trailing parameter of type @RefClass{boost::contract::virtual_}* and with default value @c 0. - @param r A reference to the contracted virtual function return value. - (This can be a local variable within the contracted function + @param r A reference to the contracted virtual function return value + (this can be a local variable within the contracted function scope, but it must be set by programmers at each function - @c return statement.) + @c return statement). The type of this parameter must be the + same as (or compatible with) the contracted function return type + (this library might not be able to raise a compile-time error + if that is not the case (because this function does not take a + pointer to the contracted function or similar), but in general + such a type mismatch will cause a run-time error or undefined + behaviour). Alternatively, + boost::optional<return_type> can be used here (see + @RefSect{advanced_topics, Advanced Topics}). @param obj The object @c this from the scope of the contracted function. This object can be @c const and @c volatile depending on the cv-qualifier for the contracted function (volatile public @@ -413,10 +416,15 @@ specify_precondition_old_postcondition<> public_function(Class* obj) { @param v The contracted virtual function extra trailing parameter of type @RefClass{boost::contract::virtual_}* and with default value @c 0. - @param r A reference to the contracted virtual function return value. - (This can be a local variable within the contracted function + @param r A reference to the contracted virtual function return value + (this can be a local variable within the contracted function scope, but it must be set by programmers at each function - @c return statement.) + @c return statement). The type of this parameter must be the + same as (or compatible with) the contracted function return type + as specified by @c F (this library will generate a compile-time + error otherwise) Alternatively, + boost::optional<return_type> can be used here (see + @RefSect{advanced_topics, Advanced Topics}). @param f A pointer to the contracted function. @param obj The object @c this from the scope of the contracted function. This object can be @c const and @c volatile depending on the diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 7d5383a..117d09f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -187,9 +187,6 @@ test-suite function [ subdir-run-with-no function : body_throw ] [ subdir-run-with-no function : old_throw ] - - [ subdir-run-with-no function : check_if ] - [ subdir-compile-fail function : no_check_if_error ] ; test-suite result @@ -245,22 +242,27 @@ test-suite disable disable-lib_x disable-lib_y ] ; -test-suite set +test-suite specify : - [ subdir-run-with-no set : pre_old_post ] - [ subdir-run-with-no set : pre_old ] - [ subdir-run-with-no set : old_post ] - [ subdir-run-with-no set : pre_post ] - [ subdir-run-with-no set : pre ] - [ subdir-run-with-no set : old ] - [ subdir-run-with-no set : post ] - [ subdir-run-with-no set : nothing ] + [ subdir-run-with-no specify : pre_old_post ] + [ subdir-run-with-no specify : pre_old ] + [ subdir-run-with-no specify : old_post ] + [ subdir-run-with-no specify : pre_post ] + [ subdir-run-with-no specify : pre ] + [ subdir-run-with-no specify : old ] + [ subdir-run-with-no specify : post ] + [ subdir-run-with-no specify : nothing ] - [ subdir-run-with-no set : no_guard ] + [ subdir-run-with-no specify : no_guard ] - [ subdir-compile-fail set : old_pre_error ] - [ subdir-compile-fail set : post_old_error ] - [ subdir-compile-fail set : post_pre_error ] + [ subdir-compile-fail specify : old_pre_error ] + [ subdir-compile-fail specify : post_old_error ] + [ subdir-compile-fail specify : post_pre_error ] +; + +test-suite misc +: + [ subdir-run-with-no misc : mutex ] ; test-suite call_if @@ -272,6 +274,9 @@ test-suite call_if [ subdir-run call_if : false_void ] [ subdir-run call_if : equal_to ] + + [ subdir-run-with-no call_if : check_if ] + [ subdir-compile-fail call_if : no_check_if_error ] ; # C++14 supported only by Clang... so in its own test-suite and explicit. diff --git a/test/function/check_if.cpp b/test/call_if/check_if.cpp similarity index 100% rename from test/function/check_if.cpp rename to test/call_if/check_if.cpp diff --git a/test/function/no_check_if_error.cpp b/test/call_if/no_check_if_error.cpp similarity index 100% rename from test/function/no_check_if_error.cpp rename to test/call_if/no_check_if_error.cpp diff --git a/test/disable/lib_a_inlined.hpp b/test/disable/lib_a_inlined.hpp index 592e86d..301aac3 100644 --- a/test/disable/lib_a_inlined.hpp +++ b/test/disable/lib_a_inlined.hpp @@ -36,32 +36,32 @@ int a::f(x_type& x) { void a::disable_pre_failure() { boost::contract::set_precondition_failure([] (boost::contract::from) - { out("a::pre_failure"); }); + { out("a::pre_failure\n"); }); } void a::disable_post_failure() { boost::contract::set_postcondition_failure([] (boost::contract::from) - { out("a::post_failure"); }); + { out("a::post_failure\n"); }); } void a::disable_entry_inv_failure() { boost::contract::set_entry_invariant_failure([] (boost::contract::from) - { out("a::entry_inv_failure"); }); + { out("a::entry_inv_failure\n"); }); } void a::disable_exit_inv_failure() { boost::contract::set_exit_invariant_failure([] (boost::contract::from) - { out("a::exit_inv_failure"); }); + { out("a::exit_inv_failure\n"); }); } void a::disable_inv_failure() { boost::contract::set_invariant_failure([] (boost::contract::from) - { out("a::inv_failure"); }); + { out("a::inv_failure\n"); }); } void a::disable_failure() { boost::contract::set_failure([] (boost::contract::from) - { out("a::failure"); }); + { out("a::failure\n"); }); } #endif // #include guard diff --git a/test/disable/lib_ab.hpp b/test/disable/lib_ab.hpp index b3ad6ee..b9c603e 100644 --- a/test/disable/lib_ab.hpp +++ b/test/disable/lib_ab.hpp @@ -50,7 +50,7 @@ int main() { // Test preconditions have disabled no contract (incorrect, but // only possible behaviour if shared linking not used ad doc). << ok_f() - #elif defined(BOOST_CONTRACT_PRECONDITIONS_DISABLE_NOTHING) + #elif defined(BOOST_CONTRACT_PRECONDITIONS_DISABLE_NO_ASSERTION) // Test preconditions have disabled no contract. << ok_f() #else @@ -82,7 +82,7 @@ int main() { // Test old values not copied for disabled contracts. unsigned const cnt = - #if defined(BOOST_CONTRACT_PRECONDITIONS_DISABLE_NOTHING) && \ + #if defined(BOOST_CONTRACT_PRECONDITIONS_DISABLE_NO_ASSERTION) && \ !defined(BOOST_CONTRACT_NO_PRECONDITIONS) && \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) 1 @@ -105,53 +105,53 @@ int main() { out(""); boost::contract::precondition_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::pre_failure")); + "a::pre_failure\n")); a::disable_post_failure(); out(""); boost::contract::postcondition_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::post_failure")); + "a::post_failure\n")); a::disable_entry_inv_failure(); out(""); boost::contract::entry_invariant_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::entry_inv_failure")); + "a::entry_inv_failure\n")); a::disable_exit_inv_failure(); out(""); boost::contract::exit_invariant_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::exit_inv_failure")); + "a::exit_inv_failure\n")); a::disable_inv_failure(); out(""); boost::contract::entry_invariant_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::inv_failure")); + "a::inv_failure\n")); out(""); boost::contract::exit_invariant_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::inv_failure")); + "a::inv_failure\n")); a::disable_failure(); out(""); boost::contract::precondition_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::failure")); + "a::failure\n")); out(""); boost::contract::postcondition_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::failure")); + "a::failure\n")); out(""); boost::contract::entry_invariant_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::failure")); + "a::failure\n")); out(""); boost::contract::exit_invariant_failure(boost::contract::from()); BOOST_TEST(boost::contract::test::detail::oteststream::eq(out(), - "a::failure")); + "a::failure\n")); // Test setting failure handlers (from a lib using another lib). diff --git a/test/disable/lib_b_inlined.hpp b/test/disable/lib_b_inlined.hpp index d9a3641..317ba42 100644 --- a/test/disable/lib_b_inlined.hpp +++ b/test/disable/lib_b_inlined.hpp @@ -39,7 +39,7 @@ bool b::test_disable_pre_failure() { out(""); boost::contract::precondition_failure(boost::contract::from()); return boost::contract::test::detail::oteststream::eq(out(), - "a::pre_failure"); + "a::pre_failure\n"); } bool b::test_disable_post_failure() { @@ -47,7 +47,7 @@ bool b::test_disable_post_failure() { out(""); boost::contract::postcondition_failure(boost::contract::from()); return boost::contract::test::detail::oteststream::eq(out(), - "a::post_failure"); + "a::post_failure\n"); } bool b::test_disable_entry_inv_failure() { @@ -55,7 +55,7 @@ bool b::test_disable_entry_inv_failure() { out(""); boost::contract::entry_invariant_failure(boost::contract::from()); return boost::contract::test::detail::oteststream::eq(out(), - "a::entry_inv_failure"); + "a::entry_inv_failure\n"); } bool b::test_disable_exit_inv_failure() { @@ -63,7 +63,7 @@ bool b::test_disable_exit_inv_failure() { out(""); boost::contract::exit_invariant_failure(boost::contract::from()); return boost::contract::test::detail::oteststream::eq(out(), - "a::exit_inv_failure"); + "a::exit_inv_failure\n"); } bool b::test_disable_inv_failure() { @@ -71,11 +71,11 @@ bool b::test_disable_inv_failure() { out(""); boost::contract::entry_invariant_failure(boost::contract::from()); bool entry_inv = boost::contract::test::detail::oteststream::eq(out(), - "a::inv_failure"); + "a::inv_failure\n"); out(""); boost::contract::exit_invariant_failure(boost::contract::from()); bool exit_inv = boost::contract::test::detail::oteststream::eq(out(), - "a::inv_failure"); + "a::inv_failure\n"); return entry_inv && exit_inv; } @@ -84,19 +84,19 @@ bool b::test_disable_failure() { out(""); boost::contract::precondition_failure(boost::contract::from()); bool pre = boost::contract::test::detail::oteststream::eq(out(), - "a::failure"); + "a::failure\n"); out(""); boost::contract::postcondition_failure(boost::contract::from()); bool post = boost::contract::test::detail::oteststream::eq(out(), - "a::failure"); + "a::failure\n"); out(""); boost::contract::entry_invariant_failure(boost::contract::from()); bool entry_inv = boost::contract::test::detail::oteststream::eq(out(), - "a::failure"); + "a::failure\n"); out(""); boost::contract::exit_invariant_failure(boost::contract::from()); bool exit_inv = boost::contract::test::detail::oteststream::eq(out(), - "a::failure"); + "a::failure\n"); return pre && post && entry_inv && exit_inv; } diff --git a/test/disable/prog_pre_disable_nothing.cpp b/test/disable/prog_pre_disable_nothing.cpp index 03e1e68..7132dbd 100644 --- a/test/disable/prog_pre_disable_nothing.cpp +++ b/test/disable/prog_pre_disable_nothing.cpp @@ -1,7 +1,7 @@ // Test other contract checking but pre disabled within contract checking. -#define BOOST_CONTRACT_PRECONDITIONS_DISABLE_NOTHING +#define BOOST_CONTRACT_PRECONDITIONS_DISABLE_NO_ASSERTION #include "lib_a.hpp" #include "lib_a_inlined.hpp" #include "lib_b.hpp" diff --git a/test/misc/mutex.cpp b/test/misc/mutex.cpp new file mode 100644 index 0000000..32d8b67 --- /dev/null +++ b/test/misc/mutex.cpp @@ -0,0 +1,258 @@ + +// Test scoped mutex locks before contracts (no need to also test ctors, etc.). + +#include "../detail/oteststream.hpp" +#include +#include +#include +#include +#include + +boost::contract::test::detail::oteststream out; + +struct b : private boost::contract::constructor_precondition { + static void static_invariant() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + + b() : boost::contract::constructor_precondition( + [&] { out << "b::ctor::pre" << std::endl; }) { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::constructor(this) + .old([&] { out << "b::ctor::old" << std::endl; }) + .postcondition([&] { out << "b::ctor::post" << std::endl; }) + ; + out << "b::ctor::body" << std::endl; + } + + virtual ~b() { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::destructor(this) + .old([&] { out << "b::dtor::old" << std::endl; }) + .postcondition([&] { out << "b::dtor::post" << std::endl; }) + ; + out << "b::dtor::body" << std::endl; + } + + virtual void f(boost::contract::virtual_* v = 0) const { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { out << "b::f::pre" << std::endl; }) + .old([&] { out << "b::f::old" << std::endl; }) + .postcondition([&] { out << "b::f::post" << std::endl; }) + ; + out << "b::f::body" << std::endl; + } + + virtual void g(boost::contract::virtual_* v = 0) const { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { out << "b::g::pre" << std::endl; }) + .old([&] { out << "b::g::old" << std::endl; }) + .postcondition([&] { out << "b::g::post" << std::endl; }) + ; + out << "b::g::body" << std::endl; + } + +private: + mutable boost::mutex mutex_; +}; + +struct a + #define BASES private boost::contract::constructor_precondition, public b + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + static void static_invariant() { out << "a::static_inv" << std::endl; } + void invariant() const { out << "a::inv" << std::endl; } + + a() : boost::contract::constructor_precondition( + [&] { out << "a::ctor::pre" << std::endl; }) { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::constructor(this) + .old([&] { out << "a::ctor::old" << std::endl; }) + .postcondition([&] { out << "a::ctor::post" << std::endl; }) + ; + out << "a::ctor::body" << std::endl; + } + + ~a() { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::destructor(this) + .old([&] { out << "a::dtor::old" << std::endl; }) + .postcondition([&] { out << "a::dtor::post" << std::endl; }) + ; + out << "a::dtor::body" << std::endl; + } + + void f(boost::contract::virtual_* v = 0) const /* override */ { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::public_function( + v, &a::f, this) + .precondition([&] { out << "a::f::pre" << std::endl; }) + .old([&] { out << "a::f::old" << std::endl; }) + .postcondition([&] { out << "a::f::post" << std::endl; }) + ; + out << "a::f::body" << std::endl; + } + BOOST_CONTRACT_OVERRIDE(f) + + void g(boost::contract::virtual_* v = 0) const /* override */ { + boost::mutex::scoped_lock lock(mutex_); + boost::contract::guard c = boost::contract::public_function( + v, &a::g, this) + .precondition([&] { out << "a::g::pre" << std::endl; }) + .old([&] { out << "a::g::old" << std::endl; }) + .postcondition([&] { out << "a::g::post" << std::endl; }) + ; + out << "a::g::body" << std::endl; + } + BOOST_CONTRACT_OVERRIDE(g) + +private: + mutable boost::mutex mutex_; +}; + +int main() { + // Unfortunately, I cannot compile Boost.Thread on Cygwin with GCC and + // CLang... I get linker errors even if I use `threadapi=pthread` and add + // `/boost/thread//boost_thread` to the Jamfile. +#ifdef BOOST_MSVC + { + a aa; + boost::thread tf([&aa] { aa.f(); }); + boost::thread tg([&aa] { aa.g(); }); + tf.join(); + tg.join(); + } + + std::ostringstream ok; ok // Mutexes also guarantee order of text in `out`. + // Test constructor. + + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "a::ctor::pre" << std::endl + << "b::ctor::pre" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::static_inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::ctor::old" << std::endl + #endif + << "b::ctor::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::ctor::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "a::static_inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::ctor::old" << std::endl + #endif + << "a::ctor::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::ctor::post" << std::endl + #endif + + // Test public functions. + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "b::f::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::f::old" << std::endl + << "a::f::old" << std::endl + #endif + << "a::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::f::old" << std::endl + << "b::f::post" << std::endl + << "a::f::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "b::g::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::g::old" << std::endl + << "a::g::old" << std::endl + #endif + << "a::g::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::g::old" << std::endl + << "b::g::post" << std::endl + << "a::g::post" << std::endl + #endif + + // Test destructor. + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::dtor::old" << std::endl + #endif + << "a::dtor::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "a::static_inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::dtor::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::dtor::old" << std::endl + #endif + << "b::dtor::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "b::static_inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::dtor::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); +#endif + return boost::report_errors(); +} + diff --git a/test/set/no_guard.cpp b/test/specify/no_guard.cpp similarity index 100% rename from test/set/no_guard.cpp rename to test/specify/no_guard.cpp diff --git a/test/set/nothing.cpp b/test/specify/nothing.cpp similarity index 100% rename from test/set/nothing.cpp rename to test/specify/nothing.cpp diff --git a/test/set/old.cpp b/test/specify/old.cpp similarity index 100% rename from test/set/old.cpp rename to test/specify/old.cpp diff --git a/test/set/old_post.cpp b/test/specify/old_post.cpp similarity index 100% rename from test/set/old_post.cpp rename to test/specify/old_post.cpp diff --git a/test/set/old_pre_error.cpp b/test/specify/old_pre_error.cpp similarity index 100% rename from test/set/old_pre_error.cpp rename to test/specify/old_pre_error.cpp diff --git a/test/set/post.cpp b/test/specify/post.cpp similarity index 100% rename from test/set/post.cpp rename to test/specify/post.cpp diff --git a/test/set/post_old_error.cpp b/test/specify/post_old_error.cpp similarity index 100% rename from test/set/post_old_error.cpp rename to test/specify/post_old_error.cpp diff --git a/test/set/post_pre_error.cpp b/test/specify/post_pre_error.cpp similarity index 100% rename from test/set/post_pre_error.cpp rename to test/specify/post_pre_error.cpp diff --git a/test/set/pre.cpp b/test/specify/pre.cpp similarity index 100% rename from test/set/pre.cpp rename to test/specify/pre.cpp diff --git a/test/set/pre_old.cpp b/test/specify/pre_old.cpp similarity index 100% rename from test/set/pre_old.cpp rename to test/specify/pre_old.cpp diff --git a/test/set/pre_old_post.cpp b/test/specify/pre_old_post.cpp similarity index 100% rename from test/set/pre_old_post.cpp rename to test/specify/pre_old_post.cpp diff --git a/test/set/pre_post.cpp b/test/specify/pre_post.cpp similarity index 100% rename from test/set/pre_post.cpp rename to test/specify/pre_post.cpp