diff --git a/include/boost/contract.hpp b/include/boost/contract.hpp index a8a09d8..073b1f1 100644 --- a/include/boost/contract.hpp +++ b/include/boost/contract.hpp @@ -16,8 +16,6 @@ #include <boost/contract/call_if.hpp> #include <boost/contract/config.hpp> -// TODO: base_types, invariant, and static_invariant must be public. Allow to make them private (so to not alter user's public API) by declaring some `friend class boost::contract::aux::access;` from the user's class (then this lib can access base_types, invariant, and static_invariant via that access class). Document that if invariant function is made private but access is not made friend, this library will not check invariants and will not error (is this true? test it...). - // TODO: Should C++11 move preserve class invariants at exit and/or on throw? Maybe not because after move no other public member can be called (but dtor can... so dtor should not check inv at that time...). If so, users could use an internal moved_ data member to guard class invariant checking and set that after the move operation... How can I program C++11 move operations with this lib? Should I used boost::contract::function instead of public_function? (But probably not because that does not subcontract and does not check inv at entry...) // TODO: What shall I do with unions? Can/shall I contract them? Double check which members C++11 unions can have (ctor, dtor, etc?). diff --git a/include/boost/contract/aux_/condition/check_pre_post_inv.hpp b/include/boost/contract/aux_/condition/check_pre_post_inv.hpp index 742a4db..c18096b 100644 --- a/include/boost/contract/aux_/condition/check_pre_post_inv.hpp +++ b/include/boost/contract/aux_/condition/check_pre_post_inv.hpp @@ -2,11 +2,10 @@ #ifndef BOOST_CONTRACT_AUX_CHECK_PRE_POST_INV_HPP_ #define BOOST_CONTRACT_AUX_CHECK_PRE_POST_INV_HPP_ -#include <boost/contract/core/config.hpp> +#include <boost/contract/core/access.hpp> #include <boost/contract/core/exception.hpp> +#include <boost/contract/core/config.hpp> #include <boost/contract/aux_/condition/check_pre_post.hpp> -#include <boost/contract/aux_/type_traits/invariant.hpp> -#include <boost/contract/aux_/debug.hpp> /** @cond */ #include <boost/mpl/bool.hpp> #include <boost/shared_ptr.hpp> @@ -35,11 +34,11 @@ protected: private: void check_inv(bool on_entry, bool static_inv_only) { try { - check_static_inv(on_entry, boost::mpl::bool_< - has_static_invariant<C>::value>()); + check_static_inv(on_entry, boost::mpl::bool_<boost::contract:: + access::has_static_invariant<C>::value>()); if(!static_inv_only) { - check_const_inv(on_entry, boost::mpl::bool_< - has_const_invariant<C>::value>()); + check_const_inv(on_entry, boost::mpl::bool_<boost::contract:: + access::has_const_invariant<C>::value>()); } } catch(...) { if(on_entry) boost::contract::entry_invariant_failed(from()); @@ -49,13 +48,12 @@ private: void check_static_inv(bool, boost::mpl::false_ const&) {} void check_static_inv(bool on_entry, boost::mpl::true_ const&) { - C::BOOST_CONTRACT_CONFIG_STATIC_INVARIANT(); + boost::contract::access::static_invariant<C>(); } void check_const_inv(bool, boost::mpl::false_ const&) {} void check_const_inv(bool on_entry, boost::mpl::true_ const&) { - BOOST_CONTRACT_AUX_DEBUG(obj_); - obj_->BOOST_CONTRACT_CONFIG_INVARIANT(); + boost::contract::access::const_invariant(obj_); } // TODO: Add support for volatile member functions and class invariants. diff --git a/include/boost/contract/aux_/condition/check_subcontracted_pre_post_inv.hpp b/include/boost/contract/aux_/condition/check_subcontracted_pre_post_inv.hpp index d6e03d4..d458cd2 100644 --- a/include/boost/contract/aux_/condition/check_subcontracted_pre_post_inv.hpp +++ b/include/boost/contract/aux_/condition/check_subcontracted_pre_post_inv.hpp @@ -2,11 +2,11 @@ #ifndef BOOST_CONTRACT_AUX_CHECK_SUBCONTRACTED_PRE_POST_INV_HPP_ #define BOOST_CONTRACT_AUX_CHECK_SUBCONTRACTED_PRE_POST_INV_HPP_ +#include <boost/contract/core/access.hpp> #include <boost/contract/core/virtual.hpp> #include <boost/contract/core/config.hpp> #include <boost/contract/core/exception.hpp> #include <boost/contract/aux_/condition/check_pre_post_inv.hpp> -#include <boost/contract/aux_/type_traits/base_types.hpp> #include <boost/contract/aux_/type_traits/member_function_types.hpp> #include <boost/contract/aux_/type_traits/optional.hpp> #include <boost/contract/aux_/debug.hpp> @@ -53,7 +53,7 @@ class check_subcontracted_pre_post_inv : // Copyable (as * and &). class overridden_bases_of { struct search_bases { typedef typename boost::mpl::fold< - typename base_types_of<Class>::type, + typename boost::contract::access::base_types_of<Class>::type, Result, // Fold: _1 = result, _2 = current base type from base_types. boost::mpl::eval_if<boost::mpl::contains<boost::mpl::_1, @@ -85,7 +85,8 @@ class check_subcontracted_pre_post_inv : // Copyable (as * and &). >::type type; }; public: - typedef typename boost::mpl::eval_if<has_base_types<Class>, + typedef typename boost::mpl::eval_if< + boost::contract::access::has_base_types<Class>, search_bases , boost::mpl::identity<Result> // Return result (stop recursion). diff --git a/include/boost/contract/aux_/operator_safe_bool.hpp b/include/boost/contract/aux_/operator_safe_bool.hpp new file mode 100644 index 0000000..eb8ecf8 --- /dev/null +++ b/include/boost/contract/aux_/operator_safe_bool.hpp @@ -0,0 +1,73 @@ + +#ifndef BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL_HPP_ +#define BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL_HPP_ + +#include <boost/config.hpp> +#include <boost/detail/workaround.hpp> + +// This code is inspired by "boost/shared_ptr/detail/operator_bool.hpp". + +/* PRIVATE */ + +// operator! is redundant, but some compilers need it. +#define BOOST_CONTRACT_OPERATOR_SAFE_BOOL_NOT_(bool_expr) \ + bool operator!() const BOOST_NOEXCEPT { return !(bool_expr); } + +/* PUBLIC */ + +#if !defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) && \ + !defined(BOOST_NO_CXX11_NULLPTR) + +#define BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL(this_type, bool_expr) \ + explicit operator bool() const BOOST_NOEXCEPT { return (bool_expr); } \ + BOOST_CONTRACT_OPERATOR_SAFE_BOOL_NOT_(bool_expr) + +#elif (defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, < 0x570) ) || \ + defined(__CINT__) + +#define BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL(this_type, bool_expr) \ + operator bool() const BOOST_NOEXCEPT { return (bool_expr); } \ + BOOST_CONTRACT_OPERATOR_SAFE_BOOL_NOT_(bool_expr) + +#elif defined(_MANAGED) + +#define BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL(this_type, bool_expr) \ + static void boost_contract_aux_operator_safe_bool_func(this_type***) {} \ + typedef void (*boost_contract_aux_operator_safe_bool_type)(this_type***); \ + operator boost_contract_aux_operator_safe_bool_type() \ + const BOOST_NOEXCEPT { \ + return (bool_expr) ? &boost_contract_aux_operator_safe_bool_func : 0; \ + } \ + BOOST_CONTRACT_OPERATOR_SAFE_BOOL_NOT_(bool_expr) + +#elif (defined(__MWERKS__) && BOOST_WORKAROUND(__MWERKS__, < 0x3200)) || \ + (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 304)) || \ + (defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, <= 0x590)) + +#define BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL(this_type, bool_expr) \ + void boost_contract_aux_operator_safe_bool_func() const {} \ + typedef void (this_type::*boost_contract_aux_operator_safe_bool_type)() \ + const; \ + operator boost_contract_aux_operator_safe_bool_type() \ + const BOOST_NOEXCEPT { \ + return (bool_expr) ? \ + &this_type::boost_contract_aux_operator_safe_bool_func : 0; \ + } \ + BOOST_CONTRACT_OPERATOR_SAFE_BOOL_NOT_(bool_expr) + +#else + +#define BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL(this_type, bool_expr) \ + void* boost_contract_aux_operator_safe_bool_data; \ + typedef void* this_type::*boost_contract_aux_operator_safe_bool_type; \ + operator boost_contract_aux_operator_safe_bool_type() \ + const BOOST_NOEXCEPT { \ + return (bool_expr) ? \ + &this_type::boost_contract_aux_operator_safe_bool_data : 0; \ + } \ + BOOST_CONTRACT_OPERATOR_SAFE_BOOL_NOT_(bool_expr) + +#endif + +#endif // #include guard + diff --git a/include/boost/contract/aux_/type_traits/base_types.hpp b/include/boost/contract/aux_/type_traits/base_types.hpp deleted file mode 100644 index ef61775..0000000 --- a/include/boost/contract/aux_/type_traits/base_types.hpp +++ /dev/null @@ -1,21 +0,0 @@ - -#ifndef BOOST_CONTRACT_AUX_BASE_TYPES_HPP_ -#define BOOST_CONTRACT_AUX_BASE_TYPES_HPP_ - -#include <boost/contract/core/config.hpp> -#include <boost/contract/aux_/type_traits/introspection.hpp> - -namespace boost { namespace contract { namespace aux { - -BOOST_CONTRACT_AUX_INTROSPECTION_HAS_TYPE(has_base_types, - BOOST_CONTRACT_CONFIG_BASE_TYPES) - -template<class C> -struct base_types_of { - typedef typename C::BOOST_CONTRACT_CONFIG_BASE_TYPES type; -}; - -} } } // namespace - -#endif // #include guard - diff --git a/include/boost/contract/aux_/type_traits/invariant.hpp b/include/boost/contract/aux_/type_traits/invariant.hpp deleted file mode 100644 index 37c63d9..0000000 --- a/include/boost/contract/aux_/type_traits/invariant.hpp +++ /dev/null @@ -1,52 +0,0 @@ - -#ifndef BOOST_CONTRACT_AUX_INVARIANT_HPP_ -#define BOOST_CONTRACT_AUX_INVARIANT_HPP_ - -#include <boost/contract/core/config.hpp> -#include <boost/contract/aux_/type_traits/introspection.hpp> -/** @cond */ -#include <boost/function_types/property_tags.hpp> -#include <boost/mpl/vector.hpp> -/** @endcond */ - -namespace boost { namespace contract { namespace aux { - -namespace invariant_ { - BOOST_CONTRACT_AUX_INTROSPECTION_HAS_MEMBER_FUNCTION( - has_invariant, BOOST_CONTRACT_CONFIG_INVARIANT) - - BOOST_CONTRACT_AUX_INTROSPECTION_HAS_MEMBER_FUNCTION( - has_non_static_invariant, BOOST_CONTRACT_CONFIG_STATIC_INVARIANT) - - BOOST_CONTRACT_AUX_INTROSPECTION_HAS_STATIC_MEMBER_FUNCTION( - has_static_invariant, BOOST_CONTRACT_CONFIG_STATIC_INVARIANT) -} - -// TODO: Unless PERMISSIVE, enforce: !has_invariant<C> || has_const_invariant<C> || has_const_volatile_invariant<C> - -template<typename T> -struct has_const_invariant : invariant_::has_invariant<T, void, - boost::mpl::vector<>, boost::function_types::const_non_volatile> {}; - -template<typename T> -struct has_const_volatile_invariant : invariant_::has_invariant<T, void, - boost::mpl::vector<>, boost::function_types::cv_qualified> {}; - -template<typename T> -struct has_invariant : invariant_::has_invariant<T, void, - boost::mpl::vector<> > {}; - -// TODO: Unless PERMISSIVE, enforce: !has_non_static_invariant<Class> - -template<typename T> -struct has_static_invariant : invariant_::has_static_invariant<T, void, - boost::mpl::vector<> > {}; - -template<typename T> -struct has_non_static_invariant : invariant_::has_non_static_invariant<T, void, - boost::mpl::vector<> > {}; - -} } } // namespace - -#endif // #include guard - diff --git a/include/boost/contract/core/access.hpp b/include/boost/contract/core/access.hpp new file mode 100644 index 0000000..7fa00f3 --- /dev/null +++ b/include/boost/contract/core/access.hpp @@ -0,0 +1,131 @@ + +#ifndef BOOST_CONTRACT_ACCESS_HPP_ +#define BOOST_CONTRACT_ACCESS_HPP_ + +#include <boost/contract/core/config.hpp> +// Include instead of fwd decl to avoid warnings on tparam default value redef. +#include <boost/contract/core/set_precondition_old_postcondition.hpp> +#include <boost/contract/aux_/type_traits/introspection.hpp> +#include <boost/contract/aux_/debug.hpp> +/** @cond */ +#include <boost/function_types/property_tags.hpp> +#include <boost/mpl/vector.hpp> +/** @endcond */ + +// TODO: Try to remove all friendship relations everywhere in the library and see if tests compile any faster by making internal API public instead. If that is the case, I could use AUX_SYMBOL instead of private... + +namespace boost { + namespace contract { + class virtual_; + + namespace aux { + template<class, typename, typename, class, typename, typename> + class check_subcontracted_pre_post_inv; + + template<typename, class> + class check_pre_post_inv; + } + } +} + +namespace boost { namespace contract { + +// NOTE: Not making this class friend will cause compiler errors on some +// compilers (e.g., MSVC) because the private members needed for contracts +// will not be accessible. On other compilers (e.g., GCC and CLang), the +// private access will instead simply fail SFINAE and no compiler error will be +// reported but invariants and subcontracting checking will be silently skipped +// at run-time. Therefore programmers must make sure to either declare contract +// members public or to make this class a friend. +class access { + BOOST_CONTRACT_AUX_INTROSPECTION_HAS_TYPE(has_base_types, + BOOST_CONTRACT_CONFIG_BASE_TYPES) + + template<class C> + struct base_types_of { + typedef typename C::BOOST_CONTRACT_CONFIG_BASE_TYPES type; + }; + + BOOST_CONTRACT_AUX_INTROSPECTION_HAS_MEMBER_FUNCTION( + has_invariant_func, BOOST_CONTRACT_CONFIG_INVARIANT) + + BOOST_CONTRACT_AUX_INTROSPECTION_HAS_MEMBER_FUNCTION( + has_non_static_invariant_func, + BOOST_CONTRACT_CONFIG_STATIC_INVARIANT + ) + + BOOST_CONTRACT_AUX_INTROSPECTION_HAS_STATIC_MEMBER_FUNCTION( + has_static_invariant_func, BOOST_CONTRACT_CONFIG_STATIC_INVARIANT) + + // TODO: Unless PERMISSIVE, enforce: !has_invariant<C> || has_const_invariant<C> || has_const_volatile_invariant<C> + + template<typename T> + struct has_const_invariant : has_invariant_func<T, void, + boost::mpl::vector<>, boost::function_types::const_non_volatile> {}; + + template<typename T> + struct has_const_volatile_invariant : has_invariant_func<T, void, + boost::mpl::vector<>, boost::function_types::cv_qualified> {}; + + template<typename T> + struct has_invariant : has_invariant_func<T, void, + boost::mpl::vector<> > {}; + + // TODO: Unless PERMISSIVE, enforce: !has_non_static_invariant<Class> + + template<typename T> + struct has_static_invariant : has_static_invariant_func<T, void, + boost::mpl::vector<> > {}; + + template<typename T> + struct has_non_static_invariant : has_non_static_invariant_func<T, void, + boost::mpl::vector<> > {}; + + template<typename C> + static void static_invariant() { + C::BOOST_CONTRACT_CONFIG_STATIC_INVARIANT(); + } + + template<typename C> + static void const_invariant(C const* obj) { + BOOST_CONTRACT_AUX_DEBUG(obj); + obj->BOOST_CONTRACT_CONFIG_INVARIANT(); + } + + // Friendship used to limit library's public API. + + template<class, typename, typename, class, typename, typename> + friend class boost::contract::aux::check_subcontracted_pre_post_inv; + + template<typename, class> + friend class boost::contract::aux::check_pre_post_inv; + + template<class O, typename F, class C> + friend set_precondition_old_postcondition<> public_function( + virtual_*, F, C*); + + template<class O, typename R, typename F, class C> + friend set_precondition_old_postcondition<R> public_function( + virtual_*, R&, F, C*); + + template<class O, typename F, class C, typename A0> + friend set_precondition_old_postcondition<> public_function( + virtual_*, F, C*, A0&); + + template<class O, typename R, typename F, class C, typename A0> + friend set_precondition_old_postcondition<R> public_function( + virtual_*, R&, F, C*, A0&); + + template<class O, typename F, class C, typename A0, typename A1> + friend set_precondition_old_postcondition<> public_function( + virtual_*, F, C*, A0&, A1&); + + template<class O, typename R, typename F, class C, typename A0, typename A1> + friend set_precondition_old_postcondition<R> public_function( + virtual_*, R&, F, C*, A0&, A1&); +}; + +} } // namespace + +#endif // #include guard + diff --git a/include/boost/contract/old.hpp b/include/boost/contract/old.hpp index 065489c..3cfcb0d 100644 --- a/include/boost/contract/old.hpp +++ b/include/boost/contract/old.hpp @@ -7,6 +7,7 @@ #include <boost/contract/core/config.hpp> #include <boost/contract/core/virtual.hpp> #include <boost/contract/aux_/check_guard.hpp> +#include <boost/contract/aux_/operator_safe_bool.hpp> #include <boost/contract/aux_/debug.hpp> /** @cond */ #include <boost/make_shared.hpp> @@ -30,8 +31,6 @@ BOOST_CONTRACT_ERROR_macro_OLDOF_requires_variadic_macros_otherwise_manually_pro #include <boost/preprocessor/cat.hpp> /** @endcond */ -// TODO: Test old value and postcondition when a type does not have all operations required to check postcondition (e.g., T without operator== in vector::push_back postcondition `back() == old_value`). - /* PUBLIC */ #define BOOST_CONTRACT_OLDOF(...) \ @@ -91,10 +90,10 @@ public: explicit old_ptr() {} T const& operator*() const { return ptr_.operator*(); } + T const* operator->() const { return ptr_.operator->(); } - // TODO: Use safe bool instead. - operator bool() { return !!ptr_; } + BOOST_CONTRACT_AUX_OPERATOR_SAFE_BOOL(old_ptr<T>, !!ptr_) private: explicit old_ptr(boost::shared_ptr<T const> ptr) : ptr_(ptr) {} diff --git a/include/boost/contract/public_function.hpp b/include/boost/contract/public_function.hpp index 566b077..b41376d 100644 --- a/include/boost/contract/public_function.hpp +++ b/include/boost/contract/public_function.hpp @@ -4,11 +4,11 @@ /** @file */ +#include <boost/contract/core/access.hpp> #include <boost/contract/core/set_precondition_old_postcondition.hpp> #include <boost/contract/core/virtual.hpp> #include <boost/contract/aux_/operation/public_function.hpp> #include <boost/contract/aux_/operation/public_static_function.hpp> -#include <boost/contract/aux_/type_traits/base_types.hpp> #include <boost/contract/aux_/none.hpp> /** @cond */ #include <boost/optional.hpp> @@ -56,7 +56,7 @@ // Always enforce this so this lib can check and enforce override. #define BOOST_CONTRACT_PUBLIC_FUNCTION_HAS_BASE_TYPES_(C) \ BOOST_STATIC_ASSERT_MSG( \ - boost::contract::aux::has_base_types<C>::value, \ + boost::contract::access::has_base_types<C>::value, \ "enclosing class missing 'base types' typedef" \ ); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ba094a3..7be5eed 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -2,6 +2,7 @@ test-suite constructor : [ subdir-run constructor : bases ] [ subdir-run constructor : body_throw ] + [ subdir-run constructor : access ] [ subdir-compile-fail constructor : no_pre_error ] ; @@ -24,6 +25,7 @@ test-suite public_function : [ subdir-run public_function : body_throw ] [ subdir-run public_function : static ] [ subdir-run public_function : static_body_throw ] + [ subdir-run public_function : access ] [ subdir-compile-fail public_function : override_error ] [ subdir-run public_function : override_permissive ] ; @@ -37,9 +39,11 @@ test-suite result : test-suite old : [ subdir-run old : no_macros ] [ subdir-run old : auto ] + [ subdir-run old : no_equal ] [ subdir-run old : noncopyable ] - [ subdir-compile-fail old : noncopyable_error ] [ subdir-compile-fail old : no_make_old_error ] + [ subdir-compile-fail old : noncopyable_error ] + [ subdir-compile-fail old : no_equal_error ] ; test-suite disable : diff --git a/test/constructor/access.cpp b/test/constructor/access.cpp new file mode 100644 index 0000000..9200b9e --- /dev/null +++ b/test/constructor/access.cpp @@ -0,0 +1,103 @@ + +// Test making all contract extra declarations (base types, inv, etc.) private. + +#include "../aux_/oteststream.hpp" +#include <boost/contract/constructor.hpp> +#include <boost/contract/base_types.hpp> +#include <boost/contract/guard.hpp> +#include <boost/detail/lightweight_test.hpp> +#include <sstream> + +boost::contract::aux::test::oteststream out; + +class b + #define BASES private boost::contract::constructor_precondition<b> + : BASES +{ + friend class boost::contract::access; + + 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; } + +public: + b() : boost::contract::constructor_precondition<b>([] { + out << "b::ctor::pre" << std::endl; + }) { + boost::contract::guard c = boost::contract::constructor(this) + .old([] { out << "b::ctor::old" << std::endl; }) + .postcondition([] { out << "b::ctor::post" << std::endl; }) + ; + out << "b::ctor::body" << std::endl; + } +}; + +class a + #define BASES private boost::contract::constructor_precondition<a>, public b + : BASES +{ + friend class boost::contract::access; + + // Private base types (always OK because never used by ctors). + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + // Private invariants. + static void static_invariant() { out << "a::static_inv" << std::endl; } + void invariant() const { out << "a::inv" << std::endl; } + +public: + a() : boost::contract::constructor_precondition<a>([] { + out << "a::ctor::pre" << std::endl; + }) { + boost::contract::guard c = boost::contract::constructor(this) + .old([] { out << "a::ctor::old" << std::endl; }) + .postcondition([] { out << "a::ctor::post" << std::endl; }) + ; + out << "a::ctor::body" << std::endl; + } +}; + +int main() { + std::ostringstream ok; + + out.str(""); + a aa; + ok.str(""); ok + << "a::ctor::pre" << std::endl + + << "b::ctor::pre" << std::endl + << "b::static_inv" << std::endl + << "b::ctor::old" << std::endl + << "b::ctor::body" << std::endl + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "b::ctor::post" << std::endl + + << "a::static_inv" << std::endl + << "a::ctor::old" << std::endl + << "a::ctor::body" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + << "a::ctor::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + b bb; + ok.str(""); ok + << "b::ctor::pre" << std::endl + << "b::static_inv" << std::endl + << "b::ctor::old" << std::endl + << "b::ctor::body" << std::endl + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "b::ctor::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} + diff --git a/test/old/no_equal.cpp b/test/old/no_equal.cpp new file mode 100644 index 0000000..c665461 --- /dev/null +++ b/test/old/no_equal.cpp @@ -0,0 +1,52 @@ + +// Test old value skipped when operations to check them missing (e.g., `==`). + +#include <boost/contract/function.hpp> +#include <boost/contract/guard.hpp> +#include <boost/contract/old.hpp> +#include <boost/contract/assert.hpp> +#include <boost/contract/call_if.hpp> +#include <boost/bind.hpp> +#include <boost/type_traits/has_equal_to.hpp> +#include <boost/detail/lightweight_test.hpp> +#include <functional> +#include <vector> + +unsigned equal_skips; + +template<typename T> +void push_back(std::vector<T>& vect, T const& val) { + boost::contract::guard c = boost::contract::function() + .postcondition([&] { + BOOST_CONTRACT_ASSERT( + boost::contract::call_if<boost::has_equal_to<T> >( + boost::bind(std::equal_to<T>(), boost::cref(vect.back()), + boost::cref(val)) + ).else_([] { ++equal_skips; return true; }) + ); + }) + ; + vect.push_back(val); +} + +struct j { // Type without operator==. + explicit j(int i) : j_(i) {} +private: + int j_; +}; + +int main() { + std::vector<int> vi; + equal_skips = 0; + push_back(vi, 123); + BOOST_TEST_EQ(equal_skips, 0); + + j jj(456); + std::vector<j> vj; + equal_skips = 0; + push_back(vj, jj); + BOOST_TEST_EQ(equal_skips, 1); + + return boost::report_errors(); +} + diff --git a/test/old/no_equal_error.cpp b/test/old/no_equal_error.cpp new file mode 100644 index 0000000..b69226c --- /dev/null +++ b/test/old/no_equal_error.cpp @@ -0,0 +1,36 @@ + +// Test old value error when operations to check them missing (e.g., `==`). + +#include <boost/contract/function.hpp> +#include <boost/contract/guard.hpp> +#include <boost/contract/old.hpp> +#include <boost/contract/assert.hpp> +#include <vector> + +template<typename T> +void push_back(std::vector<T>& vect, T const& val) { + boost::contract::guard c = boost::contract::function() + .postcondition([&] { + BOOST_CONTRACT_ASSERT(vect.back() == val); // Error (j has no ==). + }) + ; + vect.push_back(val); +} + +struct j { // Type without operator==. + explicit j(int i) : j_(i) {} +private: + int j_; +}; + +int main() { + std::vector<int> vi; + push_back(vi, 123); + + j jj(456); + std::vector<j> vj; + push_back(vj, jj); + + return 0; +} + diff --git a/test/public_function/access.cpp b/test/public_function/access.cpp new file mode 100644 index 0000000..1ed0de4 --- /dev/null +++ b/test/public_function/access.cpp @@ -0,0 +1,116 @@ + +// Test making all contract extra declarations (base types, inv, etc.) private. + +#include "../aux_/oteststream.hpp" +#include <boost/contract/base_types.hpp> +#include <boost/contract/override.hpp> +#include <boost/contract/public_function.hpp> +#include <boost/contract/guard.hpp> +#include <boost/contract/assert.hpp> +#include <boost/detail/lightweight_test.hpp> +#include <sstream> + +boost::contract::aux::test::oteststream out; + +class b { + friend class boost::contract::access; + + static void static_invariant() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + +public: + virtual void f(char ch, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + out << "b::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(ch == 'b'); + }) + .old([] { out << "b::f::old" << std::endl; }) + .postcondition([] { out << "b::f::post" << std::endl; }) + ; + out << "b::f::body" << std::endl; + } +}; + +class a + #define BASES public b + : BASES +{ + friend class boost::contract::access; + + // Private base types. + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + // Private invariants. + static void static_invariant() { out << "a::static_inv" << std::endl; } + void invariant() const { out << "a::inv" << std::endl; } + + // Private override (always possible even when access is not friend). + BOOST_CONTRACT_OVERRIDE(f) + +public: + virtual void f(char ch, boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function<override_f>( + v, &a::f, this, ch) + .precondition([&] { + out << "a::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(ch == 'a'); + }) + .old([] { out << "a::f::old" << std::endl; }) + .postcondition([] { out << "a::f::post" << std::endl; }) + ; + out << "a::f::body" << std::endl; + } +}; + +int main() { + std::ostringstream ok; + + a aa; + out.str(""); + aa.f('a'); + ok.str(""); ok + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + + << "b::f::pre" << std::endl + << "a::f::pre" << std::endl + + << "b::f::old" << std::endl + << "a::f::old" << std::endl + + << "a::f::body" << std::endl + + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << 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 + ; + BOOST_TEST(out.eq(ok.str())); + + b bb; + out.str(""); + bb.f('b'); + ok.str(""); ok + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "b::f::pre" << std::endl + << "b::f::old" << std::endl + << "b::f::body" << std::endl + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "b::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} +