#ifndef BOOST_CONTRACT_DETAIL_COND_SUBCONTRACTING_HPP_ #define BOOST_CONTRACT_DETAIL_COND_SUBCONTRACTING_HPP_ // Copyright (C) 2008-2018 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 #if !defined(BOOST_CONTRACT_NO_PRECONDITIONS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) #include #endif #include #include #include #ifndef BOOST_CONTRACT_NO_CONDITIONS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_CONTRACT_PERMISSIVE #include #include #include #include #endif #include #include #endif #include #if !defined(BOOST_CONTRACT_NO_INVARIANTS) || \ !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) #include #endif #ifndef BOOST_CONTRACT_NO_PRECONDITIONS #include #include #endif #if !defined(BOOST_CONTRACT_NO_POSTCONDITIONS) || \ !defined(BOSOT_CONTRACT_NO_EXCEPTS) #include #include #include #include #include #endif namespace boost { namespace contract { namespace detail { namespace cond_subcontracting_ { // Exception signals (must not inherit). class signal_no_error {}; class signal_not_checked {}; } // O, VR, F, and Args-i can be none types (but C cannot). BOOST_CONTRACT_DETAIL_DECL_DETAIL_COND_SUBCONTRACTING_Z(1, /* is_friend = */ 0, O, VR, F, C, Args) : public cond_inv { // Non-copyable base. #ifndef BOOST_CONTRACT_NO_CONDITIONS template > class overridden_bases_of { struct search_bases { typedef typename boost::mpl::fold< typename boost::contract::access::base_types_of:: type, Result, // Fold: _1 = result, _2 = current base from base_types. boost::mpl::eval_if >, boost::mpl::_1 // Base in result, do not add it again. , boost::mpl::eval_if< typename O::template BOOST_CONTRACT_DETAIL_NAME1( has_member_function)< boost::mpl::_2, typename member_function_types:: result_type, typename member_function_types:: virtual_argument_types, typename member_function_types:: property_tag > , boost::mpl::push_back< overridden_bases_of, // Bases as * since for_each constructs them. boost::add_pointer > , overridden_bases_of > > >::type type; }; public: typedef typename boost::mpl::eval_if< boost::contract::access::has_base_types, search_bases , boost::mpl::identity // Return result (stop recursion). >::type type; }; typedef typename boost::mpl::eval_if, boost::mpl::vector<> , overridden_bases_of >::type overridden_bases; #ifndef BOOST_CONTRACT_PERMISSIVE BOOST_STATIC_ASSERT_MSG( (boost::mpl::or_< boost::is_same, boost::mpl::not_ > >::value), "subcontracting function specified as 'override' but does not " "override any contracted member function" ); #endif #else typedef boost::mpl::vector<> overridden_bases; #endif public: explicit cond_subcontracting( boost::contract::from from, boost::contract::virtual_* v, C* obj, VR& #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS // Avoid unused param warning. r #endif BOOST_CONTRACT_DETAIL_TVARIADIC_COMMA(BOOST_CONTRACT_MAX_ARGS) BOOST_CONTRACT_DETAIL_TVARIADIC_FPARAMS_Z(1, BOOST_CONTRACT_MAX_ARGS, Args, &, args) ) : cond_inv(from, obj) #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS , r_(r) #endif #ifndef BOOST_CONTRACT_NO_CONDITIONS BOOST_CONTRACT_DETAIL_TVARIADIC_COMMA(BOOST_CONTRACT_MAX_ARGS) BOOST_CONTRACT_DETAIL_TVARIADIC_TUPLE_INIT_Z(1, BOOST_CONTRACT_MAX_ARGS, args_, args) #endif { #ifndef BOOST_CONTRACT_NO_CONDITIONS if(v) { base_call_ = true; v_ = v; // Invariant: v_ never null if base_call_. BOOST_CONTRACT_DETAIL_DEBUG(v_); } else { base_call_ = false; if(!boost::mpl::empty::value) { v_ = new boost::contract::virtual_( boost::contract::virtual_::no_action); #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS v_->result_ptr_ = &r_; v_->result_type_name_ = typeid(VR).name(); v_->result_optional_ = is_optional::value; #endif } else v_ = 0; } #endif } #ifndef BOOST_CONTRACT_NO_CONDITIONS virtual ~cond_subcontracting() BOOST_NOEXCEPT_IF(false) { if(!base_call_) delete v_; } #endif protected: #ifndef BOOST_CONTRACT_NO_OLDS void init_subcontracted_old() { // Old values of overloaded func on stack (so no `f` param here). exec_and(boost::contract::virtual_::push_old_init_copy); } #endif #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS void check_subcontracted_entry_inv() { exec_and(boost::contract::virtual_::check_entry_inv, &cond_subcontracting::check_entry_inv); } #endif #ifndef BOOST_CONTRACT_NO_PRECONDITIONS void check_subcontracted_pre() { exec_or( boost::contract::virtual_::check_pre, &cond_subcontracting::check_pre, &boost::contract::precondition_failure ); } #endif #ifndef BOOST_CONTRACT_NO_OLDS void copy_subcontracted_old() { exec_and(boost::contract::virtual_::call_old_ftor, &cond_subcontracting::copy_virtual_old); } #endif #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS void check_subcontracted_exit_inv() { exec_and(boost::contract::virtual_::check_exit_inv, &cond_subcontracting::check_exit_inv); } #endif #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS void check_subcontracted_post() { exec_and(boost::contract::virtual_::check_post, &cond_subcontracting::check_virtual_post); } #endif #ifndef BOOST_CONTRACT_NO_EXCEPTS void check_subcontracted_except() { exec_and(boost::contract::virtual_::check_except, &cond_subcontracting::check_virtual_except); } #endif #ifndef BOOST_CONTRACT_NO_CONDITIONS bool base_call() const { return base_call_; } bool failed() const /* override */ { if(v_) return v_->failed_; else return cond_base::failed(); } void failed(bool value) /* override */ { if(v_) v_->failed_ = value; else cond_base::failed(value); } #endif private: #ifndef BOOST_CONTRACT_NO_OLDS void copy_virtual_old() { boost::contract::virtual_::action_enum a; if(base_call_) { a = v_->action_; v_->action_ = boost::contract::virtual_::push_old_ftor_copy; } this->copy_old(); if(base_call_) v_->action_ = a; } void pop_base_old() { if(base_call_) { boost::contract::virtual_::action_enum a = v_->action_; v_->action_ = boost::contract::virtual_::pop_old_ftor_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. if(!base_call_) r = optional_get(r_); else if(v_->result_optional_) { try { r = **boost::any_cast*>( v_->result_ptr_); } catch(boost::bad_any_cast const&) { try { // Handle optional<...&>. r = **boost::any_cast*>( v_->result_ptr_); } catch(boost::bad_any_cast const&) { try { throw boost::contract::bad_virtual_result_cast(v_-> result_type_name_, typeid(r_type).name()); } catch(...) { this->fail(&boost::contract::postcondition_failure); } } } } else { try { r = *boost::any_cast(v_->result_ptr_); } catch(boost::bad_any_cast const&) { try { throw boost::contract::bad_virtual_result_cast( v_->result_type_name_, typeid(r_type).name()); } catch(...) { this->fail(&boost::contract::postcondition_failure); } } } check_virtual_post_with_result(r); } template typename boost::enable_if >::type check_virtual_post_with_result(Result const& r) { this->check_post(r); } template typename boost::disable_if >::type check_virtual_post_with_result(Result const& r) { BOOST_CONTRACT_DETAIL_DEBUG(r); 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) || \ !defined(BOOST_CONTRACT_NO_EXCEPTS) void exec_and( // Execute action in short-circuit logic-and with bases. boost::contract::virtual_::action_enum a, void (cond_subcontracting::* f)() = 0 ) { if(failed()) return; if(!base_call_ || v_->action_ == a) { if(!base_call_ && v_) { v_->action_ = a; boost::mpl::for_each(call_base(*this)); } if(f) (this->*f)(); if(base_call_) { throw cond_subcontracting_::signal_no_error(); } } } #endif #ifndef BOOST_CONTRACT_NO_PRECONDITIONS void exec_or( // Execute action in short-circuit logic-or with bases. boost::contract::virtual_::action_enum a, bool (cond_subcontracting::* f)(bool) = 0, void (*h)(boost::contract::from) = 0 ) { if(failed()) return; if(!base_call_ || v_->action_ == a) { if(!base_call_ && v_) { v_->action_ = a; try { exec_or_bases(); return; // A base checked with no error (done). } catch(...) { bool checked = f ? (this->*f)( /* throw_on_failure = */ false) : false; if(!checked) { try { throw; } // Report latest exception found. catch(...) { this->fail(h); } } return; // Checked and no exception (done). } } bool checked = f ? (this->*f)(/* throw_on_failure = */ base_call_) : false; if(base_call_) { if(!checked) { throw cond_subcontracting_::signal_not_checked(); } throw cond_subcontracting_::signal_no_error(); } } } template typename boost::enable_if, bool>::type exec_or_bases() { return false; } template typename boost::disable_if, bool>::type exec_or_bases() { if(boost::mpl::empty::value) return false; try { call_base(*this)(typename boost::mpl::front::type()); } catch(cond_subcontracting_::signal_not_checked const&) { return exec_or_bases< typename boost::mpl::pop_front::type>(); } catch(...) { bool checked = false; try { checked = exec_or_bases< typename boost::mpl::pop_front::type>(); } catch(...) { checked = false; } if(!checked) throw; } return true; } #endif #ifndef BOOST_CONTRACT_NO_CONDITIONS class call_base { // Copyable (as &). public: explicit call_base(cond_subcontracting& me) : me_(me) {} template void operator()(B*) { BOOST_CONTRACT_DETAIL_DEBUG(me_.object()); BOOST_CONTRACT_DETAIL_DEBUG(me_.v_); BOOST_CONTRACT_DETAIL_DEBUG(me_.v_->action_ != boost::contract::virtual_::no_action); try { call(BOOST_CONTRACT_DETAIL_TVARIADIC_TUPLE_INDEXES_OF( Args)); } catch(cond_subcontracting_::signal_no_error const&) { // No error (do not throw). } } private: template< class B // Can't use TVARIADIC_COMMA here. BOOST_PP_COMMA_IF(BOOST_CONTRACT_DETAIL_TVARIADIC) BOOST_CONTRACT_DETAIL_TVARIADIC_TUPLE_INDEXES_TPARAM(I) > void call( BOOST_CONTRACT_DETAIL_TVARIADIC_TUPLE_INDEXES_FPARAM(I)) { O::template BOOST_CONTRACT_DETAIL_NAME1(call_base)( me_.v_, me_.object() BOOST_CONTRACT_DETAIL_TVARIADIC_COMMA( BOOST_CONTRACT_MAX_ARGS) BOOST_CONTRACT_DETAIL_TVARIADIC_TUPLE_ELEMS_Z(1, BOOST_CONTRACT_MAX_ARGS, I, me_.args_) ); } cond_subcontracting& me_; }; #endif #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS VR& r_; #endif #ifndef BOOST_CONTRACT_NO_CONDITIONS boost::contract::virtual_* v_; bool base_call_; BOOST_CONTRACT_DETAIL_TVARIADIC_TUPLE_Z(1, BOOST_CONTRACT_MAX_ARGS, Args, &, args_) #endif }; } } } // namespace #endif // #include guard