diff --git a/README.txt b/README.txt index e05edda..4de6e0c 100644 --- a/README.txt +++ b/README.txt @@ -4,10 +4,11 @@ This library implements Contract Programming (a.k.a., Design by Contract or DbC) for the C++ programming language. All Contract Programming features are supported by this library: subcontracting, -class invariants, postconditions (with old and return values), preconditions, -customizable actions on assertion failure (terminate, throw, etc.), optional -compilation of assertion checking, disable assertion checking while already -checking other assertions (to avoid infinite recursion), etc. +class invariants (also static and volatile), postconditions (with old and return +values), preconditions, customizable actions on assertion failure (terminate, +throw, etc.), optional compilation of assertion checking, disable assertion +checking while already checking other assertions (to avoid infinite recursion), +etc. NOTE: In one of its previous revisions, this library passed Boost formal reviews and it was accepted into the Boost libraries. However, the diff --git a/doc/advanced_topics.qbk b/doc/advanced_topics.qbk index 13fdfb7..5e65afd 100644 --- a/doc/advanced_topics.qbk +++ b/doc/advanced_topics.qbk @@ -8,57 +8,6 @@ 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: -[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. - ... - .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__). - -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). @@ -134,8 +83,43 @@ For example (see also [@../../example/features/named_override.cpp =named_overrid [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 Private and Protected Functions] + +Private and protected member functions do not check class invariants (because they are not part of the public class interface) and they do not subcontract (because they are not accessible at the calling site where the __substitution_principle__ applies, see also __Function_Calls__). +However, programmers can still choose to specify preconditions and postconditions for private and protected member functions if they want to check correctness of implementations and usage of base member functions in derived classes. +Therefore, when programmers decide to specify contracts for private and protected member functions, they can use [funcref boost::contract::function] (like for non-member functions). +For example (see also [@../../example/features/private_protected.cpp =private_protected.cpp=]): + +[import ../example/features/private_protected.cpp] +[private_protected] + +The same considerations made in __Non_Member_Functions__ apply. +See __Constructors__ and __Destructors__ for notes on how to program contracts for private and protected constructors and destructors respectively. + +When private and protected member functions are virtual they should declare the extra parameter of type [classref boost::contract::virtual_]`*` with default value `0` (see __Virtual_Public_Functions__) even if [funcref boost::contract::function] never accepts that as an argument so this parameter will remain unused and it need not a name. +Otherwise, these private and protected virtual functions cannot be overridden by public functions in the derived classes that specifies contracts for the overriding functions (because C++ uses also default parameters to match signatures of overriding functions). +In this case, overriding public functions in derived classes will not specify the extra `override_...` parameter to [funcref boost::contract::public_function] (because the overridden functions are private or protected and not being public they do not participate to subcontracting). +For example (see also [@../../example/features/private_protected_virtual.cpp =private_protected_virtual.cpp=]): + +[import ../example/features/private_protected_virtual.cpp] +[private_protected_virtual_counter] +[private_protected_virtual_counter10] + +Finally, using multiple inheritance it is also possible to override functions that are private or protected from one base but public from another base. +In this case, overriding public functions in derived classes will specify the extra `override_...` parameter to [funcref boost::contract::public_function] (because the overridden functions private or protected in one base and those do not participate to subcontracting, but public in another base and these participate to subcontracting). +For example (see also [@../../example/features/private_protected_virtual_multi.cpp =private_protected_virtual_multi.cpp=]): + +[import ../example/features/private_protected_virtual_multi.cpp] +[private_protected_virtual_multi_counter] +[private_protected_virtual_multi_countable] +[private_protected_virtual_multi_counter10] + +[warning +Unfortunately, the code above does not compile on MSVC (at least up to Visual Studio 2015) because MSVC incorrectly gives a compile-time error when SFINAE fails due to private or protected access levels. +Instead, GCC and Clang correctly implement SFINAE failures due to private and protected member functions so they successfully compile the code above. +] [endsect] @@ -275,6 +259,57 @@ As usual, static class invariants can also be specified (see __Static_Class_Inva [endsect] +[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: +[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. + ... + .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__). + +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 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. diff --git a/doc/contract.qbk b/doc/contract.qbk index 659d9f1..9dd8003 100644 --- a/doc/contract.qbk +++ b/doc/contract.qbk @@ -104,7 +104,7 @@ This library implements Design by Contract (DbC) is a registered trademark of [@http://en.wikipedia.org/wiki/Eiffel_Software Eiffel Software] and it was first introduced by the Eiffel programming language (see __Meyer97__). ] for the C++ programming language. -All Contract Programming features are supported by this library: subcontracting, class invariants, postconditions (with old and return values), preconditions, customizable actions on assertion failure (terminate, throw, etc.), optional compilation of assertion checking, disable assertion checking while already checking other assertions (to avoid infinite recursion), etc. +All Contract Programming features are supported by this library: subcontracting, class invariants (also static and volatile), postconditions (with old and return values), preconditions, customizable actions on assertion failure (terminate, throw, etc.), optional compilation of assertion checking, disable assertion checking while already checking other assertions (to avoid infinite recursion), etc. Hosted at: [@https://github.com/lcaminiti/boost-contract] diff --git a/doc/tutorial.qbk b/doc/tutorial.qbk index 31d3879..8c94312 100644 --- a/doc/tutorial.qbk +++ b/doc/tutorial.qbk @@ -621,21 +621,5 @@ A static public member function can avoid calling [funcref boost::contract::publ [endsect] -[section Private and Protected Functions] - -Private and protected member functions do not check class invariants (because they are not part of the public class interface) and they do not subcontract (because they are not accessible at the calling site where the __substitution_principle__ applies, see also __Function_Calls__). -However, programmers can still choose to specify preconditions and postconditions for private and protected member functions if they want to check correctness of implementations and usage of base member functions in derived classes. -Therefore, when programmers decide to specify contracts for private and protected member functions, they can use [funcref boost::contract::function] (like for non-member functions). -For example (see also [@../../example/features/private_protected.cpp =private_protected.cpp=]): - -[import ../example/features/private_protected.cpp] -[private_protected] - -The same considerations made in __Non_Member_Functions__ apply. - -See __Constructors__ and __Destructors__ for notes on how to program contracts for private and protected constructors and destructors respectively. - -[endsect] - [endsect] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index dd682e4..bb1b906 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -15,6 +15,10 @@ test-suite features : [ subdir-run features : base_types ] [ subdir-run features : static_public ] [ subdir-run features : private_protected ] + [ subdir-run features : private_protected_virtual ] + [ subdir-run features : private_protected_virtual_multi ] + [ subdir-run features : friend ] + [ subdir-run features : check ] [ subdir-run features : old ] [ subdir-run features : optional_result ] diff --git a/example/features/check.cpp b/example/features/check.cpp new file mode 100644 index 0000000..b5501ad --- /dev/null +++ b/example/features/check.cpp @@ -0,0 +1,51 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +#include +#include + +int gcd(int const a, int const b) { + int result; + boost::contract::check c = boost::contract::function() + .precondition([&] { + BOOST_CONTRACT_ASSERT(a > 0); + BOOST_CONTRACT_ASSERT(b > 0); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(result <= a); + BOOST_CONTRACT_ASSERT(result <= b); + }) + ; + + // Function body follows... + + //[check_class + int x = a, y = b; + boost::contract::check c1 = [&] { // Body checks with functor. + BOOST_CONTRACT_ASSERT(x == a); + BOOST_CONTRACT_ASSERT(y == b); + }; + //] + + //[check_macro + while(x != y) { + BOOST_CONTRACT_CHECK(x > 0); // Body checks with macro (preferred). + BOOST_CONTRACT_CHECK(y > 0); + + if(x > y) x = x - y; + else y = y - x; + } + //] + + return result = x; +} + +int main() { + assert(gcd(12, 28) == 4); + assert(gcd(4, 14) == 2); + return 0; +} + diff --git a/example/features/check_if.cpp b/example/features/condition_if.cpp similarity index 88% rename from example/features/check_if.cpp rename to example/features/condition_if.cpp index 228f388..873fc04 100644 --- a/example/features/check_if.cpp +++ b/example/features/condition_if.cpp @@ -10,16 +10,16 @@ #include #include -//[check_if +//[condition_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 `==`. + // Instead of `ASSERT(back() == value)` for T without `==`. BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(std::equal_to(), boost::cref(back()), boost::cref(value) diff --git a/example/features/friend.cpp b/example/features/friend.cpp new file mode 100644 index 0000000..97fd531 --- /dev/null +++ b/example/features/friend.cpp @@ -0,0 +1,68 @@ + +#include +#include +#include + +class y; +class z; + +class x { +public: + void invariant() const { + std::cout << "x::inv" << std::endl; + BOOST_CONTRACT_ASSERT(get() >= 0); + } + + x() : value_(0) {} + int get() const { return value_; } + friend void set_all(x&, y&, int value); + +private: + int value_; +}; + +class y { +public: + void invariant() const { + std::cout << "y::inv" << std::endl; + BOOST_CONTRACT_ASSERT(get() >= 0); + } + + y() : value_(0) {} + int get() const { return value_; } + friend void set_all(x&, y&, int value); + +private: + int value_; +}; + +void set_all(x& a, y& b, int value) { + boost::contract::guard post = boost::contract::function() + .postcondition([&] { + std::cout << "f::post" << std::endl; + BOOST_CONTRACT_ASSERT(a.get() == value); + BOOST_CONTRACT_ASSERT(b.get() == value); + }) + ; + boost::contract::guard inv_b = boost::contract::public_function(&b); + boost::contract::guard inv_a = boost::contract::public_function(&a); + boost::contract::guard pre = boost::contract::function() + .precondition([&] { + std::cout << "f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(value > 0); + }) + ; + + std::cout << "f::body" << std::endl; + a.value_ = b.value_ = value; +} + +int main() { + x a; + y b; + set_all(a, b, 123); + assert(a.get() == 123); + assert(b.get() == 123); + return 1; +} + diff --git a/example/features/introduction.cpp b/example/features/introduction.cpp index 5ad07cd..30cc9eb 100644 --- a/example/features/introduction.cpp +++ b/example/features/introduction.cpp @@ -39,7 +39,7 @@ public: }) ; - vect_.push_back(value); + vect_.push_back(value); // Function body. } BOOST_CONTRACT_OVERRIDE(push_back) // For `override_push_back`. diff --git a/example/features/old.cpp b/example/features/old.cpp index 32cf9e0..1ccfc80 100644 --- a/example/features/old.cpp +++ b/example/features/old.cpp @@ -18,7 +18,7 @@ char replace(std::string& s, std::size_t index, char x) { BOOST_CONTRACT_ASSERT(index < s.size()); }) .old([&] { // ...after preconditions (and invariants) checked. - old_y = BOOST_CONTRACT_OLDOF(s[index]); + old_y = BOOST_CONTRACT_OLD(s[index]); }) .postcondition([&] { BOOST_CONTRACT_ASSERT(s[index] == x); diff --git a/example/features/old_noncopyable.cpp b/example/features/old_if_copyable.cpp similarity index 92% rename from example/features/old_noncopyable.cpp rename to example/features/old_if_copyable.cpp index 9bdcb4b..5fe3c3f 100644 --- a/example/features/old_noncopyable.cpp +++ b/example/features/old_if_copyable.cpp @@ -7,12 +7,12 @@ #include #include -//[old_noncopyable +//[old_if_copyable template void accumulate(T& total, T const& x) { // No compiler error if T has no copy constructor... - boost::contract::old_ptr_noncopyable old_total = - BOOST_CONTRACT_OLDOF(total); + boost::contract::old_ptr_if_copyable old_total = + BOOST_CONTRACT_OLD(total); boost::contract::guard c = boost::contract::function() .postcondition([&] { // ...but old value null if T has no copy constructor. diff --git a/example/features/old_no_macros.cpp b/example/features/old_no_macros.cpp index 5bd4d7e..5a6bd00 100644 --- a/example/features/old_no_macros.cpp +++ b/example/features/old_no_macros.cpp @@ -13,7 +13,7 @@ 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. + // Program old value instead of using `OLD(size())` macro. boost::contract::old_ptr old_size = boost::contract::make_old(v, boost::contract::copy_old(v) ? size() : boost::contract::null_old()) diff --git a/example/features/private_protected.cpp b/example/features/private_protected.cpp index 69495a0..d87a15d 100644 --- a/example/features/private_protected.cpp +++ b/example/features/private_protected.cpp @@ -10,54 +10,71 @@ //[private_protected class counter { -protected: - int get() const { // Protected function (like non-member functions). - int result; + // Private and protected functions use `function()` like non-members. + +private: + int n_; + + void dec() { + boost::contract::old_ptr old_get = BOOST_CONTRACT_OLDOF(get()); boost::contract::guard c = boost::contract::function() + .precondition([&] { + BOOST_CONTRACT_ASSERT( + get() + 1 >= std::numeric_limits::min()); + }) .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == *old_get - 1); + }) + ; + + set(get() - 1); + } + +protected: + virtual void set(int n, boost::contract::virtual_* = 0) { + boost::contract::guard c = boost::contract::function() + .precondition([&] { + BOOST_CONTRACT_ASSERT(n <= 0); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == n); + }) + ; + + n_ = n; + } + + /* ... */ +//] + +public: + virtual int get(boost::contract::virtual_* v = 0) const { + int result; + boost::contract::guard c = boost::contract::public_function( + v, result, this) + .postcondition([&] (int const result) { + BOOST_CONTRACT_ASSERT(result <= 0); BOOST_CONTRACT_ASSERT(result == n_); }) ; - return result = n_; // Function body. + return result = n_; } -private: - void dec() { // Private function (like non-member functions). - boost::contract::old_ptr old_n = BOOST_CONTRACT_OLDOF(n_); - boost::contract::guard c = boost::contract::function() - .precondition([&] { - BOOST_CONTRACT_ASSERT(n_ > std::numeric_limits::min()); - }) - .postcondition([&] { - BOOST_CONTRACT_ASSERT(n_ == *old_n - 1); - }) - ; - - --n_; // Function body. - } - - int n_; + counter() : n_(0) {} // Should contract constructor and destructor too. - /* ... */ -//] - - friend struct test_counter; -public: - counter() : n_(0) {} -}; - -struct test_counter { - static void run() { - counter cnt; - assert(cnt.get() == 0); - cnt.dec(); - assert(cnt.get() == -1); + void invariant() const { + BOOST_CONTRACT_ASSERT(get() <= 0); } + + friend int main(); }; int main() { - test_counter::run(); + counter cnt; + assert(cnt.get() == 0); + cnt.dec(); + assert(cnt.get() == -1); return 0; } diff --git a/example/features/private_protected_virtual.cpp b/example/features/private_protected_virtual.cpp new file mode 100644 index 0000000..a3d23c8 --- /dev/null +++ b/example/features/private_protected_virtual.cpp @@ -0,0 +1,146 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +#include +#include +#include + +//[private_protected_virtual_counter +class counter { + // Private and protected virtual functions declare extra `virtual_* = 0` + // parameter (otherwise they cannot be overridden). + +private: + int n_; + + virtual void dec(boost::contract::virtual_* = 0) { + boost::contract::old_ptr old_get = BOOST_CONTRACT_OLDOF(get()); + boost::contract::guard c = boost::contract::function() + .precondition([&] { + BOOST_CONTRACT_ASSERT( + get() + 1 >= std::numeric_limits::min()); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == *old_get - 1); + }) + ; + + set(get() - 1); + } + +protected: + virtual void set(int n, boost::contract::virtual_* = 0) { + boost::contract::guard c = boost::contract::function() + .precondition([&] { + BOOST_CONTRACT_ASSERT(n <= 0); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == n); + }) + ; + + n_ = n; + } + + /* ... */ +//] + +public: + virtual int get(boost::contract::virtual_* v = 0) const { + int result; + boost::contract::guard c = boost::contract::public_function( + v, result, this) + .postcondition([&] (int const result) { + BOOST_CONTRACT_ASSERT(result <= 0); + BOOST_CONTRACT_ASSERT(result == n_); + }) + ; + + return result = n_; + } + + counter() : n_(0) {} // Should contract constructor and destructor too. + + void invariant() const { + BOOST_CONTRACT_ASSERT(get() <= 0); + } + + friend int main(); +}; + +//[private_protected_virtual_counter10 +class counter10 + #define BASES public counter + : BASES +{ +public: + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + // Not overriding from public members so no `override_...`. + + virtual void dec(boost::contract::virtual_* v = 0) { + boost::contract::old_ptr old_get = BOOST_CONTRACT_OLDOF(v, get()); + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT( + get() + 10 >= std::numeric_limits::min()); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == *old_get - 10); + }) + ; + + set(get() - 10); + } + + virtual void set(int n, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(n % 10 == 0); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == n); + }) + ; + + counter::set(n); + } + + /* ... */ +//] + + virtual int get(boost::contract::virtual_* v = 0) const { + int result; + boost::contract::guard c = boost::contract::public_function< + override_get>(v, result, &counter10::get, this); + + return result = counter::get(); + } + BOOST_CONTRACT_OVERRIDE(get) + + // Should contract default constructor and destructor too. + + void invariant() const { + BOOST_CONTRACT_ASSERT(get() % 10 == 0); + } +}; + +int main() { + counter cnt; + assert(cnt.get() == 0); + cnt.dec(); + assert(cnt.get() == -1); + + counter10 cnt10; + counter& b = cnt10; // Polymorphic calls. + assert(b.get() == 0); + b.dec(); + assert(b.get() == -10); + + return 0; +} + diff --git a/example/features/private_protected_virtual_multi.cpp b/example/features/private_protected_virtual_multi.cpp new file mode 100644 index 0000000..bc85e2e --- /dev/null +++ b/example/features/private_protected_virtual_multi.cpp @@ -0,0 +1,214 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +#include +#ifdef BOOST_MSVC + +// WARNING: MSVC (at least up to VS 2015) gives a compile-time error if SFINAE +// cannot introspect a member because of its private or protected access level. +// That is incorrect, SFINAE should fail in these cases without generating +// compile-time errors like GCC and CLang do. Therefore, currently it is not +// possible to override a member that is public in one base but private or +// protected in other base using this library on MSVC (that can be done instead +// using this library on GCC or CLang). +int main() { return 0; } // Trivial program for MSVC. + +#else + +#include +#include +#include + +//[private_protected_virtual_multi_counter +class counter { + // Private and protected virtual functions declare extra `virtual_* = 0` + // parameter (otherwise they cannot be overridden). + +private: + int n_; + + virtual void dec(boost::contract::virtual_* = 0) { + boost::contract::old_ptr old_get = BOOST_CONTRACT_OLDOF(get()); + boost::contract::guard c = boost::contract::function() + .precondition([&] { + BOOST_CONTRACT_ASSERT( + get() + 1 >= std::numeric_limits::min()); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == *old_get - 1); + }) + ; + + set(get() - 1); + } + +protected: + virtual void set(int n, boost::contract::virtual_* = 0) { + boost::contract::guard c = boost::contract::function() + .precondition([&] { + BOOST_CONTRACT_ASSERT(n <= 0); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == n); + }) + ; + + n_ = n; + } + + /* ... */ +//] + +public: + virtual int get(boost::contract::virtual_* v = 0) const { + int result; + boost::contract::guard c = boost::contract::public_function( + v, result, this) + .postcondition([&] (int const result) { + BOOST_CONTRACT_ASSERT(result <= 0); + BOOST_CONTRACT_ASSERT(result == n_); + }) + ; + + return result = n_; + } + + counter() : n_(0) {} // Should contract constructor and destructor too. + + void invariant() const { + BOOST_CONTRACT_ASSERT(get() <= 0); + } + + friend int main(); +}; + +//[private_protected_virtual_multi_countable +class countable { +public: + void invariant() const { + BOOST_CONTRACT_ASSERT(get() <= 0); + } + + virtual void dec(boost::contract::virtual_* v = 0) = 0; + virtual void set(int n, boost::contract::virtual_* v = 0) = 0; + virtual int get(boost::contract::virtual_* v = 0) const = 0; +}; + +/* ... */ +//] + +void countable::dec(boost::contract::virtual_* v) { + boost::contract::old_ptr old_get = BOOST_CONTRACT_OLDOF(v, get()); + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(get() > std::numeric_limits::min()); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() < *old_get); + }) + ; + assert(false); // Never executed by this library. +} + +void countable::set(int n, boost::contract::virtual_* v) { + boost::contract::guard c = boost::contract::public_function( + v, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT(n <= 0); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == n); + }) + ; + assert(false); // Never executed by this library. +} + +int countable::get(boost::contract::virtual_* v) const { + int result; + boost::contract::guard c = boost::contract::public_function( + v, result, this); + assert(false); // Never executed by this library. +} + +//[private_protected_virtual_multi_counter10 +class counter10 + #define BASES public countable, public counter + : BASES +{ +public: + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + // Overriding from public members from `countable` so use `override_...`. + + virtual void dec(boost::contract::virtual_* v = 0) { + boost::contract::old_ptr old_get = BOOST_CONTRACT_OLDOF(v, get()); + boost::contract::guard c = boost::contract::public_function< + override_dec>(v, &counter10::dec, this) + .precondition([&] { + BOOST_CONTRACT_ASSERT( + get() + 10 >= std::numeric_limits::min()); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == *old_get - 10); + }) + ; + + set(get() - 10); + } + + virtual void set(int n, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function< + override_set>(v, &counter10::set, this, n) + .precondition([&] { + BOOST_CONTRACT_ASSERT(n % 10 == 0); + }) + .postcondition([&] { + BOOST_CONTRACT_ASSERT(get() == n); + }) + ; + + counter::set(n); + } + + BOOST_CONTRACT_OVERRIDES(dec, set) + + /* ... */ +//] + + virtual int get(boost::contract::virtual_* v = 0) const { + int result; + boost::contract::guard c = boost::contract::public_function< + override_get>(v, result, &counter10::get, this); + + return result = counter::get(); + } + BOOST_CONTRACT_OVERRIDE(get) + + // Should contract default constructor and destructor too. + + void invariant() const { + BOOST_CONTRACT_ASSERT(get() % 10 == 0); + } +}; + +int main() { + counter cnt; + assert(cnt.get() == 0); + cnt.dec(); + assert(cnt.get() == -1); + + counter10 cnt10; + countable& b = cnt10; // Polymorphic calls. + assert(b.get() == 0); + b.dec(); + assert(b.get() == -10); + + return 0; +} + +#endif // MSVC + diff --git a/example/n1962/vector.cpp b/example/n1962/vector.cpp index 49ce9f9..363eb92 100644 --- a/example/n1962/vector.cpp +++ b/example/n1962/vector.cpp @@ -77,7 +77,7 @@ public: .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == count); BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(all_of_equal_to(), begin(), end(), T()) ) ); @@ -90,7 +90,7 @@ public: .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == count); BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(all_of_equal_to(), begin(), end(), boost::cref(value)) ) @@ -105,7 +105,7 @@ public: .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == count); BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(all_of_equal_to(), begin(), end(), boost::cref(value)) ) @@ -141,7 +141,7 @@ public: boost::contract::guard c = boost::contract::constructor(this) .postcondition([&] { BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(std::equal_to >(), boost::cref(*this), boost::cref(other)) ) @@ -155,13 +155,13 @@ public: boost::contract::guard c = boost::contract::public_function(this) .postcondition([&] { BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(std::equal_to >(), boost::cref(*this), boost::cref(other)) ) ); BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(std::equal_to >(), boost::cref(*result), boost::cref(*this)) ) @@ -267,13 +267,13 @@ public: void resize(size_type count, T const& value = T()) { boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::guard c = boost::contract::public_function(this) .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == count); if(count > *old_size) { BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(all_of_equal_to(), begin() + *old_size, end(), boost::cref(value)) ) @@ -397,9 +397,9 @@ public: void push_back(T const& value) { boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::old_ptr old_capacity = - BOOST_CONTRACT_OLDOF(capacity()); + BOOST_CONTRACT_OLD(capacity()); boost::contract::guard c = boost::contract::public_function(this) .precondition([&] { BOOST_CONTRACT_ASSERT(size() < max_size()); @@ -408,7 +408,7 @@ public: BOOST_CONTRACT_ASSERT(size() == *old_size + 1); BOOST_CONTRACT_ASSERT(capacity() >= *old_capacity); BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(std::equal_to(), boost::cref(back()), boost::cref(value)) ) @@ -421,7 +421,7 @@ public: void pop_back() { boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::guard c = boost::contract::public_function(this) .precondition([&] { BOOST_CONTRACT_ASSERT(!empty()); @@ -454,7 +454,7 @@ public: }) .postcondition([&] { BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(all_of_equal_to(), begin(), end(), boost::cref(value)) ) @@ -468,7 +468,7 @@ public: iterator insert(iterator where, T const& value) { iterator result; boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::guard c = boost::contract::public_function(this) .precondition([&] { BOOST_CONTRACT_ASSERT(size() < max_size()); @@ -476,11 +476,11 @@ public: .postcondition([&] { BOOST_CONTRACT_ASSERT(size() == *old_size + 1); BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(std::equal_to(), boost::cref(*result), boost::cref(value)) ) - // if(capacity() > oldof capacity()) + // if(capacity() > old(capacity())) // [begin(), end()) is invalid // else // [where, end()) is invalid @@ -493,11 +493,11 @@ public: void insert(iterator where, size_type count, T const& value) { boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::old_ptr old_capacity = - BOOST_CONTRACT_OLDOF(capacity()); + BOOST_CONTRACT_OLD(capacity()); boost::contract::old_ptr old_where = - BOOST_CONTRACT_OLDOF(where); + BOOST_CONTRACT_OLD(where); boost::contract::guard c = boost::contract::public_function(this) .precondition([&] { BOOST_CONTRACT_ASSERT(size() + count < max_size()); @@ -507,7 +507,7 @@ public: BOOST_CONTRACT_ASSERT(capacity() >= *old_capacity); if(capacity() == *old_capacity) { BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(all_of_equal_to(), boost::prior(*old_where), boost::prior(*old_where) + count, @@ -527,9 +527,9 @@ public: template void insert(iterator where, InputIter first, InputIter last) { boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::old_ptr old_capacity = - BOOST_CONTRACT_OLDOF(capacity()); + BOOST_CONTRACT_OLD(capacity()); boost::contract::guard c = boost::contract::public_function(this) .precondition([&] { BOOST_CONTRACT_ASSERT(size() + std::distance(first, last) < @@ -549,7 +549,7 @@ public: iterator erase(iterator where) { iterator result; boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::guard c = boost::contract::public_function(this) .precondition([&] { BOOST_CONTRACT_ASSERT(!empty()); @@ -568,7 +568,7 @@ public: iterator erase(iterator first, iterator last) { iterator result; boost::contract::old_ptr old_size = - BOOST_CONTRACT_OLDOF(size()); + BOOST_CONTRACT_OLD(size()); boost::contract::guard c = boost::contract::public_function(this) .precondition([&] { BOOST_CONTRACT_ASSERT(size() >= std::distance(first, last)); @@ -595,20 +595,19 @@ public: } void swap(vector& other) { - boost::contract::old_ptr old_me = BOOST_CONTRACT_OLDOF(*this); - boost::contract::old_ptr old_other = - BOOST_CONTRACT_OLDOF(other); + boost::contract::old_ptr old_me = BOOST_CONTRACT_OLD(*this); + boost::contract::old_ptr old_other = BOOST_CONTRACT_OLD(other); boost::contract::guard c = boost::contract::public_function(this) .postcondition([&] { BOOST_CONTRACT_ASSERT( - boost::contract::check_if > >( boost::bind(std::equal_to >(), boost::cref(*this), boost::cref(*old_other)) ) ); BOOST_CONTRACT_ASSERT( - boost::contract::check_if > >( boost::bind(std::equal_to >(), boost::cref(other), boost::cref(*old_me)) diff --git a/include/boost/contract.hpp b/include/boost/contract.hpp index 23e6dc8..6232a87 100644 --- a/include/boost/contract.hpp +++ b/include/boost/contract.hpp @@ -25,6 +25,24 @@ never be used directly by programmers. @see @RefSect{getting_started, Getting Started} */ +// TODO: Document that boost::contract::function() can be used to program contracts for lambda functions. And also "abused" a bit to program pre/postconditions for any arbitrary scope of code in function body. + +// TODO: Document that friends do not in general check invariants so their contracts are usually programmed using function(). But if a function friend of an object takes an instance of that object as a parameter and therefore is essentially part of the object's public API, then programmers can make that explicit by using public_function(obj) after function() to program the friend function contract (but note that in general friends functions can take instances of multiple different objects because the same function can be friend of different classes). + +// TODO: Document that noexcept (and exception throw(...)) specifications of the enclosing function apply to contracts. So if a contract handler is set so contract failures throw, noexcept function will still not throw, they will always terminate (because that's what users of such functions except, even if the function fails in any way, including the function contract fails). + +// TODO: Document that if installing contract failure handlers that throw, it might be a good idea to check invariants at exit. That way the destructor entry invariant should *in theory* never fail (unless not all public functions, ctors are contracted, or because of non-contracted friends, or because of public data members... all those can still break the invariant before the dtor is called and before this lib will detect the invariants have failed so the dtor entry inv will fail... and what will dtor do in that case? terminate?). + +// TODO: Document (in Getting Started) that some example source code has `//[...` and `//]` tags used to import example code in Quickbook docs (so doc's examples always up to date with code). + +// TODO: Add a macro ALL_DISABLE_NOTHING to turn-off disabling assertions within assertions for all contracts, not just preconditions (do I still need PRECONDITIONS_DISABLE_NOTHING then?). + +// TODO: Consider a better name for contract::detail::check_guard (it's confusing with old contract::guard and with current check_base::guard)... maybe I can call it contract::detail::disable? + +// TODO: Consider using a trait like boost::contract::is_copy_constructible (or has_old) instead of boost::is_copy_constructible directly (so programmers can specialize it to avoid old copies of specify types T without affecting other parts of the program that might be using boost::is_copy_constructible). Then maybe I should also introduce a trait to make the copy instead of directly using the copy constructor... so to allow for maximum customization.. + +// TODO: Add .except(...) that is checked at function exit when body throws (in contrast to postconditions). result is NOT accessible in .except's functor because function threw. Old values are accessible in both post and except so OLDOF copies disabled on if both except and post checking disabled (NO_EXCEPTS && NO_POSTCONDITIONS). except will have its own failure handler except_failure, check disabling macro NO_EXCEPTS, etc. + #include #include #include @@ -32,12 +50,10 @@ never be used directly by programmers. #include #include #include -#include +#include #include #include #include -// TODO: Document that the correct way of programming contracts for overridden protected and overriding public functions is as follows: Both must use virtual_ (otherwise C++ won't override because mismatching parameters), but overridden protected does not use public_function. See test/public_function/protected.cpp. - #endif // #include guard diff --git a/include/boost/contract/assert.hpp b/include/boost/contract/assert.hpp index ec9ef9c..dfc3dab 100644 --- a/include/boost/contract/assert.hpp +++ b/include/boost/contract/assert.hpp @@ -18,12 +18,9 @@ Facility to assert contract conditions. #if !defined(BOOST_CONTRACT_NO_PRECONDITIONS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_INVARIANTS) - #include - // Ternary operator used so this macro expand to an expression and it can - // be used with `if` and all other statements. + #include #define BOOST_CONTRACT_ASSERT(condition) \ - ((condition) ? (void*)0 : throw boost::contract::assertion_failure( \ - __FILE__, __LINE__, BOOST_PP_STRINGIZE(condition))) + { BOOST_CONTRACT_DETAIL_ASSERT(condition); } #else /** Preferred way to assert contract conditions. @@ -47,7 +44,7 @@ Facility to assert contract conditions. @see @RefSect{tutorial, Tutorial} @param condition The contract condition being checked. */ - #define BOOST_CONTRACT_ASSERT(condition) /* nothing */ + #define BOOST_CONTRACT_ASSERT(condition) {} #endif #endif // #include guard diff --git a/include/boost/contract/call_if.hpp b/include/boost/contract/call_if.hpp index 0631305..8b1dde1 100644 --- a/include/boost/contract/call_if.hpp +++ b/include/boost/contract/call_if.hpp @@ -12,10 +12,10 @@ Statically disable compilation and execution of functor calls. */ // Do not include all_core_headers here (call_if is essentially standalone). -#include #include #include #include +#include #include /* PRIVATE */ @@ -420,11 +420,13 @@ static condition is true, otherwise trivially return @c true. @return Boolean value returned by @c f() if the static condition if true, otherwise simply return @c true (i.e., check trivially passed). */ -template -bool check_if_c(Check f) { - return call_if_c(f).else_( - boost::contract::detail::always_true()); -} +template +typename boost::enable_if_c::type +condition_if_c(Then f, bool else_ = true) { return f(); } + +template +typename boost::disable_if_c::type +condition_if_c(Then f, bool else_ = true) { return else_; } /** Select compilation and execution of a boolean functor check via a static boolean @@ -439,10 +441,9 @@ static condition is true, otherwise trivially return @c true. @return Boolean value returned by @c f() if the static condition if true, otherwise simply return @c true (i.e., check trivially passed). */ -template -bool check_if(Check f) { - return call_if_c(f).else_( - boost::contract::detail::always_true()); +template +bool condition_if(Then f, bool else_ = true) { + return condition_if_c(f, else_); } } } // namespace diff --git a/include/boost/contract/guard.hpp b/include/boost/contract/check.hpp similarity index 76% rename from include/boost/contract/guard.hpp rename to include/boost/contract/check.hpp index f01b65d..d983155 100644 --- a/include/boost/contract/guard.hpp +++ b/include/boost/contract/check.hpp @@ -1,6 +1,6 @@ -#ifndef BOOST_CONTRACT_GUARD_HPP_ -#define BOOST_CONTRACT_GUARD_HPP_ +#ifndef BOOST_CONTRACT_CHECK_HPP_ +#define BOOST_CONTRACT_CHECK_HPP_ // Copyright (C) 2008-2016 Lorenzo Caminiti // Distributed under the Boost Software License, Version 1.0 (see accompanying @@ -13,8 +13,11 @@ RAII object to check contracts. #include #include +#include +#include #include #include +#include #include /* PRIVATE */ @@ -24,18 +27,39 @@ RAII object to check contracts. #if !defined(BOOST_CONTRACT_NO_PRECONDITIONS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_INVARIANTS) - #define BOOST_CONTRACT_GUARD_CTOR_DEF_(contract_type) \ + #define BOOST_CONTRACT_CHECK_CTOR_DEF_(contract_type) \ : check_(const_cast(contract).check_.release()) \ { \ BOOST_CONTRACT_DETAIL_DEBUG(check_); \ check_->guard(); \ } #else - #define BOOST_CONTRACT_GUARD_CTOR_DEF_(contract_type) {} + #define BOOST_CONTRACT_CHECK_CTOR_DEF_(contract_type) {} +#endif + +#ifndef BOOST_CONTRACT_NO_CHECKS + #define BOOST_CONTRACT_CHECK_(assertion) \ + { \ + try { \ + if(!boost::contract::detail::check_guard::checking()) { \ + /* this name somewhat unique to min var shadow warnings */ \ + boost::contract::detail::check_guard \ + BOOST_CONTRACT_DETAIL_NAME2(checking, __LINE__); \ + { assertion; } \ + } \ + } catch(...) { boost::contract::check_failure(); } \ + } +#else + #define BOOST_CONTRACT_CHECK_(assertion) {} #endif /** @endcond */ +/* PUBLIC */ + +#define BOOST_CONTRACT_CHECK(condition) \ + BOOST_CONTRACT_CHECK_(BOOST_CONTRACT_DETAIL_ASSERT(condition)) + /* CODE */ namespace boost { namespace contract { @@ -52,19 +76,23 @@ throw an exception, static invariants are always checked at entry and exit, etc. Contract Programming Overview}). @see @RefSect{tutorial, Tutorial} */ -class guard { // Copy ctor only (as move via ptr release). +class check { // Copy ctor only (as move via ptr release). public: + // f must be a valid callable object (not null func ptr, empty ftor, etc. + template // Cannot check `if(f) ...` as f can be a lambda. + /* implicit */ check(F const& f) { BOOST_CONTRACT_CHECK_({ f(); }) } + /** Construct this object copying it from the specified one. This object will check the contract, the copied-from object will not (i.e., contract checking ownership is transfered from the copied object to this object). */ - guard(guard const& other) // Copy ctor moves check_ pointer to dest. + check(check const& other) // Copy ctor moves check_ pointer to dest. #if !defined(BOOST_CONTRACT_NO_PRECONDITIONS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_INVARIANTS) - : check_(const_cast(other).check_.release()) + : check_(const_cast(other).check_.release()) #endif {} @@ -84,10 +112,10 @@ public: otherwise this is always @c void. */ template - /* implicit */ guard(specify_precondition_old_postcondition + /* implicit */ check(specify_precondition_old_postcondition const& contract) #ifndef DOXYGEN - BOOST_CONTRACT_GUARD_CTOR_DEF_( + BOOST_CONTRACT_CHECK_CTOR_DEF_( specify_precondition_old_postcondition) #else ; @@ -110,10 +138,10 @@ public: otherwise this is always @c void. */ template - /* implicit */ guard(specify_old_postcondition const& + /* implicit */ check(specify_old_postcondition const& contract) #ifndef DOXYGEN - BOOST_CONTRACT_GUARD_CTOR_DEF_(specify_old_postcondition) + BOOST_CONTRACT_CHECK_CTOR_DEF_(specify_old_postcondition) #else ; #endif @@ -135,10 +163,10 @@ public: otherwise this is always @c void. */ template - /* implicit */ guard(specify_postcondition_only const& + /* implicit */ check(specify_postcondition_only const& contract) #ifndef DOXYGEN - BOOST_CONTRACT_GUARD_CTOR_DEF_( + BOOST_CONTRACT_CHECK_CTOR_DEF_( specify_postcondition_only) #else ; @@ -158,9 +186,9 @@ public: @RefFunc{boost::contract::set_precondition_failure}, etc.). @param contract Contract to be checked. */ - /* implicit */ guard(specify_nothing const& contract) + /* implicit */ check(specify_nothing const& contract) #ifndef DOXYGEN - BOOST_CONTRACT_GUARD_CTOR_DEF_(specify_nothing) + BOOST_CONTRACT_CHECK_CTOR_DEF_(specify_nothing) #else ; #endif @@ -176,11 +204,11 @@ public: terminating the program (see @RefFunc{boost::contract::set_postcondition_failure}, etc.). */ - ~guard() BOOST_NOEXCEPT_IF(false) {} // Allow auto_ptr dtor to throw. + ~check() BOOST_NOEXCEPT_IF(false) {} // Allow auto_ptr dtor to throw. /** @cond */ private: - guard& operator=(guard const&); // Cannot copy outside of `guard c = ...`. + check& operator=(check const&); // Cannot copy outside of `check c = ...`. #if !defined(BOOST_CONTRACT_NO_PRECONDITIONS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ diff --git a/include/boost/contract/core/config.hpp b/include/boost/contract/core/config.hpp index 60f47ef..0ac0f87 100644 --- a/include/boost/contract/core/config.hpp +++ b/include/boost/contract/core/config.hpp @@ -156,9 +156,9 @@ Facilities to configure this library compile-time and run-time behaviour. #ifdef DOXYGEN /** Code block to execute if contracts are not assigned to a - @RefClass{boost::contract::guard} variable (undefined by default). + @RefClass{boost::contract::check} variable (undefined by default). In general, there is a logic error in the program when contracts are not - assigned to a local variable of type @RefClass{boost::contract::guard}. + assigned to a local variable of type @RefClass{boost::contract::check}. Therefore, by default (i.e., when this macro is not defined) this library calls @c assert with a @c false condition in such cases. If this macro is defined, this library will execute the code expanded by the macro instead of @@ -267,7 +267,7 @@ Facilities to configure this library compile-time and run-time behaviour. // Following are NOT configuration macros. -// Ctor pre checked separately and outside guard so not part of this #define. +// Ctor pre checked separately and outside RAII so not part of this #define. #ifdef BOOST_CONTRACT_NO_CONSTRUCTORS #error "define NO_ENTRY_INVARIANTS, NO_EXIT_INVARIANTS, and NO_POSTCONDITIONS instead" #elif defined(BOOST_CONTRACT_NO_POSTCONDITIONS) && \ diff --git a/include/boost/contract/core/exception.hpp b/include/boost/contract/core/exception.hpp index 6e85d35..d013a43 100644 --- a/include/boost/contract/core/exception.hpp +++ b/include/boost/contract/core/exception.hpp @@ -127,6 +127,9 @@ public: @param line Number of the line containing the assertion (usually set using __LINE__). @param code Text listing the source code of the asserted condition. + @note No need to support __func__ because it will trivially expand + to operator() for lambdas functions (which are typically used to + specify contract assertions). */ explicit assertion_failure(char const* const file = "", unsigned long const line = 0, char const* const code = ""); @@ -220,7 +223,20 @@ returning @c void and taking a single parameter of type also lambdas, binds, etc.). @see @RefSect{advanced_topics, Advanced Topics} */ -typedef boost::function assertion_failure_handler; +typedef boost::function from_failure_handler; + +typedef boost::function failure_handler; + +failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +set_check_failure(failure_handler const& f) + /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; + +failure_handler +/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +get_check_failure() /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; + +void /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +check_failure() /* can throw */; /** Set the precondition failure handler. @@ -231,9 +247,8 @@ Set a new precondition failure handler and return the old one. @return Old precondition failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ -set_precondition_failure(assertion_failure_handler const& f) +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +set_precondition_failure(from_failure_handler const& f) /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; /** @@ -244,8 +259,7 @@ This is often called only internally by this library. @return Precondition failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ get_precondition_failure() /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; @@ -271,9 +285,8 @@ Set a new postcondition failure handler and return the old one. @return Old postcondition failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ -set_postcondition_failure(assertion_failure_handler const& f) +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +set_postcondition_failure(from_failure_handler const& f) /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; /** @@ -284,8 +297,7 @@ This is often called only internally by this library. @return Postcondition failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ get_postcondition_failure() /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; @@ -312,9 +324,8 @@ Set a new entry invariant failure handler and return the old one. @return Old entry invariant failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ -set_entry_invariant_failure(assertion_failure_handler const& f) +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +set_entry_invariant_failure(from_failure_handler const& f) /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; /** @@ -325,8 +336,7 @@ This is often called only internally by this library. @return Entry invariant failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ get_entry_invariant_failure() /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; @@ -353,9 +363,8 @@ Set a new exit invariant failure handler and return the old one. @return Old exit invariant failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ -set_exit_invariant_failure(assertion_failure_handler const& f) +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +set_exit_invariant_failure(from_failure_handler const& f) /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; /** @@ -366,8 +375,7 @@ This is often called only internally by this library. @return Exit invariant failure handler functor. @see @RefSect{advanced_topics, Advanced Topics} */ -assertion_failure_handler -/** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ get_exit_invariant_failure() /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; @@ -396,24 +404,10 @@ This is equivalent to calling both @see @RefSect{advanced_topics, Advanced Topics} */ void /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ -set_invariant_failure(assertion_failure_handler const& f) +set_invariant_failure(from_failure_handler const& f) /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; -/** -Set the all contract failure handlers at once (for convenience). -This is equivalent to calling -@RefFunc{boost::contract::set_entry_invariant_failure}(f), -@RefFunc{boost::contract::set_precondition_failure}(f), -@RefFunc{boost::contract::set_exit_invariant_failure}(f), and -@RefFunc{boost::contract::set_postcondition_failure}(f). - -@b Throws: @c noexcept (or @c throw() if no C++11). -@param f New contract failure handler functor. -@see @RefSect{advanced_topics, Advanced Topics} -*/ -void /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ -set_failure(assertion_failure_handler const& f) - /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; +// Cannot provide a `set_all_failures` because check handler has no `from`. } } // namespace diff --git a/include/boost/contract/core/specify.hpp b/include/boost/contract/core/specify.hpp index c423349..88f48da 100644 --- a/include/boost/contract/core/specify.hpp +++ b/include/boost/contract/core/specify.hpp @@ -77,7 +77,7 @@ private: // Friends (used to limit library's public API). - friend class guard; + friend class check; template friend class specify_precondition_old_postcondition; @@ -159,7 +159,7 @@ private: #endif // Friends (used to limit library's public API). - friend class guard; + friend class check; friend class specify_precondition_old_postcondition; friend class specify_old_postcondition; /** @endcond */ @@ -274,7 +274,7 @@ private: // Friends (used to limit library's public API). - friend class guard; + friend class check; friend class specify_precondition_old_postcondition; template @@ -428,7 +428,7 @@ private: // Friends (used to limit library's public API). - friend class guard; + friend class check; friend specify_precondition_old_postcondition<> function(); template diff --git a/include/boost/contract/detail/always_true.hpp b/include/boost/contract/detail/always_true.hpp deleted file mode 100644 index e2e4556..0000000 --- a/include/boost/contract/detail/always_true.hpp +++ /dev/null @@ -1,22 +0,0 @@ - -#ifndef BOOST_CONTRACT_DETAIL_ALWAYS_TRUE_HPP_ -#define BOOST_CONTRACT_DETAIL_ALWAYS_TRUE_HPP_ - -// Copyright (C) 2008-2016 Lorenzo Caminiti -// Distributed under the Boost Software License, Version 1.0 (see accompanying -// 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 - -namespace boost { namespace contract { namespace detail { - -struct always_true { - template struct result; // To support boost::result_of. - template struct result { typedef bool type; }; - - bool operator()() { return true; } -}; - -} } } // namespace - -#endif // #include guard - diff --git a/include/boost/contract/detail/assert.hpp b/include/boost/contract/detail/assert.hpp new file mode 100644 index 0000000..c696f5c --- /dev/null +++ b/include/boost/contract/detail/assert.hpp @@ -0,0 +1,22 @@ + +#ifndef BOOST_CONTRACT_DETAIL_ASSERT_HPP_ +#define BOOST_CONTRACT_DETAIL_ASSERT_HPP_ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +#include +#include + +#define BOOST_CONTRACT_DETAIL_ASSERT(condition) \ + { \ + if(!(condition)) { \ + throw boost::contract::assertion_failure(__FILE__, __LINE__, \ + BOOST_PP_STRINGIZE(condition)); \ + } \ + } + +#endif // #include guard + diff --git a/include/boost/contract/detail/condition/check_base.hpp b/include/boost/contract/detail/condition/check_base.hpp index 2f9977f..841dd8e 100644 --- a/include/boost/contract/detail/condition/check_base.hpp +++ b/include/boost/contract/detail/condition/check_base.hpp @@ -73,9 +73,8 @@ protected: // Return true if actually checked calling user ftor. #ifndef BOOST_CONTRACT_NO_PRECONDITIONS bool check_pre(bool throw_on_failure = false) { - if(!pre_) return false; if(failed()) return true; - try { pre_(); } + try { if(pre_) pre_(); else return false; } catch(...) { // Subcontracted pre must throw on failure (instead of // calling failure handler) so to be checked in logic-or. diff --git a/include/boost/contract/detail/inlined/core/exception.hpp b/include/boost/contract/detail/inlined/core/exception.hpp index c123fc3..b0d57e0 100644 --- a/include/boost/contract/detail/inlined/core/exception.hpp +++ b/include/boost/contract/detail/inlined/core/exception.hpp @@ -13,10 +13,11 @@ #include #endif #include +#include #include -#include #include #include +#include /* PRIVATE */ @@ -28,9 +29,10 @@ /* nothing */ #endif -#define BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(handler_mutex, handler, f) \ +#define BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(handler_mutex, handler_type, \ + handler, f) \ BOOST_CONTRACT_EXCEPTION_HANDLER_SCOPED_LOCK_(handler_mutex); \ - assertion_failure_handler result = handler; \ + handler_type result = handler; \ handler = f; \ return result; @@ -99,12 +101,15 @@ void assertion_failure::init() { } namespace exception_ { - enum failure_key { pre_key, post_key, entry_inv_key, exit_inv_key }; + enum failure_key { + check_key, pre_key, post_key, entry_inv_key, exit_inv_key + }; template - void default_handler(from) { + void default_handler() { std::string k = ""; switch(Key) { + case check_key: k = "check "; break; case pre_key: k = "precondition "; break; case post_key: k = "postcondition "; break; case entry_inv_key: k = "entry invariant "; break; @@ -116,45 +121,69 @@ namespace exception_ { // what = "assertion '...' failed: ...". std::cerr << k << error.what() << std::endl; } catch(...) { - std::cerr << k << "checking threw following exception:" << std::endl + std::cerr << k << "threw following exception:" << std::endl << boost::current_exception_diagnostic_information(); } std::terminate(); // Default handlers log and call terminate. } + + template + void default_from_handler(from) { default_handler(); } + + #ifndef BOOST_CONTRACT_DISABLE_THREAD + boost::mutex check_failure_mutex; + #endif + failure_handler check_failure_handler = &default_handler; #ifndef BOOST_CONTRACT_DISABLE_THREADS boost::mutex pre_failure_mutex; #endif - assertion_failure_handler pre_failure_handler = &default_handler; + from_failure_handler pre_failure_handler = &default_from_handler; #ifndef BOOST_CONTRACT_DISABLE_THREADS boost::mutex post_failure_mutex; #endif - assertion_failure_handler post_failure_handler = &default_handler; + from_failure_handler post_failure_handler = &default_from_handler; #ifndef BOOST_CONTRACT_DISABLE_THREADS boost::mutex entry_inv_failure_mutex; #endif - assertion_failure_handler entry_inv_failure_handler = - &default_handler; + from_failure_handler entry_inv_failure_handler = + &default_from_handler; #ifndef BOOST_CONTRACT_DISABLE_THREADS boost::mutex exit_inv_failure_mutex; #endif - assertion_failure_handler exit_inv_failure_handler = - &default_handler; + from_failure_handler exit_inv_failure_handler = + &default_from_handler; } // IMPORTANT: Following func cannot be declared inline (on GCC, Clang, etc.) and -// their definition code cannot be changed by contract on/off macros. +// their definition code should not be changed by contract on/off macros. -assertion_failure_handler set_precondition_failure( - assertion_failure_handler const& f) BOOST_NOEXCEPT_OR_NOTHROW { - BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::pre_failure_mutex, - exception_::pre_failure_handler, f); +failure_handler set_check_failure(failure_handler const& f) + BOOST_NOEXCEPT_OR_NOTHROW { + BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::check_failure_mutex, + failure_handler, exception_::check_failure_handler, f); } -assertion_failure_handler get_precondition_failure() BOOST_NOEXCEPT_OR_NOTHROW { +failure_handler get_check_failure() BOOST_NOEXCEPT_OR_NOTHROW { + BOOST_CONTRACT_EXCEPTION_HANDLER_GET_(exception_::check_failure_mutex, + exception_::check_failure_handler); +} + +void check_failure() /* can throw */ { + BOOST_CONTRACT_EXCEPTION_HANDLER_(exception_::check_failure_mutex, + exception_::check_failure_handler, BOOST_PP_EMPTY()) +} + +from_failure_handler set_precondition_failure(from_failure_handler const& f) + BOOST_NOEXCEPT_OR_NOTHROW { + BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::pre_failure_mutex, + from_failure_handler, exception_::pre_failure_handler, f); +} + +from_failure_handler get_precondition_failure() BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_GET_(exception_::pre_failure_mutex, exception_::pre_failure_handler); } @@ -164,14 +193,13 @@ void precondition_failure(from where) /* can throw */ { exception_::pre_failure_handler, where) } -assertion_failure_handler set_postcondition_failure( - assertion_failure_handler const& f) BOOST_NOEXCEPT_OR_NOTHROW { +from_failure_handler set_postcondition_failure(from_failure_handler const& f) + BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::post_failure_mutex, - exception_::post_failure_handler, f); + from_failure_handler, exception_::post_failure_handler, f); } -assertion_failure_handler get_postcondition_failure() - BOOST_NOEXCEPT_OR_NOTHROW { +from_failure_handler get_postcondition_failure() BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_GET_(exception_::post_failure_mutex, exception_::post_failure_handler); } @@ -181,14 +209,13 @@ void postcondition_failure(from where) /* can throw */ { exception_::post_failure_handler, where); } -assertion_failure_handler set_entry_invariant_failure( - assertion_failure_handler const& f) BOOST_NOEXCEPT_OR_NOTHROW { +from_failure_handler set_entry_invariant_failure(from_failure_handler const& f) + BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::entry_inv_failure_mutex, - exception_::entry_inv_failure_handler, f); + from_failure_handler, exception_::entry_inv_failure_handler, f); } -assertion_failure_handler get_entry_invariant_failure() - BOOST_NOEXCEPT_OR_NOTHROW { +from_failure_handler get_entry_invariant_failure() BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_GET_(exception_::entry_inv_failure_mutex, exception_::entry_inv_failure_handler); } @@ -198,14 +225,13 @@ void entry_invariant_failure(from where) /* can throw */ { exception_::entry_inv_failure_handler, where); } -assertion_failure_handler set_exit_invariant_failure( - assertion_failure_handler const& f) BOOST_NOEXCEPT_OR_NOTHROW { +from_failure_handler set_exit_invariant_failure(from_failure_handler const& f) + BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::exit_inv_failure_mutex, - exception_::exit_inv_failure_handler, f); + from_failure_handler, exception_::exit_inv_failure_handler, f); } -assertion_failure_handler get_exit_invariant_failure() - BOOST_NOEXCEPT_OR_NOTHROW { +from_failure_handler get_exit_invariant_failure() BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_GET_(exception_::exit_inv_failure_mutex, exception_::exit_inv_failure_handler); } @@ -215,18 +241,12 @@ void exit_invariant_failure(from where) /* can throw */ { exception_::exit_inv_failure_handler, where); } -void set_invariant_failure(assertion_failure_handler const& f) +void set_invariant_failure(from_failure_handler const& f) BOOST_NOEXCEPT_OR_NOTHROW { set_entry_invariant_failure(f); set_exit_invariant_failure(f); } -void set_failure(assertion_failure_handler const& f) BOOST_NOEXCEPT_OR_NOTHROW { - set_precondition_failure(f); - set_postcondition_failure(f); - set_invariant_failure(f); -} - } } // namespace #endif // #include guard diff --git a/include/boost/contract/detail/name.hpp b/include/boost/contract/detail/name.hpp index c52ba2d..78ef8dc 100644 --- a/include/boost/contract/detail/name.hpp +++ b/include/boost/contract/detail/name.hpp @@ -17,6 +17,9 @@ #define BOOST_CONTRACT_DETAIL_NAME1(name1) \ BOOST_PP_CAT(BOOST_CONTRACT_DETAIL_NAME_PREFIX, name1) + +#define BOOST_CONTRACT_DETAIL_NAME2(name1, name2) \ + BOOST_PP_CAT(BOOST_CONTRACT_DETAIL_NAME_PREFIX, BOOST_PP_CAT(name1, name2)) #endif // #include guard diff --git a/include/boost/contract/function.hpp b/include/boost/contract/function.hpp index c327cb0..853b73f 100644 --- a/include/boost/contract/function.hpp +++ b/include/boost/contract/function.hpp @@ -2,6 +2,8 @@ #ifndef BOOST_CONTRACT_FUNCTION_HPP_ #define BOOST_CONTRACT_FUNCTION_HPP_ +// TODO: Document that boost::contract::function can also be used to program contracts for lambda functions. + // Copyright (C) 2008-2016 Lorenzo Caminiti // Distributed under the Boost Software License, Version 1.0 (see accompanying // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt). diff --git a/include/boost/contract/old.hpp b/include/boost/contract/old.hpp index d789603..8cca47e 100644 --- a/include/boost/contract/old.hpp +++ b/include/boost/contract/old.hpp @@ -25,8 +25,8 @@ Facilities to support old values. #if !BOOST_PP_VARIADICS -#define BOOST_CONTRACT_OLDOF \ -BOOST_CONTRACT_ERROR_macro_OLDOF_requires_variadic_macros_otherwise_manually_program_old_values +#define BOOST_CONTRACT_OLD \ +BOOST_CONTRACT_ERROR_macro_OLD_requires_variadic_macros_otherwise_manually_program_old_values #else // variadics @@ -40,23 +40,23 @@ BOOST_CONTRACT_ERROR_macro_OLDOF_requires_variadic_macros_otherwise_manually_pro /** @cond */ #ifdef BOOST_NO_CXX11_AUTO_DECLARATIONS - #define BOOST_CONTRACT_OLDOF_AUTO_TYPEOF_(value) /* nothing */ + #define BOOST_CONTRACT_OLD_AUTO_TYPEOF_(value) /* nothing */ #else #include // Explicitly force old_ptr<...> conversion to allow for C++11 auto decl. - #define BOOST_CONTRACT_OLDOF_AUTO_TYPEOF_(value) \ + #define BOOST_CONTRACT_OLD_AUTO_TYPEOF_(value) \ boost::contract::old_ptr #endif -#define BOOST_CONTRACT_ERROR_macro_OLDOF_has_invalid_number_of_arguments_2( \ +#define BOOST_CONTRACT_ERROR_macro_OLD_has_invalid_number_of_arguments_2( \ v, value) \ - BOOST_CONTRACT_OLDOF_AUTO_TYPEOF_(value)(boost::contract::make_old(v, \ + BOOST_CONTRACT_OLD_AUTO_TYPEOF_(value)(boost::contract::make_old(v, \ boost::contract::copy_old(v) ? (value) : boost::contract::null_old() \ )) -#define BOOST_CONTRACT_ERROR_macro_OLDOF_has_invalid_number_of_arguments_1( \ +#define BOOST_CONTRACT_ERROR_macro_OLD_has_invalid_number_of_arguments_1( \ value) \ - BOOST_CONTRACT_OLDOF_AUTO_TYPEOF_(value)(boost::contract::make_old( \ + BOOST_CONTRACT_OLD_AUTO_TYPEOF_(value)(boost::contract::make_old( \ boost::contract::copy_old() ? (value) : boost::contract::null_old() \ )) @@ -70,7 +70,7 @@ Macro typically used to copy an old value expression and assign it to an old value pointer. The expression expanded by this macro should be assigned to an old value pointer of type @RefClass{boost::contract::old_ptr} or -@RefClass{boost::contract::old_ptr_noncopyable}. +@RefClass{boost::contract::old_ptr_if_copyable}. This is a variadic macro. On compilers that do not support variadic macros, programmers can manually copy old value expressions without using this macro @@ -84,10 +84,10 @@ programmers can manually copy old value expressions without using this macro the pointer to @RefClass{boost::contract::virtual_} and the second parameter is the old value expression to be copied. */ -#define BOOST_CONTRACT_OLDOF(...) \ +#define BOOST_CONTRACT_OLD(...) \ BOOST_PP_CAT( /* CAT(..., EMTPY()) required on MSVC */ \ BOOST_PP_OVERLOAD( \ - BOOST_CONTRACT_ERROR_macro_OLDOF_has_invalid_number_of_arguments_, \ + BOOST_CONTRACT_ERROR_macro_OLD_has_invalid_number_of_arguments_, \ __VA_ARGS__ \ )(__VA_ARGS__), \ BOOST_PP_EMPTY() \ @@ -112,13 +112,13 @@ namespace boost { namespace boost { namespace contract { template -class old_ptr_noncopyable; +class old_ptr_if_copyable; /** Old value pointer (that requires the pointed old value type to be copy constructible). This is set to point to an actual old value via either -@RefMacro{BOOST_CONTRACT_OLDOF} or @RefFunc{boost::contract::make_old} (that is +@RefMacro{BOOST_CONTRACT_OLD} or @RefFunc{boost::contract::make_old} (that is why this class does not have public non-default constructors). @see @RefSect{tutorial, Tutorial} @tparam T Type of the pointed old value. This type must be copy constructible, @@ -145,7 +145,7 @@ public: T const& operator*() const { BOOST_STATIC_ASSERT_MSG( boost::is_copy_constructible::value, -"old_ptr requires T copy constructor, otherwise use old_ptr_noncopyable" +"old_ptr requires T copy constructor, otherwise use old_ptr_if_copyable" ); BOOST_CONTRACT_DETAIL_DEBUG(ptr_); return *ptr_; @@ -162,7 +162,7 @@ public: T const* const operator->() const { BOOST_STATIC_ASSERT_MSG( boost::is_copy_constructible::value, -"old_ptr requires T copy constructor, otherwise use old_ptr_noncopyable" +"old_ptr requires T copy constructor, otherwise use old_ptr_if_copyable" ); return ptr_.operator->(); } @@ -187,36 +187,38 @@ private: boost::shared_ptr ptr_; friend class old_pointer; - friend class old_ptr_noncopyable; + friend class old_ptr_if_copyable; /** @endcond */ }; +// TODO: Rename this old_ptr_ifcopyable. + /** Old value pointer (that does not require the pointed old value type to be copy constructible). This is set to point to an actual old value via either -@RefMacro{BOOST_CONTRACT_OLDOF} or @RefFunc{boost::contract::make_old}. +@RefMacro{BOOST_CONTRACT_OLD} or @RefFunc{boost::contract::make_old}. @see @RefSect{advanced_topics, Advanced Topics} @tparam T Type of pointed old value. If this type is not copy constructible, this pointer will always be null (but this library will not generate compile-time errors when this pointer is dereferenced). */ template -class old_ptr_noncopyable { /* copyable (as *) */ +class old_ptr_if_copyable { /* copyable (as *) */ public: /** Pointed old value type. */ typedef T element_type; /** Construct this object as a null old value pointer. */ - old_ptr_noncopyable() {} + old_ptr_if_copyable() {} /** Construct this object from an old value pointer of copyable-only types. This constructor is implicitly called by this library when assigning an - object of this type using @RefMacro{BOOST_CONTRACT_OLDOF}. + object of this type using @RefMacro{BOOST_CONTRACT_OLD}. @param other Copyable-only old value pointer. */ - /* implicit */ old_ptr_noncopyable(old_ptr const& other) : + /* implicit */ old_ptr_if_copyable(old_ptr const& other) : ptr_(other.ptr_) {} /** @@ -245,7 +247,7 @@ public: } #ifndef DOXYGEN - BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(old_ptr_noncopyable, !!ptr_) + BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(old_ptr_if_copyable, !!ptr_) #else /** Check if this old value pointer is null or not (safe-bool operator). @@ -257,7 +259,7 @@ public: /** @cond */ private: #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - explicit old_ptr_noncopyable(boost::shared_ptr ptr) : + explicit old_ptr_if_copyable(boost::shared_ptr ptr) : ptr_(ptr) {} #endif @@ -347,8 +349,8 @@ public: will not generate a compile-time error). */ template - /* implicit */ operator old_ptr_noncopyable() { - return ptr >(); + /* implicit */ operator old_ptr_if_copyable() { + return ptr >(); } /** @@ -454,7 +456,7 @@ actually copied. expression to be copied). @return Old value pointer (usually implicitly converted to either @RefClass{boost::contract::old_ptr} or - @RefClass{boost::contract::old_ptr_noncopyable} in user code). + @RefClass{boost::contract::old_ptr_if_copyable} in user code). */ old_pointer make_old(old_value const& old) { return old_pointer(0, old); @@ -473,7 +475,7 @@ actually copied. expression to be copied). @return Old value pointer (usually implicitly converted to either @RefClass{boost::contract::old_ptr} or - @RefClass{boost::contract::old_ptr_noncopyable} in user code). + @RefClass{boost::contract::old_ptr_if_copyable} in user code). */ old_pointer make_old(virtual_* v, old_value const& old) { return old_pointer(v, old); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 8f2778b..82723e4 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -118,6 +118,8 @@ test-suite public_function [ subdir-run-with-no public_function : virtual ] [ subdir-run-with-no public_function : virtual_branch ] [ subdir-run-with-no public_function : virtual_sparse ] + [ subdir-run-with-no public_function : virtual_access ] + [ subdir-run-with-no public_function : virtual_access_multi ] [ subdir-run-with-no public_function : protected ] [ subdir-compile-fail public_function : protected_error ] @@ -194,6 +196,12 @@ test-suite function [ subdir-run-with-no function : old_throw ] ; +test-suite check +: + [ subdir-run-with-no check : decl_class ] + [ subdir-run-with-no check : decl_macro ] +; + test-suite result : [ subdir-run-with-no result : mixed_optional ] @@ -275,8 +283,8 @@ test-suite call_if [ subdir-run call_if : equal_to ] - [ subdir-run-with-no call_if : check_if ] - [ subdir-compile-fail call_if : no_check_if_error ] + [ subdir-run-with-no call_if : condition_if ] + [ subdir-compile-fail call_if : no_condition_if_error ] ; # C++14 supported only by Clang... so in its own test-suite and explicit. diff --git a/test/call_if/advance_cxx14.cpp b/test/call_if/advance_cxx14.cpp index 60aea4e..00742c5 100644 --- a/test/call_if/advance_cxx14.cpp +++ b/test/call_if/advance_cxx14.cpp @@ -1,4 +1,6 @@ +// TODO: Move this under example/. + // Copyright (C) 2008-2016 Lorenzo Caminiti // Distributed under the Boost Software License, Version 1.0 (see accompanying // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt). @@ -58,7 +60,7 @@ void myadvance(Iter& i, Dist n) { }, p, n) ).else_( std::bind([] (auto false_) { - static_assert(false_, "requires input iter"); + static_assert(false_, "requires at least input iter"); }, std::false_type()) // Use constexpr value. ); } diff --git a/test/call_if/check_if.cpp b/test/call_if/condition_if.cpp similarity index 95% rename from test/call_if/check_if.cpp rename to test/call_if/condition_if.cpp index 698650f..698b824 100644 --- a/test/call_if/check_if.cpp +++ b/test/call_if/condition_if.cpp @@ -23,7 +23,7 @@ void push_back(std::vector& vect, T const& val) { boost::contract::guard c = boost::contract::function() .postcondition([&] { BOOST_CONTRACT_ASSERT( - boost::contract::check_if >( + boost::contract::condition_if >( boost::bind(std::equal_to(), boost::cref(vect.back()), boost::cref(val)) ) diff --git a/test/call_if/no_check_if_error.cpp b/test/call_if/no_condition_if_error.cpp similarity index 100% rename from test/call_if/no_check_if_error.cpp rename to test/call_if/no_condition_if_error.cpp diff --git a/test/check/decl.hpp b/test/check/decl.hpp new file mode 100644 index 0000000..241009e --- /dev/null +++ b/test/check/decl.hpp @@ -0,0 +1,65 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +// Test check (class and macro). + +#include "../detail/oteststream.hpp" +#include +#include +#include +#include + +boost::contract::test::detail::oteststream out; + +void f(bool check) { + #ifdef BOOST_CONTRACT_TEST_CHECK_MACRO + BOOST_CONTRACT_CHECK([&] () -> bool { + out << "f::check" << std::endl; + return check; + }()); + #else + boost::contract::check c = [&] { + out << "f::check" << std::endl; + BOOST_CONTRACT_ASSERT(check); + }; + #endif + out << "f::body" << std::endl; +} + +int main() { + std::ostringstream ok; + + out.str(""); + f(true); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_CHECKS + << "f::check" << std::endl + #endif + << "f::body" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + struct err {}; + boost::contract::set_check_failure([] { throw err(); }); + + out.str(""); + try { + f(false); + #ifndef BOOST_CONTRACT_NO_CHECKS + BOOST_TEST(false); + } catch(err const&) { + ok.str(""); + ok << "f::check" << std::endl; + #else + ok.str(""); + ok << "f::body" << std::endl; + #endif + BOOST_TEST(out.eq(ok.str())); + } catch(...) { BOOST_TEST(false); } + + return boost::report_errors(); +} + diff --git a/test/check/decl_class.cpp b/test/check/decl_class.cpp new file mode 100644 index 0000000..90d7d63 --- /dev/null +++ b/test/check/decl_class.cpp @@ -0,0 +1,11 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +// Test check class. + +#undef BOOST_CONTRACT_TEST_CHECK_MACRO +#include "decl.hpp" + diff --git a/test/check/decl_macro.cpp b/test/check/decl_macro.cpp new file mode 100644 index 0000000..492231a --- /dev/null +++ b/test/check/decl_macro.cpp @@ -0,0 +1,11 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +// Test check macro. + +#define BOOST_CONTRACT_TEST_CHECK_MACRO +#include "decl.hpp" + diff --git a/test/public_function/contracts.hpp b/test/public_function/contracts.hpp index 917b881..5c8ea4b 100644 --- a/test/public_function/contracts.hpp +++ b/test/public_function/contracts.hpp @@ -77,7 +77,7 @@ result_type& t::f(s_type& s, boost::contract::virtual_* v) { BOOST_CONTRACT_ASSERT(result.value == old_s->value); }) ; - out << "t<" << Id << ">::body" << std::endl; + out << "t<" << Id << ">::f::body" << std::endl; return result; } diff --git a/test/public_function/virtual_access.cpp b/test/public_function/virtual_access.cpp new file mode 100644 index 0000000..9290b2c --- /dev/null +++ b/test/public_function/virtual_access.cpp @@ -0,0 +1,220 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +// Test overrides with mixed access level from (single) base. + +#include "../detail/oteststream.hpp" +#include +#include +#include +#include +#include +#include +#include + +boost::contract::test::detail::oteststream out; + +struct b { // Test all access levels (public, protected, and private). + friend void call(b& me) { // Test polymorphic calls (object by &). + me.f(); + me.g(); + me.h(); + } + + static void statci_inv() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + + virtual void f(boost::contract::virtual_* v = 0) { + 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; + } + + // NOTE: Both protected and private virtual members must declare + // extra `virtual_* = 0` parameter (otherwise they cannot be overridden in + // derived classes with contracts because C++ uses also default parameters + // to match signature of overriding functions). + +protected: + virtual void g(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::function() + .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: + virtual void h(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::function() + .precondition([&] { out << "b::h::pre" << std::endl; }) + .old([&] { out << "b::h::old" << std::endl; }) + .postcondition([&] { out << "b::h::post" << std::endl; }) + ; + out << "b::h::body" << std::endl; + } +}; + +struct a // Test overrides with mixed access levels from base. + #define BASES public b + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + static void statci_inv() { out << "a::static_inv" << std::endl; } + void invariant() const { out << "a::inv" << std::endl; } + + virtual void f(boost::contract::virtual_* v = 0) /* override */ { + 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_OVERRIDES(f); + + // Following do not override public members so no `override_...` param and + // they do not actually subcontract. + + virtual void g(boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function(v, 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; + } + + virtual void h(boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { out << "a::h::pre" << std::endl; }) + .old([&] { out << "a::h::old" << std::endl; }) + .postcondition([&] { out << "a::h::post" << std::endl; }) + ; + out << "a::h::body" << std::endl; + } +}; + +int main() { + std::ostringstream ok; + + b bb; + out.str(""); + call(bb); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::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 + #endif + << "b::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "b::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::f::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "b::g::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::g::old" << std::endl + #endif + << "b::g::body" << std::endl + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::g::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "b::h::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::h::old" << std::endl + #endif + << "b::h::body" << std::endl + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::h::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + a aa; + out.str(""); + call(aa); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::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::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 + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "a::g::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::g::old" << std::endl + #endif + << "a::g::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::g::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "a::h::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::h::old" << std::endl + #endif + << "a::h::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "a::h::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} + diff --git a/test/public_function/virtual_access_multi.cpp b/test/public_function/virtual_access_multi.cpp new file mode 100644 index 0000000..9a25276 --- /dev/null +++ b/test/public_function/virtual_access_multi.cpp @@ -0,0 +1,283 @@ + +// Copyright (C) 2008-2016 Lorenzo Caminiti +// Distributed under the Boost Software License, Version 1.0 (see accompanying +// 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 + +// Test overrides with mixed access levels from different (multiple) bases. + +#include +#ifdef BOOST_MSVC + +// WARNING: MSVC (at least up to VS 2015) gives a compile-time error if SFINAE +// cannot introspect a member because of its private or protected access level. +// That is incorrect, SFINAE should fail in these cases without generating +// compile-time errors like GCC and CLang do. Therefore, currently it is not +// possible to override a member that is public in one base but private or +// protected in other base using this library on MSVC (that can be done instead +// using this library on GCC or CLang). +int main() { return 0; } // Test trivially passes on MSVC. + +#else + +#include "../detail/oteststream.hpp" +#include +#include +#include +#include +#include +#include +#include + +boost::contract::test::detail::oteststream out; + +struct c { // Test public access different from base `b`'s access below. + static void statci_inv() { out << "c::static_inv" << std::endl; } + void invariant() const { out << "c::inv" << std::endl; } + + virtual void f(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { out << "c::f::pre" << std::endl; }) + .old([&] { out << "c::f::old" << std::endl; }) + .postcondition([&] { out << "c::f::post" << std::endl; }) + ; + out << "c::f::body" << std::endl; + } + + virtual void g(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { out << "c::g::pre" << std::endl; }) + .old([&] { out << "c::g::old" << std::endl; }) + .postcondition([&] { out << "c::g::post" << std::endl; }) + ; + out << "c::g::body" << std::endl; + } + + virtual void h(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { out << "c::h::pre" << std::endl; }) + .old([&] { out << "c::h::old" << std::endl; }) + .postcondition([&] { out << "c::h::post" << std::endl; }) + ; + out << "c::h::body" << std::endl; + } +}; + +struct b { // Test all access levels (public, protected, and private). + friend void call(b& me) { // Test polymorphic calls (object by &). + me.f(); + me.g(); + me.h(); + } + + static void statci_inv() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + + virtual void f(boost::contract::virtual_* v = 0) { + 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; + } + + // NOTE: Both protected and private virtual members must declare + // extra `virtual_* = 0` parameter (otherwise they cannot be overridden in + // derived classes with contracts because C++ uses also default parameters + // to match signature of overriding functions). + +protected: + virtual void g(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::function() + .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: + virtual void h(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::function() + .precondition([&] { out << "b::h::pre" << std::endl; }) + .old([&] { out << "b::h::old" << std::endl; }) + .postcondition([&] { out << "b::h::post" << std::endl; }) + ; + out << "b::h::body" << std::endl; + } +}; + +struct a // Test overrides with mixed access levels from different bases. + #define BASES public b, public c + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + static void statci_inv() { out << "a::static_inv" << std::endl; } + void invariant() const { out << "a::inv" << std::endl; } + + virtual void f(boost::contract::virtual_* v = 0) /* override */ { + 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; + } + + virtual void g(boost::contract::virtual_* v = 0) /* override */ { + 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; + } + + virtual void h(boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function( + v, &a::h, this) + .precondition([&] { out << "a::h::pre" << std::endl; }) + .old([&] { out << "a::h::old" << std::endl; }) + .postcondition([&] { out << "a::h::post" << std::endl; }) + ; + out << "a::h::body" << std::endl; + } + + BOOST_CONTRACT_OVERRIDES(f, g, h); +}; + +int main() { + std::ostringstream ok; + + b bb; + out.str(""); + call(bb); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::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 + #endif + << "b::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "b::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::f::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "b::g::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::g::old" << std::endl + #endif + << "b::g::body" << std::endl + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::g::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "b::h::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::h::old" << std::endl + #endif + << "b::h::body" << std::endl + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::h::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + a aa; + out.str(""); + call(aa); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "b::inv" << std::endl + << "c::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 + << "c::f::old" << std::endl + << "a::f::old" << std::endl + #endif + << "a::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "b::inv" << std::endl + << "c::inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "b::f::old" << std::endl + << "b::f::post" << std::endl + << "c::f::old" << std::endl + << "c::f::post" << std::endl + << "a::f::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "c::inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "c::g::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::g::old" << std::endl + << "a::g::old" << std::endl + #endif + << "a::g::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "c::inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::g::old" << std::endl + << "c::g::post" << std::endl + << "a::g::post" << std::endl + #endif + + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "c::inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "c::h::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::h::old" << std::endl + << "a::h::old" << std::endl + #endif + << "a::h::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "c::inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::h::old" << std::endl + << "c::h::post" << std::endl + << "a::h::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} + +#endif // MSVC +