diff options
Diffstat (limited to 'boost/contract/detail/condition/cond_inv.hpp')
-rw-r--r-- | boost/contract/detail/condition/cond_inv.hpp | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/boost/contract/detail/condition/cond_inv.hpp b/boost/contract/detail/condition/cond_inv.hpp new file mode 100644 index 0000000000..b4c6c03133 --- /dev/null +++ b/boost/contract/detail/condition/cond_inv.hpp @@ -0,0 +1,232 @@ + +#ifndef BOOST_CONTRACT_DETAIL_COND_INV_HPP_ +#define BOOST_CONTRACT_DETAIL_COND_INV_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 <boost/contract/core/exception.hpp> +#include <boost/contract/core/config.hpp> +#include <boost/contract/detail/condition/cond_post.hpp> +#ifndef BOOST_CONTRACT_NO_INVARIANTS + #include <boost/contract/core/access.hpp> + #include <boost/type_traits/add_pointer.hpp> + #include <boost/type_traits/is_volatile.hpp> + #include <boost/mpl/vector.hpp> + #include <boost/mpl/transform.hpp> + #include <boost/mpl/for_each.hpp> + #include <boost/mpl/copy_if.hpp> + #include <boost/mpl/eval_if.hpp> + #include <boost/mpl/not.hpp> + #include <boost/mpl/and.hpp> + #include <boost/mpl/placeholders.hpp> + #include <boost/utility/enable_if.hpp> + #ifndef BOOST_CONTRACT_PERMISSIVE + #include <boost/function_types/property_tags.hpp> + #include <boost/static_assert.hpp> + #endif +#endif + +namespace boost { namespace contract { namespace detail { + +template<typename VR, class C> +class cond_inv : public cond_post<VR> { // Non-copyable base. + #if !defined(BOOST_CONTRACT_NO_INVARIANTS) && \ + !defined(BOOST_CONTRACT_PERMISSIVE) + BOOST_STATIC_ASSERT_MSG( + (!boost::contract::access::has_static_invariant_f< + C, void, boost::mpl:: vector<> + >::value), + "static invariant member function cannot be mutable " + "(it must be static instead)" + ); + BOOST_STATIC_ASSERT_MSG( + (!boost::contract::access::has_static_invariant_f< + C, void, boost::mpl::vector<>, + boost::function_types::const_non_volatile + >::value), + "static invariant member function cannot be const qualified " + "(it must be static instead)" + ); + BOOST_STATIC_ASSERT_MSG( + (!boost::contract::access::has_static_invariant_f< + C, void, boost::mpl::vector<>, + boost::function_types::volatile_non_const + >::value), + "static invariant member function cannot be volatile qualified " + "(it must be static instead)" + ); + BOOST_STATIC_ASSERT_MSG( + (!boost::contract::access::has_static_invariant_f< + C, void, boost::mpl::vector<>, + boost::function_types::cv_qualified + >::value), + "static invariant member function cannot be const volatile " + "qualified (it must be static instead)" + ); + + BOOST_STATIC_ASSERT_MSG( + (!boost::contract::access::has_invariant_s< + C, void, boost::mpl::vector<> + >::value), + "non-static invariant member function cannot be static " + "(it must be either const or const volatile qualified instead)" + ); + BOOST_STATIC_ASSERT_MSG( + (!boost::contract::access::has_invariant_f< + C, void, boost::mpl::vector<>, + boost::function_types::non_cv + >::value), + "non-static invariant member function cannot be mutable " + "(it must be either const or const volatile qualified instead)" + ); + BOOST_STATIC_ASSERT_MSG( + (!boost::contract::access::has_invariant_f< + C, void, boost::mpl::vector<>, + boost::function_types::volatile_non_const + >::value), + "non-static invariant member function cannot be volatile qualified " + "(it must be const or const volatile qualified instead)" + ); + #endif + +public: + // obj can be 0 for static member functions. + explicit cond_inv(boost::contract::from from, C* obj) : + cond_post<VR>(from) + #ifndef BOOST_CONTRACT_NO_CONDITIONS + , obj_(obj) + #endif + {} + +protected: + #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS + void check_entry_inv() { check_inv(true, false, false); } + void check_entry_static_inv() { check_inv(true, true, false); } + void check_entry_all_inv() { check_inv(true, false, true); } + #endif + + #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS + void check_exit_inv() { check_inv(false, false, false); } + void check_exit_static_inv() { check_inv(false, true, false); } + void check_exit_all_inv() { check_inv(false, false, true); } + #endif + + #ifndef BOOST_CONTRACT_NO_CONDITIONS + C* object() { return obj_; } + #endif + +private: + #ifndef BOOST_CONTRACT_NO_INVARIANTS + // 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<C>(); + if(!static_only) { + if(const_and_cv) { + check_cv_inv<C>(); + check_const_inv<C>(); + } else if(boost::is_volatile<C>::value) { + check_cv_inv<C>(); + } else { + check_const_inv<C>(); + } + } + } catch(...) { + if(on_entry) { + this->fail(&boost::contract::entry_invariant_failure); + } else this->fail(&boost::contract::exit_invariant_failure); + } + } + + template<class C_> + typename boost::disable_if< + boost::contract::access::has_const_invariant<C_> >::type + check_const_inv() {} + + template<class C_> + typename boost::enable_if< + boost::contract::access::has_const_invariant<C_> >::type + check_const_inv() { boost::contract::access::const_invariant(obj_); } + + template<class C_> + typename boost::disable_if< + boost::contract::access::has_cv_invariant<C_> >::type + check_cv_inv() {} + + template<class C_> + typename boost::enable_if< + boost::contract::access::has_cv_invariant<C_> >::type + check_cv_inv() { boost::contract::access::cv_invariant(obj_); } + + template<class C_> + typename boost::disable_if< + boost::contract::access::has_static_invariant<C_> >::type + check_static_inv() {} + + template<class C_> + typename boost::enable_if< + boost::contract::access::has_static_invariant<C_> >::type + check_static_inv() { + // SFINAE HAS_STATIC_... returns true even when member is inherited + // so extra run-time check here (not the same for non static). + if(!inherited<boost::contract::access::has_static_invariant, + boost::contract::access::static_invariant_addr>::apply()) { + boost::contract::access::static_invariant<C_>(); + } + } + + // Check if class's func is inherited from its base types or not. + template<template<class> class HasFunc, template<class> class FuncAddr> + struct inherited { + static bool apply() { + try { + boost::mpl::for_each< + // For now, no reason to deeply search inheritance tree + // (SFINAE HAS_STATIC_... already fails in that case). + typename boost::mpl::transform< + typename boost::mpl::copy_if< + typename boost::mpl::eval_if<boost::contract:: + access::has_base_types<C>, + typename boost::contract::access:: + base_types_of<C> + , + boost::mpl::vector<> + >::type, + HasFunc<boost::mpl::_1> + >::type, + boost::add_pointer<boost::mpl::_1> + >::type + >(compare_func_addr()); + } catch(signal_equal const&) { return true; } + return false; + } + + private: + class signal_equal {}; // Except. to stop for_each as soon as found. + + struct compare_func_addr { + template<typename B> + void operator()(B*) { + // Inherited func has same addr as in its base. + if(FuncAddr<C>::apply() == FuncAddr<B>::apply()) { + throw signal_equal(); + } + } + }; + }; + #endif + + #ifndef BOOST_CONTRACT_NO_CONDITIONS + C* obj_; + #endif +}; + +} } } // namespace + +#endif // #include guard + |