From b36dc64d184b9c4cfef051c5f46cfc000501ec81 Mon Sep 17 00:00:00 2001 From: Lorenzo Caminiti Date: Tue, 27 Sep 2016 20:54:23 -0700 Subject: [PATCH] resolved a few TODOs, also fixing old for except of public functions virtual calls --- include/boost/contract.hpp | 6 - include/boost/contract/check.hpp | 4 +- include/boost/contract/constructor.hpp | 2 +- include/boost/contract/core/config.hpp | 8 + include/boost/contract/core/exception.hpp | 10 + include/boost/contract/core/virtual.hpp | 37 +++- .../contract/detail/condition/cond_base.hpp | 12 +- .../detail/condition/cond_with_inv.hpp | 5 +- .../detail/condition/cond_with_post.hpp | 3 +- .../condition/cond_with_subcontracting.hpp | 28 ++- .../detail/inlined/core/exception.hpp | 27 ++- .../detail/inlined/detail/checking.hpp | 30 ++- .../detail/operation/public_function.hpp | 3 +- include/boost/contract/function.hpp | 2 - include/boost/contract/old.hpp | 192 ++++++++++-------- test/Jamfile.v2 | 7 +- test/call_if/no_condition_if_error.cpp | 40 ---- .../no_equal_call_if.cpp} | 0 ...ition_if.cpp => no_equal_condition_if.cpp} | 0 test/{old => call_if}/no_equal_error.cpp | 0 test/constructor/old_throw.cpp | 3 +- test/destructor/old_throw.cpp | 3 +- test/function/old_throw.cpp | 3 +- test/invariant/decl.hpp | 12 +- test/old/auto.cpp | 13 +- test/old/if_copyable.cpp | 35 +--- test/old/if_copyable.hpp | 45 ++++ test/old/if_copyable_error.cpp | 30 +-- test/old/no_make_old_error.hpp | 3 +- test/public_function/body_throw.cpp | 174 +++++++--------- test/public_function/body_throw_virtual.cpp | 118 +++++++++++ .../body_throw_virtual_branch.cpp | 102 ++++++++++ test/public_function/contracts.hpp | 28 ++- test/public_function/old_throw.cpp | 3 +- test/public_function/static_old_throw.cpp | 3 +- 35 files changed, 632 insertions(+), 359 deletions(-) delete mode 100644 test/call_if/no_condition_if_error.cpp rename test/{old/no_equal.cpp => call_if/no_equal_call_if.cpp} (100%) rename test/call_if/{condition_if.cpp => no_equal_condition_if.cpp} (100%) rename test/{old => call_if}/no_equal_error.cpp (100%) create mode 100644 test/old/if_copyable.hpp create mode 100644 test/public_function/body_throw_virtual.cpp create mode 100644 test/public_function/body_throw_virtual_branch.cpp diff --git a/include/boost/contract.hpp b/include/boost/contract.hpp index 99d2fb7..3ac691f 100644 --- a/include/boost/contract.hpp +++ b/include/boost/contract.hpp @@ -25,12 +25,6 @@ never be used directly by programmers. @see @RefSect{getting_started, Getting Started} */ -// TODO: Clearly separate const and const volatile inv checked by mutable+const and volatile+const_volatile func respectively (see related email to Boost). - -// 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 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: 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). Also add a test (under test/public_function/...) and an example for friend. diff --git a/include/boost/contract/check.hpp b/include/boost/contract/check.hpp index 0a4d263..7de43f2 100644 --- a/include/boost/contract/check.hpp +++ b/include/boost/contract/check.hpp @@ -44,8 +44,8 @@ RAII object to check contracts. try { \ if(!boost::contract::detail::checking::already()) { \ /* this name somewhat unique to min var shadow warnings */ \ - boost::contract::detail::checking\ - BOOST_CONTRACT_DETAIL_NAME2(chk, __LINE__); \ + boost::contract::detail::checking \ + BOOST_CONTRACT_DETAIL_NAME2(k, __LINE__); \ { assertion; } \ } \ } catch(...) { boost::contract::check_failure(); } \ diff --git a/include/boost/contract/constructor.hpp b/include/boost/contract/constructor.hpp index 9d8e44a..57de3da 100644 --- a/include/boost/contract/constructor.hpp +++ b/include/boost/contract/constructor.hpp @@ -41,7 +41,7 @@ postconditions when the enclosing class has no invariants. template specify_old_postcondition_except<> constructor(Class* obj) { // Must #if also on ..._PRECONDITIONS here because specify_... is generic. - #if !defined(BOOST_CONTRACT_NO_CONSTRUCTORS) || \ + #if !defined(BOOST_CONTRACT_NO_CONSTRUCTORS) || \ !defined(BOOST_CONTRACT_NO_PRECONDITIONS) return specify_old_postcondition_except<>( new boost::contract::detail::constructor(obj)); diff --git a/include/boost/contract/core/config.hpp b/include/boost/contract/core/config.hpp index eaac0eb..361cdce 100644 --- a/include/boost/contract/core/config.hpp +++ b/include/boost/contract/core/config.hpp @@ -7,6 +7,10 @@ // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt). // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html +// TODO: See if it is possible to move most/all config macros to header files so to allow to config the lib without recompile the lib .cpp, just from user code. But rebuild all tests/examples for all compilers to make sure this does not slow down compilation/runtime. + +// 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). + /** @file Facilities to configure this library compile-time and run-time behaviour. */ @@ -185,6 +189,10 @@ Facilities to configure this library compile-time and run-time behaviour. #define BOOST_CONTRACT_PRECONDITIONS_DISABLE_NO_ASSERTION #endif +#ifdef DOXYGEN + #define BOOST_CONTRACT_ALL_DISABLE_NO_ASSERTION +#endif + #ifdef DOXYGEN /** If defined, this library does not check preconditions at run-time (undefined diff --git a/include/boost/contract/core/exception.hpp b/include/boost/contract/core/exception.hpp index b11f0c8..781d456 100644 --- a/include/boost/contract/core/exception.hpp +++ b/include/boost/contract/core/exception.hpp @@ -315,6 +315,16 @@ This is often called only internally by this library. void /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ postcondition_failure(from where) /* can throw */; +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +set_old_failure(from_failure_handler const& f) + /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; + +from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +get_old_failure() /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; + +void /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ +old_failure(from where) /* can throw */; + from_failure_handler /** @cond */ BOOST_CONTRACT_DETAIL_DECLSPEC /** @endcond */ set_except_failure(from_failure_handler const& f) /** @cond */ BOOST_NOEXCEPT_OR_NOTHROW /** @endcond */; diff --git a/include/boost/contract/core/virtual.hpp b/include/boost/contract/core/virtual.hpp index 891a0fa..e7e973b 100644 --- a/include/boost/contract/core/virtual.hpp +++ b/include/boost/contract/core/virtual.hpp @@ -70,8 +70,8 @@ class virtual_ : private boost::noncopyable { // Avoid copy queue, stack, etc. !defined(BOOST_CONTRACT_NO_EXCEPTS) no_action, #endif - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - push_old_init, + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) #endif #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS check_entry_inv, @@ -79,24 +79,43 @@ class virtual_ : private boost::noncopyable { // Avoid copy queue, stack, etc. #ifndef BOOST_CONTRACT_NO_PRECONDITIONS check_pre, #endif - #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ - !defined(BOOST_CONTRACT_NO_EXCEPTS) - call_old_copy, - push_old_copy, - #endif #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS check_exit_inv, #endif #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) + push_old_init, + call_old_copy, + push_old_copy, pop_old_copy, + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS check_post, - // TODO: Why would this need to have the same value and check_post but not of check_except? There might be a bug here... - pop_old_init = check_post, // These must be the same value. + #endif + #ifndef BOOST_CONTRACT_NO_EXCEPTS check_except, #endif }; + #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) { + return + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + a == check_post + #endif + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) && \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) + || + #endif + #ifndef BOOST_CONTRACT_NO_EXCEPTS + a == check_except + #endif + ; + } + #endif + #if !defined(BOOST_CONTRACT_NO_INVARIANTS) || \ !defined(BOOST_CONTRACT_NO_PRECONDITIONS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ diff --git a/include/boost/contract/detail/condition/cond_base.hpp b/include/boost/contract/detail/condition/cond_base.hpp index 32596d9..f2a7883 100644 --- a/include/boost/contract/detail/condition/cond_base.hpp +++ b/include/boost/contract/detail/condition/cond_base.hpp @@ -7,7 +7,14 @@ // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt). // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html -// TODO: Try to see if *all* inheritance can be replaced with some static polymorphism (strategies, policies, etc.) so I will have no virtual functions at all. Then I could measure if there is any compile/run-time change by compiling and running all examples (or tests) on MSVC, GCC, and CLang... +// NOTE: It seemed not possible to implement this library without inheritance +// here because some sort of base type needs to be used to hold contract objects +// in instances of boost::contract::check while polymorphically calling +// init and destructor functions to check contracts at entry and exit. This +// could be possible without inheritance only if boost::contract::check was made +// a template type but that would complicate user code. In any case, early +// experimentation with removing this base class and its virtual methods did not +// seem to reduce compilation and/or run time. #include #include @@ -102,8 +109,7 @@ protected: void copy_old() { if(failed()) return; try { if(old_) old_(); } - // TODO: This is no longer correct because old can fail for either postconditions or excepts... should this just call its own failure handler old_failure? - catch(...) { fail(&boost::contract::postcondition_failure); } + catch(...) { fail(&boost::contract::old_failure); } } #endif diff --git a/include/boost/contract/detail/condition/cond_with_inv.hpp b/include/boost/contract/detail/condition/cond_with_inv.hpp index b09a7f0..148851e 100644 --- a/include/boost/contract/detail/condition/cond_with_inv.hpp +++ b/include/boost/contract/detail/condition/cond_with_inv.hpp @@ -126,13 +126,14 @@ protected: private: #ifndef BOOST_CONTRACT_NO_INVARIANTS - void check_inv(bool on_entry, bool static_only, bool both_const_cv) { + // Static, cv, and const inv in that order as strongest qualifier first. + void check_inv(bool on_entry, bool static_only, bool const_and_cv) { if(this->failed()) return; try { // Static members only check static inv. check_static_inv(); if(!static_only) { - if(both_const_cv) { + if(const_and_cv) { check_cv_inv(); check_const_inv(); } else if(boost::is_volatile::value) { diff --git a/include/boost/contract/detail/condition/cond_with_post.hpp b/include/boost/contract/detail/condition/cond_with_post.hpp index f5b3a28..ab16778 100644 --- a/include/boost/contract/detail/condition/cond_with_post.hpp +++ b/include/boost/contract/detail/condition/cond_with_post.hpp @@ -47,8 +47,7 @@ public: explicit cond_with_post(boost::contract::from from) : cond_base(from) {} #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS -private: - typedef typename boost::mpl::if_, + private: typedef typename boost::mpl::if_, boost::optional::type>::type const&> const& , diff --git a/include/boost/contract/detail/condition/cond_with_subcontracting.hpp b/include/boost/contract/detail/condition/cond_with_subcontracting.hpp index 4dc893f..14f31ae 100644 --- a/include/boost/contract/detail/condition/cond_with_subcontracting.hpp +++ b/include/boost/contract/detail/condition/cond_with_subcontracting.hpp @@ -249,7 +249,7 @@ protected: #ifndef BOOST_CONTRACT_NO_EXCEPTS void check_subcontracted_except() { exec_and(boost::contract::virtual_::check_except, - &cond_with_subcontracting::check_except); + &cond_with_subcontracting::check_virtual_except); } #endif @@ -282,17 +282,20 @@ private: this->copy_old(); if(base_call_) v_->action_ = a; } - #endif - - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - void check_virtual_post() { + + void pop_base_old() { if(base_call_) { boost::contract::virtual_::action_enum a = v_->action_; v_->action_ = boost::contract::virtual_::pop_old_copy; this->copy_old(); v_->action_ = a; - } - + } // Else, do nothing (for base calls only). + } + #endif + + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + void check_virtual_post() { + pop_base_old(); typedef typename boost::remove_reference::type>::type r_type; boost::optional r; // No result copy in this code. @@ -331,7 +334,9 @@ private: template typename boost::enable_if >::type - check_virtual_post_with_result(Result const& r) { this->check_post(r); } + check_virtual_post_with_result(Result const& r) { + this->check_post(r); + } template typename boost::disable_if >::type @@ -340,6 +345,13 @@ private: this->check_post(*r); } #endif + + #ifndef BOOST_CONTRACT_NO_EXCEPTS + void check_virtual_except() { + pop_base_old(); + this->check_except(); + } + #endif #if !defined(BOOST_CONTRACT_NO_INVARIANTS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ diff --git a/include/boost/contract/detail/inlined/core/exception.hpp b/include/boost/contract/detail/inlined/core/exception.hpp index 84e88d8..9150919 100644 --- a/include/boost/contract/detail/inlined/core/exception.hpp +++ b/include/boost/contract/detail/inlined/core/exception.hpp @@ -102,7 +102,8 @@ void assertion_failure::init() { namespace exception_ { enum failure_key { - entry_inv_key, exit_inv_key, pre_key, post_key, except_key, check_key + entry_inv_key, exit_inv_key, pre_key, post_key, old_key, except_key, + check_key }; template @@ -113,6 +114,7 @@ namespace exception_ { case exit_inv_key: k = "exit invariant "; break; case pre_key: k = "precondition "; break; case post_key: k = "postcondition "; break; + case old_key: k = "old copy "; break; case except_key: k = "except "; break; case check_key: k = "check "; break; // No default (so compiler warning/error on missing enum case). @@ -121,7 +123,7 @@ namespace exception_ { catch(boost::contract::assertion_failure const& error) { // what = "assertion '...' failed: ...". std::cerr << k << error.what() << std::endl; - } catch(...) { + } catch(...) { // old_key prints this, not above. std::cerr << k << "threw following exception:" << std::endl << boost::current_exception_diagnostic_information(); } @@ -153,6 +155,11 @@ namespace exception_ { #endif from_failure_handler post_failure_handler = &default_from_handler; + #ifndef BOOST_CONTRACT_DISABLE_THREADS + boost::mutex old_failure_mutex; + #endif + from_failure_handler old_failure_handler = &default_from_handler; + #ifndef BOOST_CONTRACT_DISABLE_THREADS boost::mutex except_failure_mutex; #endif @@ -216,6 +223,22 @@ void postcondition_failure(from where) /* can throw */ { exception_::post_failure_handler, where); } +from_failure_handler set_old_failure(from_failure_handler const& f) + BOOST_NOEXCEPT_OR_NOTHROW { + BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::old_failure_mutex, + from_failure_handler, exception_::old_failure_handler, f); +} + +from_failure_handler get_old_failure() BOOST_NOEXCEPT_OR_NOTHROW { + BOOST_CONTRACT_EXCEPTION_HANDLER_GET_(exception_::old_failure_mutex, + exception_::old_failure_handler); +} + +void old_failure(from where) /* can throw */ { + BOOST_CONTRACT_EXCEPTION_HANDLER_(exception_::old_failure_mutex, + exception_::old_failure_handler, where); +} + from_failure_handler set_except_failure(from_failure_handler const& f) BOOST_NOEXCEPT_OR_NOTHROW { BOOST_CONTRACT_EXCEPTION_HANDLER_SET_(exception_::except_failure_mutex, diff --git a/include/boost/contract/detail/inlined/detail/checking.hpp b/include/boost/contract/detail/inlined/detail/checking.hpp index dcbf5ef..11c4087 100644 --- a/include/boost/contract/detail/inlined/detail/checking.hpp +++ b/include/boost/contract/detail/inlined/detail/checking.hpp @@ -15,24 +15,32 @@ namespace boost { namespace contract { namespace detail { checking::checking() { - #ifndef BOOST_CONTRACT_DISABLE_THREADS - boost::lock_guard lock(mutex_); - #endif - checking_ = true; + #ifndef BOOST_CONTRACT_ALL_DISABLE_NO_ASSERTION + #ifndef BOOST_CONTRACT_DISABLE_THREADS + boost::lock_guard lock(mutex_); + #endif + checking_ = true; + #endif // Else, do nothing. } checking::~checking() { - #ifndef BOOST_CONTRACT_DISABLE_THREADS - boost::lock_guard lock(mutex_); - #endif - checking_ = false; + #ifndef BOOST_CONTRACT_ALL_DISABLE_NO_ASSERTION + #ifndef BOOST_CONTRACT_DISABLE_THREADS + boost::lock_guard lock(mutex_); + #endif + checking_ = false; + #endif // Else, do nothing. } bool checking::already() { - #ifndef BOOST_CONTRACT_DISABLE_THREADS - boost::lock_guard lock(mutex_); + #ifndef BOOST_CONTRACT_ALL_DISABLE_NO_ASSERTION + #ifndef BOOST_CONTRACT_DISABLE_THREADS + boost::lock_guard lock(mutex_); + #endif + return checking_; + #else + return false; // Never already checking (so no assertion ever disabled). #endif - return checking_; } bool checking::checking_ = false; diff --git a/include/boost/contract/detail/operation/public_function.hpp b/include/boost/contract/detail/operation/public_function.hpp index 7a78bbb..cfacafe 100644 --- a/include/boost/contract/detail/operation/public_function.hpp +++ b/include/boost/contract/detail/operation/public_function.hpp @@ -69,7 +69,8 @@ private: !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) void init() /* override */ { - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) this->init_subcontracted_old(); #endif if(!this->base_call()) { diff --git a/include/boost/contract/function.hpp b/include/boost/contract/function.hpp index ddeb8c4..55eb9cb 100644 --- a/include/boost/contract/function.hpp +++ b/include/boost/contract/function.hpp @@ -2,8 +2,6 @@ #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 cb395a7..125f572 100644 --- a/include/boost/contract/old.hpp +++ b/include/boost/contract/old.hpp @@ -97,19 +97,29 @@ programmers can manually copy old value expressions without using this macro /* CODE */ -/** @cond */ -namespace boost { - namespace contract { - class old_value; - } +namespace boost { namespace contract { - // Needed because `old_value` incomplete type when trait used. - template<> - struct is_copy_constructible : true_type {}; -} +template +struct is_old_value_copyable : boost::is_copy_constructible {}; + +/** @cond */ +class old_value; + +template<> // Needed because `old_value` incomplete type when trait first used. +struct is_old_value_copyable : boost::true_type {}; /** @endcond */ -namespace boost { namespace contract { +template // Used only if is_old_value_copyable. +class old_value_copy { +public: + explicit old_value_copy(T const& value) : + value_(value) {} // This makes the one single copy of T. + + T const& value() const { return value_; } + +private: + T const value_; +}; template class old_ptr_if_copyable; @@ -144,11 +154,12 @@ public: */ T const& operator*() const { BOOST_STATIC_ASSERT_MSG( - boost::is_copy_constructible::value, -"old_ptr requires T copy constructor, otherwise use old_ptr_if_copyable" + boost::contract::is_old_value_copyable::value, + "old_ptr requires T copy constructible (see " + "is_old_value_copyable), otherwise use old_ptr_if_copyable" ); - BOOST_CONTRACT_DETAIL_DEBUG(ptr_); - return *ptr_; + BOOST_CONTRACT_DETAIL_DEBUG(typed_copy_); + return typed_copy_->value(); } /** @@ -161,14 +172,17 @@ 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_if_copyable" + boost::contract::is_old_value_copyable::value, + "old_ptr requires T copy constructible (see " + "is_old_value_copyable), otherwise use old_ptr_if_copyable" ); - return ptr_.operator->(); + if(typed_copy_) return &typed_copy_->value(); + return 0; } #ifndef DOXYGEN - BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(old_ptr, !!ptr_) + BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(old_ptr, + !!typed_copy_) #else /** Check if this old value pointer is null or not (safe-bool operator). @@ -179,20 +193,19 @@ public: /** @cond */ private: - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - explicit old_ptr(boost::shared_ptr ptr) : - ptr_(ptr) {} + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) + explicit old_ptr(boost::shared_ptr > old) + : typed_copy_(old) {} #endif - boost::shared_ptr ptr_; + boost::shared_ptr > typed_copy_; friend class old_pointer; 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). @@ -211,6 +224,8 @@ public: /** Construct this object as a null old value pointer. */ old_ptr_if_copyable() {} + + // 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). /** Construct this object from an old value pointer of copyable-only types. @@ -219,7 +234,7 @@ public: @param other Copyable-only old value pointer. */ /* implicit */ old_ptr_if_copyable(old_ptr const& other) : - ptr_(other.ptr_) {} + typed_copy_(other.typed_copy_) {} /** Dereference this old value pointer. @@ -230,8 +245,8 @@ public: old vale as a constant reference). */ T const& operator*() const { - BOOST_CONTRACT_DETAIL_DEBUG(ptr_); - return *ptr_; + BOOST_CONTRACT_DETAIL_DEBUG(typed_copy_); + return typed_copy_->value(); } /** @@ -243,11 +258,13 @@ public: old value as a constant pointer to a constant object). */ T const* const operator->() const { - return ptr_.operator->(); + if(typed_copy_) return &typed_copy_->value(); + return 0; } #ifndef DOXYGEN - BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(old_ptr_if_copyable, !!ptr_) + BOOST_CONTRACT_DETAIL_OPERATOR_SAFE_BOOL(old_ptr_if_copyable, + !!typed_copy_) #else /** Check if this old value pointer is null or not (safe-bool operator). @@ -258,12 +275,13 @@ public: /** @cond */ private: - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - explicit old_ptr_if_copyable(boost::shared_ptr ptr) : - ptr_(ptr) {} + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) + explicit old_ptr_if_copyable(boost::shared_ptr > old) + : typed_copy_(old) {} #endif - boost::shared_ptr ptr_; + boost::shared_ptr > typed_copy_; friend class old_pointer; /** @endcond */ @@ -296,11 +314,14 @@ public: template /* implicit */ old_value( T const& old, - typename boost::enable_if >::type* = 0 + typename boost::enable_if + >::type* = 0 ) - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - : ptr_(boost::make_shared(old)) // The one single T's copy. - #endif // Else, leave ptr_ null (and no copy of T). + #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). {} /** @@ -314,15 +335,17 @@ public: template /* implicit */ old_value( T const& old, - typename boost::disable_if >::type* = 0 - ) {} // Leave ptr_ null (and no copy of T). + typename boost::disable_if + >::type* = 0 + ) {} // Leave ptr_ null (thus no copy of T). /** @cond */ private: explicit old_value() {} - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - boost::shared_ptr ptr_; + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) + boost::shared_ptr untyped_copy_; // Type erasure. #endif friend class old_pointer; @@ -350,7 +373,7 @@ public: */ template /* implicit */ operator old_ptr_if_copyable() { - return ptr >(); + return get >(); } /** @@ -364,73 +387,72 @@ public: */ template /* implicit */ operator old_ptr() { - return ptr >(); + return get >(); } /** @cond */ private: explicit old_pointer(virtual_* v, old_value const& old) - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - : v_(v), ptr_(old.ptr_) + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) + : v_(v), untyped_copy_(old.untyped_copy_) #endif {} template - Ptr ptr() { - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS - if(!boost::is_copy_constructible< - typename Ptr::element_type>::value) { - BOOST_CONTRACT_DETAIL_DEBUG(!ptr_); // Non-copyable so no old... - return Ptr(); // ...and return null. + Ptr get() { + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) + if(!boost::contract::is_old_value_copyable::value) { + BOOST_CONTRACT_DETAIL_DEBUG(!untyped_copy_); + return Ptr(); // Non-copyable so no old value and return null. } else if(!v_ && boost::contract::detail::checking::already()) { - // Return null shared ptr (see after if statement). + return Ptr(); // Not checking (so return null). } else if(!v_) { - BOOST_CONTRACT_DETAIL_DEBUG(ptr_); - boost::shared_ptr old = - boost::static_pointer_cast< - typename Ptr::element_type const>(ptr_) - ; - BOOST_CONTRACT_DETAIL_DEBUG(old); - return Ptr(old); + BOOST_CONTRACT_DETAIL_DEBUG(untyped_copy_); + typedef old_value_copy copied_type; + boost::shared_ptr typed_copy = // Un-erase type. + 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) { - BOOST_CONTRACT_DETAIL_DEBUG(ptr_); + BOOST_CONTRACT_DETAIL_DEBUG(untyped_copy_); if(v_->action_ == boost::contract::virtual_::push_old_init) { - v_->old_inits_.push(ptr_); + v_->old_inits_.push(untyped_copy_); } else { - v_->old_copies_.push(ptr_); + v_->old_copies_.push(untyped_copy_); } - return Ptr(); - } else if(v_->action_ == boost::contract::virtual_::pop_old_init || + 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(!ptr_); - boost::shared_ptr ptr; - if(v_->action_ == boost::contract::virtual_::pop_old_init) { - ptr = v_->old_inits_.front(); - } else { - ptr = v_->old_copies_.top(); - } - BOOST_CONTRACT_DETAIL_DEBUG(ptr); - if(v_->action_ == boost::contract::virtual_::pop_old_init) { + 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(); } - boost::shared_ptr old = - boost::static_pointer_cast< - typename Ptr::element_type const>(ptr) - ; - BOOST_CONTRACT_DETAIL_DEBUG(old); - return Ptr(old); + typedef old_value_copy copied_type; + boost::shared_ptr typed_copy = // Un-erase type. + boost::static_pointer_cast(untyped_copy); + BOOST_CONTRACT_DETAIL_DEBUG(typed_copy); + return Ptr(typed_copy); } - BOOST_CONTRACT_DETAIL_DEBUG(!ptr_); + BOOST_CONTRACT_DETAIL_DEBUG(!untyped_copy_); #endif return Ptr(); } - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) virtual_* v_; - boost::shared_ptr ptr_; + boost::shared_ptr untyped_copy_; // Type erasure. #endif friend old_pointer make_old(old_value const&); @@ -490,7 +512,8 @@ being checked (see @RefMacro{BOOST_CONTRACT_NO_POSTCONDITIONS}). @return True if old values are being copied, false otherwise. */ bool copy_old() { - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) return !boost::contract::detail::checking::already(); #else return false; // Post checking disabled, so never copy old values. @@ -509,7 +532,8 @@ being checked (see @RefMacro{BOOST_CONTRACT_NO_POSTCONDITIONS}). @return True if old values are being copied, false otherwise. */ bool copy_old(virtual_* v) { - #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ + !defined(BOOST_CONTRACT_NO_EXCEPTS) if(!v) return !boost::contract::detail::checking::already(); return v->action_ == boost::contract::virtual_::push_old_init || v->action_ == boost::contract::virtual_::push_old_copy; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 3fe3c55..e53eff3 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -126,6 +126,8 @@ test-suite public_function : [ subdir-run public_function : ifdef_contracts ] [ subdir-run public_function : body_throw ] + [ subdir-run public_function : body_throw_virtual ] + [ subdir-run public_function : body_throw_virtual_branch ] [ subdir-run public_function : old_throw ] [ subdir-run public_function : max_args0 ] @@ -279,7 +281,8 @@ test-suite call_if : [ subdir-run call_if : equal_to_cxx14 : # Requires C++14. clang:-std=c++1y ] - [ subdir-run call_if : condition_if ] - [ subdir-compile-fail call_if : no_condition_if_error ] + [ subdir-run call_if : no_equal_condition_if ] + [ subdir-run call_if : no_equal_call_if ] + [ subdir-compile-fail call_if : no_equal_error ] ; diff --git a/test/call_if/no_condition_if_error.cpp b/test/call_if/no_condition_if_error.cpp deleted file mode 100644 index 0573c70..0000000 --- a/test/call_if/no_condition_if_error.cpp +++ /dev/null @@ -1,40 +0,0 @@ - -// 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 assertion error when operations to check them missing (e.g., `==`). - -#include -#include -#include -#include - -template -void push_back(std::vector& vect, T const& value) { - boost::contract::check c = boost::contract::function() - .postcondition([&] { - BOOST_CONTRACT_ASSERT(vect.back() == value); // Error (j has no ==). - }) - ; - vect.push_back(value); -} - -struct j { // Type without operator==. - explicit j(int i) : j_(i) {} -private: - int j_; -}; - -int main() { - std::vector vi; - push_back(vi, 123); - - j jj(456); - std::vector vj; - push_back(vj, jj); - - return 0; -} - diff --git a/test/old/no_equal.cpp b/test/call_if/no_equal_call_if.cpp similarity index 100% rename from test/old/no_equal.cpp rename to test/call_if/no_equal_call_if.cpp diff --git a/test/call_if/condition_if.cpp b/test/call_if/no_equal_condition_if.cpp similarity index 100% rename from test/call_if/condition_if.cpp rename to test/call_if/no_equal_condition_if.cpp diff --git a/test/old/no_equal_error.cpp b/test/call_if/no_equal_error.cpp similarity index 100% rename from test/old/no_equal_error.cpp rename to test/call_if/no_equal_error.cpp diff --git a/test/constructor/old_throw.cpp b/test/constructor/old_throw.cpp index 12baa84..2fc4ff0 100644 --- a/test/constructor/old_throw.cpp +++ b/test/constructor/old_throw.cpp @@ -94,8 +94,7 @@ struct a int main() { std::ostringstream ok; - boost::contract::set_postcondition_failure( - [] (boost::contract::from) { throw; }); + boost::contract::set_old_failure([] (boost::contract::from) { throw; }); try { out.str(""); diff --git a/test/destructor/old_throw.cpp b/test/destructor/old_throw.cpp index 926ac85..5baffd6 100644 --- a/test/destructor/old_throw.cpp +++ b/test/destructor/old_throw.cpp @@ -77,8 +77,7 @@ struct a int main() { std::ostringstream ok; - boost::contract::set_postcondition_failure( - [] (boost::contract::from) { throw; }); + boost::contract::set_old_failure([] (boost::contract::from) { throw; }); try { { diff --git a/test/function/old_throw.cpp b/test/function/old_throw.cpp index f97d416..6b673b9 100644 --- a/test/function/old_throw.cpp +++ b/test/function/old_throw.cpp @@ -31,8 +31,7 @@ void f() { int main() { std::ostringstream ok; - boost::contract::set_postcondition_failure( - [] (boost::contract::from) { throw; }); + boost::contract::set_old_failure([] (boost::contract::from) { throw; }); try { out.str(""); diff --git a/test/invariant/decl.hpp b/test/invariant/decl.hpp index 55f8b4a..8fcc410 100644 --- a/test/invariant/decl.hpp +++ b/test/invariant/decl.hpp @@ -428,7 +428,7 @@ int main() { { // Test non-volatile call with bases. out.str(""); a aa; - ok.str(""); ok + ok.str(""); ok // Ctors always check cv_inv (even if not volatile). #ifndef BOOST_CONTRACT_NO_PRECONDITIONS << "a::ctor::pre" << std::endl << "b::ctor::pre" << std::endl @@ -486,7 +486,7 @@ int main() { out.str(""); aa.f('a'); - ok.str(""); ok // Mutable checks static and const (but not cv) inv. + ok.str(""); ok // Non-cv checks static and const (but not cv) inv. #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS #ifdef BOOST_CONTRACT_TEST_STATIC_INV << "b::static_inv" << std::endl @@ -592,7 +592,7 @@ int main() { out.str(""); } // Call a's destructor. - ok.str(""); ok + ok.str(""); ok // Dtors always check cv_inv (even if not volatile). #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS #ifdef BOOST_CONTRACT_TEST_STATIC_INV << "a::static_inv" << std::endl @@ -742,7 +742,7 @@ int main() { { // Test non-volatile call with no bases. out.str(""); b bb; - ok.str(""); ok + ok.str(""); ok // Ctors always check cv_inv (even if not volatile). #ifndef BOOST_CONTRACT_NO_PRECONDITIONS << "b::ctor::pre" << std::endl #endif @@ -775,7 +775,7 @@ int main() { out.str(""); bb.f('b'); - ok.str(""); ok // Mutable checks static and const (but not cv) inv. + ok.str(""); ok // Non-cv checks static and const (but not cv) inv. #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS #ifdef BOOST_CONTRACT_TEST_STATIC_INV << "b::static_inv" << std::endl @@ -807,7 +807,7 @@ int main() { out.str(""); } // Call b's destructor. - ok.str(""); ok + ok.str(""); ok // Dtors always check cv_inv (even if not volatile). #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS #ifdef BOOST_CONTRACT_TEST_STATIC_INV << "b::static_inv" << std::endl diff --git a/test/old/auto.cpp b/test/old/auto.cpp index c19d544..066a1a7 100644 --- a/test/old/auto.cpp +++ b/test/old/auto.cpp @@ -11,21 +11,28 @@ #include #include #include + #include #endif int main() { #ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS - int x = 0; + int x = -123; auto old_x = BOOST_CONTRACT_OLD(x); + x = 123; BOOST_STATIC_ASSERT(boost::is_same >::value); + BOOST_TEST_EQ(*old_x, -123); + BOOST_TEST_EQ(x, 123); boost::contract::virtual_* v = 0; - char y = 'a'; + char y = 'j'; auto old_y = BOOST_CONTRACT_OLD(v, y); + y = 'k'; BOOST_STATIC_ASSERT(boost::is_same >::value); + BOOST_TEST_EQ(*old_y, 'j'); + BOOST_TEST_EQ(y, 'k'); #endif // Else, nothing to test. - return 0; + return boost::report_errors(); } diff --git a/test/old/if_copyable.cpp b/test/old/if_copyable.cpp index a016079..85a18ce 100644 --- a/test/old/if_copyable.cpp +++ b/test/old/if_copyable.cpp @@ -6,6 +6,7 @@ // Test old values of non-copyable types. +#include "if_copyable.hpp" #include #include #include @@ -26,7 +27,7 @@ struct b { boost::contract::check c = boost::contract::public_function(v, this) .postcondition([&] { if(old_x) { - BOOST_CONTRACT_ASSERT(x > *old_x); + BOOST_CONTRACT_ASSERT(x == *old_x + T(1)); ++old_checks; } }) @@ -51,7 +52,7 @@ struct a override_next>(v, &a::next, this, x) .postcondition([&] { if(old_x) { - BOOST_CONTRACT_ASSERT(x > *old_x); + BOOST_CONTRACT_ASSERT(x == *old_x + T(1)); ++old_checks; } }) @@ -67,40 +68,14 @@ void next(T& x) { boost::contract::check c = boost::contract::function() .postcondition([&] { if(old_x) { - BOOST_CONTRACT_ASSERT(x > *old_x); + BOOST_CONTRACT_ASSERT(x == *old_x + T(1)); ++old_checks; } }) ; ++x; } - -struct cp { - explicit cp(int value) : value_(value) {} - - friend cp& operator++(cp& me) { ++me.value_; return me; } - - friend bool operator>(cp const& left, cp const& right) { - return left.value_ > right.value_; - } - -private: - int value_; -}; - -struct ncp : private boost::noncopyable { - explicit ncp(int value) : value_(value) {} - - friend ncp& operator++(ncp& me) { ++me.value_; return me; } - - friend bool operator>(ncp const& left, ncp const& right) { - return left.value_ > right.value_; - } - -private: - int value_; -}; - + int main() { int i = 1; // Test built-in copyable type. cp c(1); // Test custom copyable type. diff --git a/test/old/if_copyable.hpp b/test/old/if_copyable.hpp new file mode 100644 index 0000000..5930843 --- /dev/null +++ b/test/old/if_copyable.hpp @@ -0,0 +1,45 @@ + +#ifndef BOOST_CONTRACT_TEST_IF_COPYABLE_HPP_ +#define BOOST_CONTRACT_TEST_IF_COPYABLE_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 old values of non-copyable types. + +#define BOOST_CONTRACT_TEST_IF_COPYABLE_TYPE(class_) \ + public: \ + explicit class_(int value) : value_(value) {} \ + \ + friend class_& operator++(class_& me) { ++me.value_; return me; } \ + \ + friend bool operator>(class_ const& left, class_ const& right) { \ + return left.value_ > right.value_; \ + } \ + \ + friend bool operator==(class_ const& left, class_ const& right) { \ + return left.value_ == right.value_; \ + } \ + \ + friend class_ operator+(class_ const& left, class_ const& right) { \ + return class_(left.value_ + right.value_); \ + } \ + \ + private: \ + int value_; + +struct cp { // Copyable type. + BOOST_CONTRACT_TEST_IF_COPYABLE_TYPE(cp) +}; + +struct ncp { + BOOST_CONTRACT_TEST_IF_COPYABLE_TYPE(ncp) + +private: // Non (publicly) copyable type. + ncp(ncp const& other) : value_(other.value_) {} +}; + +#endif // #include guard + diff --git a/test/old/if_copyable_error.cpp b/test/old/if_copyable_error.cpp index 48a7959..d0dc701 100644 --- a/test/old/if_copyable_error.cpp +++ b/test/old/if_copyable_error.cpp @@ -6,6 +6,7 @@ // Test forcing compiler error for old values of non-copyable types. +#include "if_copyable.hpp" #include #include #include @@ -17,38 +18,13 @@ void next(T& x) { boost::contract::old_ptr old_x = BOOST_CONTRACT_OLD(x); boost::contract::check c = boost::contract::function() .postcondition([&] { - BOOST_CONTRACT_ASSERT(x > *old_x); // No need to check if(old_x)... + // No need to check `if(old_x) ...` here. + BOOST_CONTRACT_ASSERT(x == *old_x + T(1)); }) ; ++x; } -struct cp { - explicit cp(int value) : value_(value) {} - - friend cp& operator++(cp& me) { ++me.value_; return me; } - - friend bool operator>(cp const& left, cp const& right) { - return left.value_ > right.value_; - } - -private: - int value_; -}; - -struct ncp : private boost::noncopyable { - explicit ncp(int value) : value_(value) {} - - friend ncp& operator++(ncp& me) { ++me.value_; return me; } - - friend bool operator>(ncp const& left, ncp const& right) { - return left.value_ > right.value_; - } - -private: - int value_; -}; - int main() { int i = 1; // Test built-in copyable type. cp c(1); // Test custom copyable type. diff --git a/test/old/no_make_old_error.hpp b/test/old/no_make_old_error.hpp index 5663211..4b2a98f 100644 --- a/test/old/no_make_old_error.hpp +++ b/test/old/no_make_old_error.hpp @@ -14,9 +14,8 @@ int main() { int x = 1; - // Error (missing outer make_old(...)). BOOST_CONTRACT_TEST_OLD_PTR_TYPE old_x = boost::contract::copy_old() ? - x : boost::contract::null_old(); + x : boost::contract::null_old(); // Error (missing make_old(...)). return 0; } diff --git a/test/public_function/body_throw.cpp b/test/public_function/body_throw.cpp index 9637485..0946efb 100644 --- a/test/public_function/body_throw.cpp +++ b/test/public_function/body_throw.cpp @@ -4,143 +4,113 @@ // 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 throw from public function (derived) body. +// Test public member function body throws with subcontracting. -#include "../detail/oteststream.hpp" -#include -#include -#include -#include -#include +#include "contracts.hpp" +#include +#include #include #include -boost::contract::test::detail::oteststream out; - -struct c { - static void static_invariant() { out << "c::static_inv" << std::endl; } - void invariant() const { out << "c::inv" << std::endl; } - - struct err {}; - - virtual void f(boost::contract::virtual_* v = 0) { - boost::contract::check c = boost::contract::public_function(v, this) - .precondition([] { - out << "c::f::pre" << std::endl; - BOOST_CONTRACT_ASSERT(false); // To check derived pre. - }) - .old([] { out << "c::f::old" << std::endl; }) - .postcondition([] { out << "c::f::post" << std::endl; }) - .except([] { out << "c::f::except" << std::endl; }) - ; - out << "c::f::body" << std::endl; - throw c::err(); // Test body throws. - } -}; - -struct b - #define BASES public c - : BASES -{ - typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; - #undef BASES - - static void static_invariant() { out << "b::static_inv" << std::endl; } - void invariant() const { out << "b::inv" << std::endl; } - - struct err {}; - - virtual void f(boost::contract::virtual_* v = 0) /* override */ { - boost::contract::check c = boost::contract::public_function( - v, &b::f, this) - .precondition([] { - out << "b::f::pre" << std::endl; - BOOST_CONTRACT_ASSERT(false); // To check derived pre. - }) - .old([] { out << "b::f::old" << std::endl; }) - .postcondition([] { out << "b::f::post" << std::endl; }) - .except([] { out << "b::f::except" << std::endl; }) - ; - out << "b::f::body" << std::endl; - throw b::err(); // Test body throws. - } - BOOST_CONTRACT_OVERRIDE(f) -}; - -struct a - #define BASES public b - : BASES -{ - typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; - #undef BASES - - static void static_invariant() { out << "a::static_inv" << std::endl; } - void invariant() const { out << "a::inv" << std::endl; } - - struct err {}; - - void f(boost::contract::virtual_* v = 0) /* override */ { - boost::contract::check 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; }) - .except([] { out << "a::f::except" << std::endl; }) - ; - out << "a::f::body" << std::endl; - throw a::err(); // Test body throws. - } - BOOST_CONTRACT_OVERRIDE(f) -}; - int main() { std::ostringstream ok; - a aa; - b& ba = aa; // Test as virtual call via polymorphism. + a aa; // Test call to derived out-most leaf. + s_type s; s.value = "X"; // So body will throw. + out.str(""); + boost::optional r; try { - out.str(""); - ba.f(); + r = aa.f(s); BOOST_TEST(false); - } catch(a::err const&) { + } catch(except_error const&) { ok.str(""); ok #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "d::static_inv" << std::endl + << "d::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl << "c::static_inv" << std::endl << "c::inv" << std::endl - << "b::static_inv" << std::endl - << "b::inv" << std::endl << "a::static_inv" << std::endl << "a::inv" << std::endl #endif #ifndef BOOST_CONTRACT_NO_PRECONDITIONS - << "c::f::pre" << std::endl - << "b::f::pre" << std::endl - << "a::f::pre" << std::endl + << "d::f::pre" << std::endl #endif #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "d::f::old" << std::endl + << "e::f::old" << std::endl << "c::f::old" << std::endl - << "b::f::old" << std::endl << "a::f::old" << std::endl #endif - << "a::f::body" << std::endl // Test this threw. + << "a::f::body" << std::endl #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS - // Test no post (but still subcon inv and except) as body threw. + << "d::static_inv" << std::endl + << "d::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl << "c::static_inv" << std::endl << "c::inv" << std::endl - << "b::static_inv" << std::endl - << "b::inv" << std::endl << "a::static_inv" << std::endl << "a::inv" << std::endl #endif #ifndef BOOST_CONTRACT_NO_EXCEPTS + << "d::f::old" << std::endl + << "d::f::except" << std::endl + << "e::f::old" << std::endl + << "e::f::except" << std::endl + << "c::f::old" << std::endl << "c::f::except" << std::endl - << "b::f::except" << std::endl + // No old call here because not a base object. << "a::f::except" << std::endl #endif ; BOOST_TEST(out.eq(ok.str())); - } catch(...) { BOOST_TEST(false); } + #ifdef BOOST_CONTRACT_NO_EXEPTS + #define BOOST_CONTRACT_TEST_except 0 + #else + #define BOOST_CONTRACT_TEST_except 1 + #endif + + BOOST_TEST(!r); // Boost.Optional result not init (as body threw). + BOOST_TEST_EQ(s.value, "X"); + BOOST_TEST_EQ(s.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 4u, 0u)); + BOOST_TEST_EQ(s.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 4u, 0u)); + BOOST_TEST_EQ(s.ctors(), s.dtors() + 1); // 1 for local var. + + BOOST_TEST_EQ(aa.x.value, "a"); + BOOST_TEST_EQ(aa.x.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.x.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.x.ctors(), aa.x.dtors() + 1); // 1 for member var. + + BOOST_TEST_EQ(aa.y.value, "c"); + BOOST_TEST_EQ(aa.y.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.y.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.y.ctors(), aa.y.dtors() + 1); // 1 for member var. + + BOOST_TEST_EQ(aa.t<'d'>::z.value, "d"); + BOOST_TEST_EQ(aa.t<'d'>::z.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.t<'d'>::z.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.t<'d'>::z.ctors(), aa.t<'d'>::z.dtors() + 1); // 1 mem. + + BOOST_TEST_EQ(aa.t<'e'>::z.value, "e"); + BOOST_TEST_EQ(aa.t<'e'>::z.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.t<'e'>::z.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.t<'e'>::z.ctors(), aa.t<'e'>::z.dtors() + 1); // 1 mem. + + #undef BOOST_CONTRACT_TEST_except + } return boost::report_errors(); } diff --git a/test/public_function/body_throw_virtual.cpp b/test/public_function/body_throw_virtual.cpp new file mode 100644 index 0000000..e4d446a --- /dev/null +++ b/test/public_function/body_throw_virtual.cpp @@ -0,0 +1,118 @@ + +// 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 virtual public member function body throws with subcontracting. + +#include "contracts.hpp" +#include +#include +#include +#include + +int main() { + std::ostringstream ok; + + a aa; // Test call to derived out-most leaf. + c& ca = aa; // Test polymorphic virtual call (via reference to base c). + s_type s; s.value = "X"; // So body will throw. + out.str(""); + boost::optional r; + try { + r = ca.f(s); + BOOST_TEST(false); + } catch(except_error const&) { + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "d::static_inv" << std::endl + << "d::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + << "c::static_inv" << std::endl + << "c::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "d::f::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "d::f::old" << std::endl + << "e::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 + << "d::static_inv" << std::endl + << "d::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + << "c::static_inv" << std::endl + << "c::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_EXCEPTS + << "d::f::old" << std::endl + << "d::f::except" << std::endl + << "e::f::old" << std::endl + << "e::f::except" << std::endl + << "c::f::old" << std::endl + << "c::f::except" << std::endl + // No old call here because not a base object. + << "a::f::except" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + #ifdef BOOST_CONTRACT_NO_EXEPTS + #define BOOST_CONTRACT_TEST_except 0 + #else + #define BOOST_CONTRACT_TEST_except 1 + #endif + + BOOST_TEST(!r); // Boost.Optional result not init (as body threw). + BOOST_TEST_EQ(s.value, "X"); + BOOST_TEST_EQ(s.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 4u, 0u)); + BOOST_TEST_EQ(s.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 4u, 0u)); + BOOST_TEST_EQ(s.ctors(), s.dtors() + 1); // 1 for local var. + + // Cannot access x via ca, but only via aa. + BOOST_TEST_EQ(aa.x.value, "a"); + BOOST_TEST_EQ(aa.x.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.x.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(aa.x.ctors(), aa.x.dtors() + 1); // 1 for member var. + + BOOST_TEST_EQ(ca.y.value, "c"); + BOOST_TEST_EQ(ca.y.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(ca.y.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(ca.y.ctors(), aa.y.dtors() + 1); // 1 for member var. + + BOOST_TEST_EQ(ca.t<'d'>::z.value, "d"); + BOOST_TEST_EQ(ca.t<'d'>::z.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(ca.t<'d'>::z.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(ca.t<'d'>::z.ctors(), aa.t<'d'>::z.dtors() + 1); // 1 mem. + + BOOST_TEST_EQ(ca.t<'e'>::z.value, "e"); + BOOST_TEST_EQ(ca.t<'e'>::z.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(ca.t<'e'>::z.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(ca.t<'e'>::z.ctors(), aa.t<'e'>::z.dtors() + 1); // 1 mem. + + #undef BOOST_CONTRACT_TEST_except + } + return boost::report_errors(); +} + diff --git a/test/public_function/body_throw_virtual_branch.cpp b/test/public_function/body_throw_virtual_branch.cpp new file mode 100644 index 0000000..d6d6638 --- /dev/null +++ b/test/public_function/body_throw_virtual_branch.cpp @@ -0,0 +1,102 @@ + +// 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 virt pub func body throws with subcontr from middle of inheritance tree. + +#include "contracts.hpp" +#include +#include +#include +#include + +int main() { + std::ostringstream ok; + + c cc; // Test call to class at mid- inheritance tree (as base with bases). + s_type s; s.value = "X"; // So body will throw. + out.str(""); + boost::optional r; + try { + r = cc.f(s); + BOOST_TEST(false); + } catch(except_error const&) { + ok.str(""); ok + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + << "d::static_inv" << std::endl + << "d::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + << "c::static_inv" << std::endl + << "c::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_PRECONDITIONS + << "d::f::pre" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS + << "d::f::old" << std::endl + << "e::f::old" << std::endl + << "c::f::old" << std::endl + #endif + << "c::f::body" << std::endl + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + << "d::static_inv" << std::endl + << "d::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + << "c::static_inv" << std::endl + << "c::inv" << std::endl + #endif + #ifndef BOOST_CONTRACT_NO_EXCEPTS + << "d::f::old" << std::endl + << "d::f::except" << std::endl + << "e::f::old" << std::endl + << "e::f::except" << std::endl + // No old call here because not a base object. + << "c::f::except" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + #ifdef BOOST_CONTRACT_NO_EXEPTS + #define BOOST_CONTRACT_TEST_except 0 + #else + #define BOOST_CONTRACT_TEST_except 1 + #endif + + BOOST_TEST(!r); // Boost.Optional result not init (as body threw). + BOOST_TEST_EQ(s.value, "X"); + BOOST_TEST_EQ(s.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 3u, 0u)); + BOOST_TEST_EQ(s.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 3u, 0u)); + BOOST_TEST_EQ(s.ctors(), s.dtors() + 1); // 1 for local var. + + BOOST_TEST_EQ(cc.y.value, "c"); + BOOST_TEST_EQ(cc.y.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(cc.y.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(cc.y.ctors(), cc.y.dtors() + 1); // 1 for member var. + + BOOST_TEST_EQ(cc.t<'d'>::z.value, "d"); + BOOST_TEST_EQ(cc.t<'d'>::z.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(cc.t<'d'>::z.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(cc.t<'d'>::z.ctors(), cc.t<'d'>::z.dtors() + 1); // 1 mem. + + BOOST_TEST_EQ(cc.t<'e'>::z.value, "e"); + BOOST_TEST_EQ(cc.t<'e'>::z.copies(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(cc.t<'e'>::z.evals(), + BOOST_PP_IIF(BOOST_CONTRACT_TEST_except, 1u, 0u)); + BOOST_TEST_EQ(cc.t<'e'>::z.ctors(), cc.t<'e'>::z.dtors() + 1); // 1 mem. + + #undef BOOST_CONTRACT_TEST_except + } + return boost::report_errors(); +} + diff --git a/test/public_function/contracts.hpp b/test/public_function/contracts.hpp index 6ac3503..feb85f4 100644 --- a/test/public_function/contracts.hpp +++ b/test/public_function/contracts.hpp @@ -24,6 +24,8 @@ 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) {} @@ -53,7 +55,7 @@ struct t { virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0) = 0; }; -template +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()); @@ -63,7 +65,7 @@ result_type& t::f(s_type& s, boost::contract::virtual_* v) { 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); + BOOST_CONTRACT_ASSERT(s.value[0] == Id || s.value[0] == 'X'); }) .old([&] { out << Id << "::f::old" << std::endl; @@ -76,8 +78,14 @@ result_type& t::f(s_type& s, boost::contract::virtual_* v) { 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; } @@ -113,7 +121,7 @@ struct c override_f>(v, result, &c::f, this, s) .precondition([&] { out << "c::f::pre" << std::endl; - BOOST_CONTRACT_ASSERT(s.value == "C"); + BOOST_CONTRACT_ASSERT(s.value == "C" || s.value == "X"); }) .old([&] { out << "c::f::old" << std::endl; @@ -126,9 +134,15 @@ struct c 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; @@ -197,7 +211,7 @@ struct a override_f>(v, result, &a::f, this, s) .precondition([&] { out << "a::f::pre" << std::endl; - BOOST_CONTRACT_ASSERT(s.value == "A"); + BOOST_CONTRACT_ASSERT(s.value == "A" || s.value == "X"); }) .old([&] { out << "a::f::old" << std::endl; @@ -210,9 +224,15 @@ struct a 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; diff --git a/test/public_function/old_throw.cpp b/test/public_function/old_throw.cpp index 67e1c77..16c5147 100644 --- a/test/public_function/old_throw.cpp +++ b/test/public_function/old_throw.cpp @@ -99,8 +99,7 @@ struct a int main() { std::ostringstream ok; - boost::contract::set_postcondition_failure( - [] (boost::contract::from) { throw; }); + boost::contract::set_old_failure([] (boost::contract::from) { throw; }); a aa; b& ba = aa; // Test as virtual call via polymorphism. diff --git a/test/public_function/static_old_throw.cpp b/test/public_function/static_old_throw.cpp index 61724a1..41edbc8 100644 --- a/test/public_function/static_old_throw.cpp +++ b/test/public_function/static_old_throw.cpp @@ -36,8 +36,7 @@ struct a { int main() { std::ostringstream ok; - boost::contract::set_postcondition_failure( - [] (boost::contract::from) { throw; }); + boost::contract::set_old_failure([] (boost::contract::from) { throw; }); try { out.str("");