diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 79d4d8f..0000000 --- a/TODO.txt +++ /dev/null @@ -1,12 +0,0 @@ -./include/boost/contract/core/config.hpp:12:// TODO: Find out the C++ example that gives linker error on global variables that are not static/extern and then test this library against that example (for all boost_link mode: shared, static, and header-only). -./include/boost/contract/old.hpp:228: // TODO: Document that auto old_x = BOOST_CONTRACT_OLD(...) will use old_ptr and not old_ptr_if_copyable (auto will by default not use old_ptr_if_conpyable because old_ptr is more stringent from a type requirement prospective, if users want to relax the copyable type requirements they need to explicitly use old_ptr_if_copyable instead of using auto). -./include/boost/contract/old.hpp:322: // TODO: Will this shared_ptr destroy old_value_copy given its type is erased to void*?? Test it! -./include/boost/contract.hpp:28:// 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. -./include/boost/contract.hpp:30:// 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). Also add a test (under test/public_function/...) and an example for friend. -./include/boost/contract.hpp:32:// 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). -./include/boost/contract.hpp:34:// 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?). -./include/boost/contract.hpp:36:// 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). -./include/boost/contract.hpp:38:// TODO: Document that 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/boost/contract.hpp:40:// TODO: Document that contract for constexpr functions cannot be supported at the moment because constexpr functions cannot: (1) declare local variables of (literal) types with non-trivial constexpr destructors (needed by this lib to check inv, post, and except at exit), (2) call other (constexpr) functions with try-catch statements (used by this lib to report assertion failure and catch any other exception that might be thrown by the evaluation of the asserted conditions), (3) use lambda functions (use by this for convenience to program functors that check per and post). Also note that even if supported, contracts for constexpr probably will not use old values (because constexpr prevent the function from having any side effect visible to the caller, variables around such side-effects are usually the candidates for old value copies) and subcontracting (because constexpr functions cannot be virtual). -./include/boost/contract.hpp:42:// TODO: Documentation updates based on all emails to Boost (review all emails). -./include/boost/contract.hpp:44:// TODO: Documentation updates based on all n-papers that I read recently (review notes I wrote on all papers), add those n-papers to the Bibliography section, and add P0380 (the most recent proposal) to the feature comparison table. diff --git a/include/boost/contract/core/virtual.hpp b/include/boost/contract/core/virtual.hpp index e7e973b..22e89e5 100644 --- a/include/boost/contract/core/virtual.hpp +++ b/include/boost/contract/core/virtual.hpp @@ -21,7 +21,6 @@ Facility to declare virtual public functions with contracts. !defined(BOOST_CONTRACT_NO_EXCEPTS) #include #include - #include #endif namespace boost { @@ -84,10 +83,13 @@ class virtual_ : private boost::noncopyable { // Avoid copy queue, stack, etc. #endif #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) - push_old_init, - call_old_copy, - push_old_copy, - pop_old_copy, + // For outside .old(...). + push_old_init_copy, + // pop_old_init_copy as static function below. + // For inside .old(...). + call_old_ftor, + push_old_ftor_copy, + pop_old_ftor_copy, #endif #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS check_post, @@ -100,7 +102,7 @@ class virtual_ : private boost::noncopyable { // Avoid copy queue, stack, etc. #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) // Not just an enum value because the logical combination of two values. - inline static bool pop_old_init(action_enum a) { + inline static bool pop_old_init_copy(action_enum a) { return #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS a == check_post @@ -139,8 +141,8 @@ class virtual_ : private boost::noncopyable { // Avoid copy queue, stack, etc. #endif #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) - std::queue > old_inits_; - std::stack > old_copies_; + std::queue > old_init_copies_; + std::queue > old_ftor_copies_; #endif #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS boost::any result_ptr_; // Result for virtual and overriding functions. diff --git a/include/boost/contract/detail/condition/cond_with_subcontracting.hpp b/include/boost/contract/detail/condition/cond_with_subcontracting.hpp index 14f31ae..372be53 100644 --- a/include/boost/contract/detail/condition/cond_with_subcontracting.hpp +++ b/include/boost/contract/detail/condition/cond_with_subcontracting.hpp @@ -203,7 +203,7 @@ protected: #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS void init_subcontracted_old() { // Old values of overloaded func on stack (so no `f` param here). - exec_and(boost::contract::virtual_::push_old_init); + exec_and(boost::contract::virtual_::push_old_init_copy); } #endif @@ -227,7 +227,7 @@ protected: #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) void copy_subcontracted_old() { - exec_and(boost::contract::virtual_::call_old_copy, + exec_and(boost::contract::virtual_::call_old_ftor, &cond_with_subcontracting::copy_virtual_old); } #endif @@ -277,7 +277,7 @@ private: boost::contract::virtual_::action_enum a; if(base_call_) { a = v_->action_; - v_->action_ = boost::contract::virtual_::push_old_copy; + v_->action_ = boost::contract::virtual_::push_old_ftor_copy; } this->copy_old(); if(base_call_) v_->action_ = a; @@ -286,7 +286,7 @@ private: void pop_base_old() { if(base_call_) { boost::contract::virtual_::action_enum a = v_->action_; - v_->action_ = boost::contract::virtual_::pop_old_copy; + v_->action_ = boost::contract::virtual_::pop_old_ftor_copy; this->copy_old(); v_->action_ = a; } // Else, do nothing (for base calls only). diff --git a/include/boost/contract/old.hpp b/include/boost/contract/old.hpp index 806843b..0734d8d 100644 --- a/include/boost/contract/old.hpp +++ b/include/boost/contract/old.hpp @@ -24,6 +24,7 @@ Facilities to support old values. #include #include #include +#include #if !BOOST_PP_VARIADICS @@ -321,7 +322,6 @@ public: ) #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) - // TODO: Will this shared_ptr destroy old_value_copy given its type is erased to void*?? Test it! : untyped_copy_(new old_value_copy(old)) #endif // Else, leave ptr_ null (thus no copy of T). {} @@ -420,28 +420,36 @@ private: boost::static_pointer_cast(untyped_copy_); BOOST_CONTRACT_DETAIL_DEBUG(typed_copy); return Ptr(typed_copy); - } else if(v_->action_ == boost::contract::virtual_::push_old_init || - v_->action_ == boost::contract::virtual_::push_old_copy) { + } else if( + v_->action_ == boost::contract::virtual_::push_old_init_copy || + v_->action_ == boost::contract::virtual_::push_old_ftor_copy + ) { BOOST_CONTRACT_DETAIL_DEBUG(untyped_copy_); - if(v_->action_ == boost::contract::virtual_::push_old_init) { - v_->old_inits_.push(untyped_copy_); - } else { - v_->old_copies_.push(untyped_copy_); - } - return Ptr(); // Push (so return null). - } else if(boost::contract::virtual_::pop_old_init(v_->action_) || - v_->action_ == boost::contract::virtual_::pop_old_copy) { - BOOST_CONTRACT_DETAIL_DEBUG(!untyped_copy_); // Copy not null... - boost::shared_ptr untyped_copy; // ...but still pop this. - if(boost::contract::virtual_::pop_old_init(v_->action_)) { - untyped_copy = v_->old_inits_.front(); - BOOST_CONTRACT_DETAIL_DEBUG(untyped_copy); - v_->old_inits_.pop(); - } else { - untyped_copy = v_->old_copies_.top(); - BOOST_CONTRACT_DETAIL_DEBUG(untyped_copy); - v_->old_copies_.pop(); - } + std::queue >& copies = v_->action_ == + boost::contract::virtual_::push_old_ftor_copy ? + v_->old_ftor_copies_ + : + v_->old_init_copies_ + ; + copies.push(untyped_copy_); + return Ptr(); // Pushed (so return null). + } else if( + boost::contract::virtual_::pop_old_init_copy(v_->action_) || + v_->action_ == boost::contract::virtual_::pop_old_ftor_copy + ) { + // Copy not null, but still pop it from the queue. + BOOST_CONTRACT_DETAIL_DEBUG(!untyped_copy_); + + std::queue >& copies = v_->action_ == + boost::contract::virtual_::pop_old_ftor_copy ? + v_->old_ftor_copies_ + : + v_->old_init_copies_ + ; + boost::shared_ptr untyped_copy = copies.front(); + BOOST_CONTRACT_DETAIL_DEBUG(untyped_copy); + copies.pop(); + typedef old_value_copy copied_type; boost::shared_ptr typed_copy = // Un-erase type. boost::static_pointer_cast(untyped_copy); @@ -549,8 +557,8 @@ bool copy_old(virtual_* v) { return true; #endif } - return v->action_ == boost::contract::virtual_::push_old_init || - v->action_ == boost::contract::virtual_::push_old_copy; + return v->action_ == boost::contract::virtual_::push_old_init_copy || + v->action_ == boost::contract::virtual_::push_old_ftor_copy; #else return false; // No post checking, so never copy old values. #endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e53eff3..6039e5c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -118,6 +118,7 @@ test-suite public_function : [ subdir-run public_function : virtual_sparse ] [ subdir-run public_function : virtual_access ] [ subdir-run public_function : virtual_access_multi ] + [ subdir-run public_function : old_virtual ] [ subdir-run public_function : protected ] [ subdir-compile-fail public_function : protected_error ] diff --git a/test/public_function/contracts.hpp b/test/public_function/contracts.hpp index 1195d42..feb85f4 100644 --- a/test/public_function/contracts.hpp +++ b/test/public_function/contracts.hpp @@ -74,7 +74,6 @@ result_type& t::f(s_type& s, boost::contract::virtual_* v) { .postcondition([&] (result_type const& result) { out << Id << "::f::post" << std::endl; BOOST_CONTRACT_ASSERT(z.value == old_z->value + old_s->value); - std::clog << Id << "::f::post OUT" << std::endl; BOOST_CONTRACT_ASSERT(s.value.find(old_z->value) != std::string::npos); BOOST_CONTRACT_ASSERT(result.value == old_s->value); @@ -90,15 +89,6 @@ result_type& t::f(s_type& s, boost::contract::virtual_* v) { return result; } -struct u { - u() { std::cout << "!!!!! u default ctor" << std::endl; } - u(u const&) { std::cout << "!!!!! u copy ctor" << std::endl; } - ~u() { std::cout << "!!!!! u dtor" << std::endl; } -private: - u& operator=(u const&); - -}; - // Test base with other bases, multiple inheritance, and no subcontracting from // protected and private bases (even if fully contracted). struct c @@ -119,9 +109,7 @@ struct c typedef boost::contract::test::detail::counter y_type; y_type y; - c() : uu() { y.value = "c"; } - - u uu; + c() { y.value = "c"; } virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0) /* override */ { @@ -129,7 +117,6 @@ struct c boost::contract::old_ptr old_y = BOOST_CONTRACT_OLD(v, y_type::eval(y)); boost::contract::old_ptr old_s; - boost::contract::old_ptr old_u; boost::contract::check c = boost::contract::public_function< override_f>(v, result, &c::f, this, s) .precondition([&] { @@ -139,12 +126,9 @@ struct c .old([&] { out << "c::f::old" << std::endl; old_s = BOOST_CONTRACT_OLD(v, s_type::eval(s)); - // TODO: Fix bug for this old_u... - old_u = BOOST_CONTRACT_OLD(v, uu); }) .postcondition([&] (result_type const& result) { out << "c::f::post" << std::endl; - std::clog << "c::f::post OUT" << std::endl; BOOST_CONTRACT_ASSERT(y.value == old_y->value + old_s->value); BOOST_CONTRACT_ASSERT(s.value.find(old_y->value) != std::string::npos); diff --git a/test/public_function/contracts.hpp.ok b/test/public_function/contracts.hpp.ok deleted file mode 100644 index feb85f4..0000000 --- a/test/public_function/contracts.hpp.ok +++ /dev/null @@ -1,261 +0,0 @@ - -#ifndef BOOST_CONTRACT_TEST_PUBLIC_FUNCTION_CONTRACTS_HPP_ -#define BOOST_CONTRACT_TEST_PUBLIC_FUNCTION_CONTRACTS_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 - -// Test public member function subcontracting (also with old and return values). - -#include "../detail/oteststream.hpp" -#include "../detail/counter.hpp" -#include -#include -#include -#include -#include -#include -#include - -boost::contract::test::detail::oteststream out; - -struct s_tag; -typedef boost::contract::test::detail::counter s_type; - -struct except_error {}; - -struct result_type { - std::string value; - explicit result_type(std::string const& s) : value(s) {} - -private: // Test non-copyable and non-default-constructible result. - result_type(); - result_type(result_type const&); - result_type& operator=(result_type const&); -}; - -// Test base without additional bases and pure virtual. -template -struct t { - static void static_invariant() { out << Id << "::static_inv" << std::endl; } - - void invariant() const { - out << Id << "::inv" << std::endl; - BOOST_CONTRACT_ASSERT(z.value != ""); - } - - struct z_tag; - typedef boost::contract::test::detail::counter z_type; - z_type z; - - t() { z.value.push_back(Id); } - - virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0) = 0; -}; - -template // Requires: Only pass lower case Id so it'll never be 'X'. -result_type& t::f(s_type& s, boost::contract::virtual_* v) { - std::ostringstream r; r << "none-" << Id; - static result_type result(r.str()); - boost::contract::old_ptr old_z = - BOOST_CONTRACT_OLD(v, z_type::eval(z)); - boost::contract::old_ptr old_s; - boost::contract::check c = boost::contract::public_function(v, result, this) - .precondition([&] { - out << Id << "::f::pre" << std::endl; - BOOST_CONTRACT_ASSERT(s.value[0] == Id || s.value[0] == 'X'); - }) - .old([&] { - out << Id << "::f::old" << std::endl; - old_s = BOOST_CONTRACT_OLD(v, s_type::eval(s)); - }) - .postcondition([&] (result_type const& result) { - out << Id << "::f::post" << std::endl; - BOOST_CONTRACT_ASSERT(z.value == old_z->value + old_s->value); - BOOST_CONTRACT_ASSERT(s.value.find(old_z->value) != - std::string::npos); - BOOST_CONTRACT_ASSERT(result.value == old_s->value); - }) - .except([&] { - out << Id << "::f::except" << std::endl; - BOOST_CONTRACT_ASSERT(z.value == old_z->value); - BOOST_CONTRACT_ASSERT(s.value == old_s->value); - }) - ; - out << "t<" << Id << ">::f::body" << std::endl; - if(s.value == "X") throw except_error(); - return result; -} - -// Test base with other bases, multiple inheritance, and no subcontracting from -// protected and private bases (even if fully contracted). -struct c - #define BASES public t<'d'>, protected t<'p'>, private t<'q'>, public t<'e'> - : BASES -{ - typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; - #undef BASES - - static void static_invariant() { out << "c::static_inv" << std::endl; } - - void invariant() const { - out << "c::inv" << std::endl; - BOOST_CONTRACT_ASSERT(y.value != ""); - } - - struct y_tag; - typedef boost::contract::test::detail::counter y_type; - y_type y; - - c() { y.value = "c"; } - - virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0) - /* override */ { - static result_type result("none-c"); - boost::contract::old_ptr old_y = - BOOST_CONTRACT_OLD(v, y_type::eval(y)); - boost::contract::old_ptr old_s; - boost::contract::check c = boost::contract::public_function< - override_f>(v, result, &c::f, this, s) - .precondition([&] { - out << "c::f::pre" << std::endl; - BOOST_CONTRACT_ASSERT(s.value == "C" || s.value == "X"); - }) - .old([&] { - out << "c::f::old" << std::endl; - old_s = BOOST_CONTRACT_OLD(v, s_type::eval(s)); - }) - .postcondition([&] (result_type const& result) { - out << "c::f::post" << std::endl; - BOOST_CONTRACT_ASSERT(y.value == old_y->value + old_s->value); - BOOST_CONTRACT_ASSERT(s.value.find(old_y->value) != - std::string::npos); - BOOST_CONTRACT_ASSERT(result.value == old_s->value); - }) - .except([&] { - out << "c::f::except" << std::endl; - BOOST_CONTRACT_ASSERT(y.value == old_y->value); - BOOST_CONTRACT_ASSERT(s.value == old_s->value); - }) - ; - - out << "c::f::body" << std::endl; - if(s.value == "X") throw except_error(); - std::string save_s = s.value; - - std::string save = y.value; - y.value += save_s; - s.value = save; - - save = t<'d'>::z.value; - t<'d'>::z.value += save_s; - s.value += save; - - save = t<'e'>::z.value; - t<'e'>::z.value += save_s; - s.value += save; - - result.value = save_s; - return result; - } - BOOST_CONTRACT_OVERRIDE(f) -}; - -// Test no subcontracting from not (fully) contracted base. -struct b { - static void static_invariant() { out << "b::static_inv" << std::endl; } - void invariant() const { out << "b::inv" << std::endl; } - - virtual ~b() {} - - // No contract (no virtual_ so this is not actually overridden by a::f). - virtual result_type& f(s_type& s) { - static result_type result("none-b"); - out << "b::f::body" << std::endl; - result.value = s.value; - return result; - } -}; - -// Test public function with both non-contracted and contracted bases. -struct a - #define BASES public b, public c - : BASES -{ - typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; - #undef BASES - - static void static_invariant() { out << "a::static_inv" << std::endl; } - - void invariant() const { - out << "a::inv" << std::endl; - BOOST_CONTRACT_ASSERT(x.value != ""); - } - - struct x_tag; - typedef boost::contract::test::detail::counter x_type; - x_type x; - - a() { x.value = "a"; } - - // Must use virtual_ even if no longer decl virtual for correct overloading. - result_type& f(s_type& s, boost::contract::virtual_* v = 0) - /* override */ { - static result_type result("none-a"); - boost::contract::old_ptr old_x = - BOOST_CONTRACT_OLD(v, x_type::eval(x)); - boost::contract::old_ptr old_s; - boost::contract::check c = boost::contract::public_function< - override_f>(v, result, &a::f, this, s) - .precondition([&] { - out << "a::f::pre" << std::endl; - BOOST_CONTRACT_ASSERT(s.value == "A" || s.value == "X"); - }) - .old([&] { - out << "a::f::old" << std::endl; - old_s = BOOST_CONTRACT_OLD(v, s_type::eval(s)); - }) - .postcondition([&] (result_type const& result) { - out << "a::f::post" << std::endl; - BOOST_CONTRACT_ASSERT(x.value == old_x->value + old_s->value); - BOOST_CONTRACT_ASSERT(s.value.find(old_x->value) != - std::string::npos); - BOOST_CONTRACT_ASSERT(result.value == old_s->value); - }) - .except([&] { - out << "a::f::except" << std::endl; - BOOST_CONTRACT_ASSERT(x.value == old_x->value); - BOOST_CONTRACT_ASSERT(s.value == old_s->value); - }) - ; - - out << "a::f::body" << std::endl; - if(s.value == "X") throw except_error(); - std::string save_s = s.value; - - std::string save = x.value; - x.value += save_s; - s.value = save; - - save = y.value; - y.value += save_s; - s.value += save; - - save = t<'d'>::z.value; - t<'d'>::z.value += save_s; - s.value += save; - - save = t<'e'>::z.value; - t<'e'>::z.value += save_s; - s.value += save; - - result.value = save_s; - return result; - } - BOOST_CONTRACT_OVERRIDE(f) -}; - -#endif // #include guard - diff --git a/test/public_function/old_virtual.cpp b/test/public_function/old_virtual.cpp new file mode 100644 index 0000000..ba3e7a6 --- /dev/null +++ b/test/public_function/old_virtual.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 + +// Test old inits/ftors and of mixed types up inheritance tree. + +#include "../detail/oteststream.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +boost::contract::test::detail::oteststream out; + +struct num { + static num make(int i) { // Test no ctor (not even explicit) but for copy. + num n; + n.value(i); + return n; + } + + num(num const& other) : value_(other.value_) {} + + void value(int i) { value_ = boost::lexical_cast(i); } + int value() const { return boost::lexical_cast(value_); } + + num operator+(int left) { + num n; + n.value(value() + left); + return n; + } + +private: + num() {} // Test no visible default ctor (only copy ctor). + num& operator=(num const&); // Test no copy operator (only copy ctor). + + std::string value_; // Test this size-of != from other old type `int` below. +}; + +struct c { + virtual void f(int& i, num& n, boost::contract::virtual_* v = 0) { + boost::contract::old_ptr old_a = BOOST_CONTRACT_OLD(v, i + 1); + boost::contract::old_ptr old_b = BOOST_CONTRACT_OLD(v, n + 2); + boost::contract::old_ptr old_x; + boost::contract::old_ptr old_y; + boost::contract::check c = boost::contract::public_function(v, this) + .old([&] { + out << "c::f::old" << std::endl; + old_x = BOOST_CONTRACT_OLD(v, i + 3); + old_y = BOOST_CONTRACT_OLD(v, n + 4); + }) + .postcondition([&] { + out << "c::f::post" << std::endl; + BOOST_CONTRACT_ASSERT(*old_a == n.value() + 1); + BOOST_CONTRACT_ASSERT(old_b->value() == i + 2); + BOOST_CONTRACT_ASSERT(*old_x == n.value() + 3); + BOOST_CONTRACT_ASSERT(old_y->value() == i + 4); + }) + ; + out << "c::f::body" << std::endl; + int tmp = i; + i = n.value(); + n.value(tmp); + } +}; + +struct b + #define BASES public c + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + virtual void f(int& i, num& n, boost::contract::virtual_* v = 0) + /* override */ { + boost::contract::old_ptr old_a = BOOST_CONTRACT_OLD(v, i + 1); + boost::contract::old_ptr old_b = BOOST_CONTRACT_OLD(v, n + 2); + boost::contract::old_ptr old_x; + boost::contract::old_ptr old_y; + boost::contract::check c = boost::contract::public_function< + override_f>(v, &c::f, this, i, n) + .old([&] { + out << "b::f::old" << std::endl; + old_x = BOOST_CONTRACT_OLD(v, i + 3); + old_y = BOOST_CONTRACT_OLD(v, n + 4); + }) + .postcondition([&] { + out << "b::f::post" << std::endl; + BOOST_CONTRACT_ASSERT(*old_a == n.value() + 1); + BOOST_CONTRACT_ASSERT(old_b->value() == i + 2); + BOOST_CONTRACT_ASSERT(*old_x == n.value() + 3); + BOOST_CONTRACT_ASSERT(old_y->value() == i + 4); + }) + ; + out << "b::f::body" << std::endl; + int tmp = i; + i = n.value(); + n.value(tmp); + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +struct a + #define BASES public b + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + virtual void f(int& i, num& n, boost::contract::virtual_* v = 0) + /* override */ { + boost::contract::old_ptr old_a = BOOST_CONTRACT_OLD(v, i + 1); + boost::contract::old_ptr old_b = BOOST_CONTRACT_OLD(v, n + 2); + boost::contract::old_ptr old_x; + boost::contract::old_ptr old_y; + boost::contract::check c = boost::contract::public_function< + override_f>(v, &c::f, this, i, n) + .old([&] { + out << "a::f::old" << std::endl; + old_x = BOOST_CONTRACT_OLD(v, i + 3); + old_y = BOOST_CONTRACT_OLD(v, n + 4); + }) + .postcondition([&] { + out << "a::f::post" << std::endl; + BOOST_CONTRACT_ASSERT(*old_a == n.value() + 1); + BOOST_CONTRACT_ASSERT(old_b->value() == i + 2); + BOOST_CONTRACT_ASSERT(*old_x == n.value() + 3); + BOOST_CONTRACT_ASSERT(old_y->value() == i + 4); + }) + ; + out << "a::f::body" << std::endl; + int tmp = i; + i = n.value(); + n.value(tmp); + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +int main() { + std::ostringstream ok; + int i = 0; + num n = num::make(0); + + i = 123; + n.value(-123); + a aa; // Test virtual call with 2 bases. + out.str(""); + aa.f(i, n); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::f::old" << std::endl + << "b::f::old" << std::endl + << "a::f::old" << std::endl + #endif + << "a::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::f::old" << std::endl + << "c::f::post" << std::endl + << "b::f::old" << std::endl + << "b::f::post" << std::endl + // No old call here because not a base object. + << "a::f::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + i = 456; + n.value(-456); + b bb; // Test virtual call with 1 base. + out.str(""); + bb.f(i, n); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::f::old" << std::endl + << "b::f::old" << std::endl + #endif + << "b::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::f::old" << std::endl + << "c::f::post" << std::endl + // No old call here because not a base object. + << "b::f::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + i = 789; + n.value(-789); + c cc; // Test virtual call with no bases. + out.str(""); + cc.f(i, n); + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "c::f::old" << std::endl + #endif + << "c::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + // No old call here because not a base object. + << "c::f::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} +