diff --git a/TODO.txt b/TODO.txt index b732302..39eb5a5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,6 +1,12 @@ IMPLEMENTATION ----------------------------------------------------------------- +* Add a config macro to define the Boost.MPL sequence to use to hold bases. + By default this should be boost::mpl::vector (because more efficient), but + it could be #defined to boost::mpl::list by users in case vector's max size + limit is hit (mpl::vector has a max size, while mpl::list does not, but + mpl::list usually compiles slower than mpl::vector). + * Should C++11 move preserve class invariants at exit and/or on throw? * If not, how I can program C++11 move operations with the lib? (If I user boost::contract::function then I don't check inv at entry...) @@ -61,7 +67,7 @@ TESTING ------------------------------------------------------------------------ * Test what happens if bodies throw (e.g., public func should check inv, dtor too, but ctor not, etc.)... test all contract types when bodies throw. - * Test what happens if old expr thros (with and without .old(...)). + * Test what happens if old expr throws (with and without .old(...)). * Test subcontracting when: * Overridden missing pre and/or post and/or inv. diff --git a/include/boost/contract.hpp b/include/boost/contract.hpp index 1a22245..e545371 100644 --- a/include/boost/contract.hpp +++ b/include/boost/contract.hpp @@ -4,16 +4,17 @@ /** @file */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif // #include guard 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 0eeb19b..4a8591e 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,8 +2,8 @@ #ifndef BOOST_CONTRACT_AUX_CHECK_SUBCONTRACTED_PRE_POST_INV_HPP_ #define BOOST_CONTRACT_AUX_CHECK_SUBCONTRACTED_PRE_POST_INV_HPP_ -// TODO: Review and cleanup all #includes. #include +#include #include #include #include @@ -15,24 +15,17 @@ #include #include #include -#include -#include #include -#include -#include -#include -#include +#include #include -#include +#include #include -#include #include -#include #include -#include -#include +#include +#include /** @endcond */ - + namespace boost { namespace contract { namespace aux { namespace check_subcontracted_pre_post_inv_ { @@ -42,28 +35,66 @@ namespace check_subcontracted_pre_post_inv_ { // TODO: Can I make this, other check_... classes, and maybe even other // calsses noncopyable? What does truly need to be copyable and why? -// TODO: Here I must assert that if O != none then at least one of the bases -// contains a virtual contracted function that can be overridden. - // O, R, F, and A-i can be none types (but C cannot). template class check_subcontracted_pre_post_inv : // Copyable (as * and &). public check_pre_post_inv { - // Base types as pointers because mpl::for_each will construct them. - typedef typename boost::mpl::transform< - typename boost::mpl::eval_if, - boost::mpl::identity > + template > + class overridden_bases_of { + struct search_bases { + typedef typename boost::mpl::fold< + typename base_types_of::type, + Result, + // _1 = result, _2 = current base from base_types. + boost::mpl::eval_if >, + boost::mpl::_1 // Base already in result, do not add again. + , + boost::mpl::eval_if< + typename O::template BOOST_CONTRACT_AUX_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 later constructs them. + boost::add_pointer > + , + overridden_bases_of + > + > + >::type type; + }; + public: + typedef typename boost::mpl::eval_if, + search_bases , - base_types_of // Already asserted has_base_types if O != none. - >::type, - boost::add_pointer - >::type base_ptrs; + boost::mpl::identity // Return result (stop recursion). + >::type type; + }; + + typedef typename boost::mpl::eval_if, + boost::mpl::identity > + , + overridden_bases_of + >::type overridden_bases; - // Followings evaluate to none if F is none type. - typedef typename member_function_types::result_type result_type; - typedef typename member_function_types::virtual_argument_types - virt_arg_types; - typedef typename member_function_types::property_tag property_tag; +#ifndef BOOST_CONTRACT_CONFIG_PERMISSIVE + BOOST_STATIC_ASSERT_MSG(boost::mpl::or_, + boost::mpl::not_ > >::value, + "subcontracting function marked 'override' but does not override " + "any contracted member function" + ); +#endif public: explicit check_subcontracted_pre_post_inv(boost::contract::from from, @@ -75,7 +106,7 @@ public: v_ = v; // Invariant: v_ never null if base_call_. } else { base_call_ = false; - if(!boost::mpl::empty::value) { + if(!boost::mpl::empty::value) { v_ = new boost::contract::virtual_( boost::contract::virtual_::no_action); // C-style cast to handle both pointer type and const. @@ -106,9 +137,9 @@ protected: if(!base_call_ || v_->action_ == boost::contract::virtual_::check_pre) { try { - if(v_) { + if(!base_call_ && v_) { v_->action_ = boost::contract::virtual_::check_pre; - boost::mpl::for_each(check_base_); + boost::mpl::for_each(check_base_); } // Pre logic-or: Last check, error also throws. this->check_pre(/* throw_on_failure = */ base_call_); @@ -162,9 +193,9 @@ private: void execute(boost::contract::virtual_::action_enum a, void (check_subcontracted_pre_post_inv::* f)() = 0) { if(!base_call_ || v_->action_ == a) { - if(v_) { + if(!base_call_ && v_) { v_->action_ = a; - boost::mpl::for_each(check_base_); + boost::mpl::for_each(check_base_); } if(f) (this->*f)(); if(base_call_) throw check_subcontracted_pre_post_inv_::no_error(); @@ -179,19 +210,6 @@ private: template void operator()(B*) { - // TODO: If not in B, search B's inheritance graph deeply for f. - call(boost::mpl::bool_::value>()); - } - - private: - template - void call(boost::mpl::false_ const&) {} - - template - void call(boost::mpl::true_ const&) { BOOST_CONTRACT_AUX_DEBUG(nest_->object()); BOOST_CONTRACT_AUX_DEBUG(nest_->v_); BOOST_CONTRACT_AUX_DEBUG(nest_->v_->action_ != @@ -210,6 +228,7 @@ private: } } + private: check_subcontracted_pre_post_inv* nest_; }; diff --git a/include/boost/contract/core/config.hpp b/include/boost/contract/core/config.hpp index ece05e2..7212c9e 100644 --- a/include/boost/contract/core/config.hpp +++ b/include/boost/contract/core/config.hpp @@ -51,5 +51,12 @@ // the lib's cast on v' void* result_ will probably segfault?). // #ifdef PERMISSIVE +// TODO: Make sure this is used everywhere it makes sense instead of using +// mpl::vector directly in code impl. +#ifndef BOOST_CONTRACT_CONFIG_MPL_SEQ +# include +# define BOOST_CONTRACT_CONFIG_MPL_SEQ boost::mpl::vector +#endif + #endif // #include guard diff --git a/include/boost/contract/public_function.hpp b/include/boost/contract/public_function.hpp index 7c442e6..484809c 100644 --- a/include/boost/contract/public_function.hpp +++ b/include/boost/contract/public_function.hpp @@ -57,7 +57,7 @@ #define BOOST_CONTRACT_PUBLIC_FUNCTION_HAS_BASE_TYPES_(C) \ BOOST_STATIC_ASSERT_MSG( \ boost::contract::aux::has_base_types::value, \ - "enclosing class missing 'base types' typdef" \ + "enclosing class missing 'base types' typedef" \ ); /* CODE */ diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 9a89891..d1e4d88 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -2,22 +2,25 @@ test-suite constructor : [ subdir-run constructor : bases ] [ subdir-run constructor : body_throw ] - [ subdir-compile-fail constructor : no_pre-error ] + [ subdir-compile-fail constructor : no_pre_error ] ; test-suite destructor : [ subdir-run destructor : bases ] [ subdir-run destructor : body_throw ] - [ subdir-compile-fail destructor : no_pre-error ] + [ subdir-compile-fail destructor : no_pre_error ] ; test-suite public_function : [ subdir-run public_function : bases ] [ subdir-run public_function : bases_virtual ] [ subdir-run public_function : bases_branch ] + [ subdir-run public_function : bases_sparse ] [ subdir-run public_function : body_throw ] [ subdir-run public_function : static ] [ subdir-run public_function : static_body_throw ] + [ subdir-compile-fail public_function : override_error ] + [ subdir-run public_function : override_permissive ] ; test-suite function : @@ -28,7 +31,7 @@ test-suite function : test-suite old : [ subdir-run oldof : no_macros ] [ subdir-run oldof : auto ] - [ subdir-compile-fail oldof : no_make_old-error ] + [ subdir-compile-fail oldof : no_make_old_error ] ; test-suite disable : @@ -44,9 +47,9 @@ test-suite set : [ subdir-run set : both_old_post ] [ subdir-run set : both_pre_post ] [ subdir-run set : all_pre_old_post ] - [ subdir-compile-fail set : old_pre-error ] - [ subdir-compile-fail set : post_old-error ] - [ subdir-compile-fail set : post_pre-error ] + [ subdir-compile-fail set : old_pre_error ] + [ subdir-compile-fail set : post_old_error ] + [ subdir-compile-fail set : post_pre_error ] ; test-suite call_if : diff --git a/test/constructor/no_pre-error.cpp b/test/constructor/no_pre_error.cpp similarity index 100% rename from test/constructor/no_pre-error.cpp rename to test/constructor/no_pre_error.cpp diff --git a/test/destructor/no_pre-error.cpp b/test/destructor/no_pre_error.cpp similarity index 100% rename from test/destructor/no_pre-error.cpp rename to test/destructor/no_pre_error.cpp diff --git a/test/oldof/no_make_old-error.cpp b/test/oldof/no_make_old_error.cpp similarity index 100% rename from test/oldof/no_make_old-error.cpp rename to test/oldof/no_make_old_error.cpp diff --git a/test/public_function/bases_sparse.cpp b/test/public_function/bases_sparse.cpp new file mode 100644 index 0000000..5be6e2c --- /dev/null +++ b/test/public_function/bases_sparse.cpp @@ -0,0 +1,358 @@ + +// Test subcontracting with sparse and complex inheritance graph. + +#include "../aux_/oteststream.hpp" +#include +#include +#include +#include +#include +#include +#include + +boost::contract::aux::test::oteststream out; + +struct j { + void invariant() const { out << "j::inv" << std::endl; } + static void static_invariant() { out << "j::static_inv" << std::endl; } + + virtual void f(char ch, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + out << "j::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(ch == 'j'); + }) + .old([&] { out << "j::f::old" << std::endl; }) + .postcondition([&] { out << "j::f::post" << std::endl; }) + ; + out << "j::f::body" << std::endl; + } +}; + +struct i { + void invariant() const { out << "i::inv" << std::endl; } + static void static_invariant() { out << "i::static_inv" << std::endl; } + + virtual void f(char ch, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + out << "i::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(ch == 'i'); + }) + .old([&] { out << "i::f::old" << std::endl; }) + .postcondition([&] { out << "i::f::post" << std::endl; }) + ; + out << "i::f::body" << std::endl; + } +}; + +struct k {}; + +struct h + #define BASES public j + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + void invariant() const { out << "h::inv" << std::endl; } + static void static_invariant() { out << "h::static_inv" << std::endl; } + + virtual void f(char ch, boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function< + override_f>(v, &h::f, this, ch) + .precondition([&] { + out << "h::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(ch == 'h'); + }) + .old([&] { out << "h::f::old" << std::endl; }) + .postcondition([&] { out << "h::f::post" << std::endl; }) + ; + out << "h::f::body" << std::endl; + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +struct e + #define BASES public virtual i + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + void invariant() const { out << "e::inv" << std::endl; } + static void static_invariant() { out << "e::static_inv" << std::endl; } + + virtual void f(char ch, boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function< + override_f>(v, &e::f, this, ch) + .precondition([&] { + out << "e::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(ch == 'e'); + }) + .old([&] { out << "e::f::old" << std::endl; }) + .postcondition([&] { out << "e::f::post" << std::endl; }) + ; + out << "e::f::body" << std::endl; + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +struct d + #define BASES public k, virtual public i + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + void invariant() const { out << "d::inv" << std::endl; } + static void static_invariant() { out << "d::static_inv" << std::endl; } +}; + +struct c { + void invariant() const { out << "c::inv" << std::endl; } + static void static_invariant() { out << "c::static_inv" << std::endl; } + + virtual void f(char ch, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([&] { + out << "c::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(ch == 'c'); + }) + .old([&] { out << "c::f::old" << std::endl; }) + .postcondition([&] { out << "c::f::post" << std::endl; }) + ; + out << "c::f::body" << std::endl; + } +}; + +struct b + #define BASES public c, public d + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + void invariant() const { out << "b::inv" << std::endl; } + static void static_invariant() { out << "b::static_inv" << std::endl; } +}; + +struct x {}; +struct y {}; +struct z {}; + +struct a + #define BASES public b, public x, public e, protected y, public h, \ + private z + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + void invariant() const { out << "a::inv" << std::endl; } + static void static_invariant() { out << "a::static_inv" << std::endl; } + + 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; + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +int main() { + std::ostringstream ok; + + a aa; + out.str(""); + aa.f('a'); + ok.str(""); ok + << "c::static_inv" << std::endl + << "c::inv" << std::endl + << "i::static_inv" << std::endl + << "i::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + << "j::static_inv" << std::endl + << "j::inv" << std::endl + << "h::static_inv" << std::endl + << "h::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + + << "c::f::pre" << std::endl + << "i::f::pre" << std::endl + << "e::f::pre" << std::endl + << "j::f::pre" << std::endl + << "h::f::pre" << std::endl + << "a::f::pre" << std::endl + + << "c::f::old" << std::endl + << "i::f::old" << std::endl + << "e::f::old" << std::endl + << "j::f::old" << std::endl + << "h::f::old" << std::endl + << "a::f::old" << std::endl + + << "a::f::body" << std::endl + + << "c::static_inv" << std::endl + << "c::inv" << std::endl + << "i::static_inv" << std::endl + << "i::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + << "j::static_inv" << std::endl + << "j::inv" << std::endl + << "h::static_inv" << std::endl + << "h::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + + << "c::f::old" << std::endl + << "c::f::post" << std::endl + << "i::f::old" << std::endl + << "i::f::post" << std::endl + << "e::f::old" << std::endl + << "e::f::post" << std::endl + << "j::f::old" << std::endl + << "j::f::post" << std::endl + << "h::f::old" << std::endl + << "h::f::post" << std::endl + // No old call here because not a base object. + << "a::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + c cc; + out.str(""); + cc.f('c'); + ok.str(""); ok + << "c::static_inv" << std::endl + << "c::inv" << std::endl + << "c::f::pre" << std::endl + << "c::f::old" << std::endl + << "c::f::body" << std::endl + << "c::static_inv" << std::endl + << "c::inv" << std::endl + // No old call here because not a base object. + << "c::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + d dd; + out.str(""); + dd.f('i'); // d's f inherited from i. + ok.str(""); ok + << "i::static_inv" << std::endl + << "i::inv" << std::endl + << "i::f::pre" << std::endl + << "i::f::old" << std::endl + << "i::f::body" << std::endl + << "i::static_inv" << std::endl + << "i::inv" << std::endl + // No old call here because not a base object. + << "i::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + e ee; + out.str(""); + ee.f('e'); + ok.str(""); ok + << "i::static_inv" << std::endl + << "i::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + + << "i::f::pre" << std::endl + << "e::f::pre" << std::endl + + << "i::f::old" << std::endl + << "e::f::old" << std::endl + + << "e::f::body" << std::endl + + << "i::static_inv" << std::endl + << "i::inv" << std::endl + << "e::static_inv" << std::endl + << "e::inv" << std::endl + + << "i::f::old" << std::endl + << "i::f::post" << std::endl + // No old call here because not a base object. + << "e::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + i ii; + out.str(""); + ii.f('i'); + ok.str(""); ok + << "i::static_inv" << std::endl + << "i::inv" << std::endl + << "i::f::pre" << std::endl + << "i::f::old" << std::endl + << "i::f::body" << std::endl + << "i::static_inv" << std::endl + << "i::inv" << std::endl + // No old call here because not a base object. + << "i::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + h hh; + out.str(""); + hh.f('h'); + ok.str(""); ok + << "j::static_inv" << std::endl + << "j::inv" << std::endl + << "h::static_inv" << std::endl + << "h::inv" << std::endl + + << "j::f::pre" << std::endl + << "h::f::pre" << std::endl + + << "j::f::old" << std::endl + << "h::f::old" << std::endl + + << "h::f::body" << std::endl + + << "j::static_inv" << std::endl + << "j::inv" << std::endl + << "h::static_inv" << std::endl + << "h::inv" << std::endl + + << "j::f::old" << std::endl + << "j::f::post" << std::endl + // No old call here because not a base object. + << "h::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + j jj; + out.str(""); + jj.f('j'); + ok.str(""); ok + << "j::static_inv" << std::endl + << "j::inv" << std::endl + << "j::f::pre" << std::endl + << "j::f::old" << std::endl + << "j::f::body" << std::endl + << "j::static_inv" << std::endl + << "j::inv" << std::endl + // No old call here because not a base object. + << "j::f::post" << std::endl + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} + diff --git a/test/public_function/override.i b/test/public_function/override.i new file mode 100644 index 0000000..2831c32 --- /dev/null +++ b/test/public_function/override.i @@ -0,0 +1,34 @@ + +// Test a::f overrides, but base b does not have f (has g instead). + +#include +#include +#include +#include + +struct b { + virtual void g(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this); + } +}; + +struct a + #define BASES public b + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + virtual void f(boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function< + override_f>(v, &a::f, this); + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +int main() { + a aa; + aa.f(); + return 0; +} + diff --git a/test/public_function/override_error.cpp b/test/public_function/override_error.cpp new file mode 100644 index 0000000..b565b69 --- /dev/null +++ b/test/public_function/override_error.cpp @@ -0,0 +1,4 @@ + +// Test error when a::f marked override does not override any function. +#include "override.i" + diff --git a/test/public_function/override_permissive.cpp b/test/public_function/override_permissive.cpp new file mode 100644 index 0000000..afc3dda --- /dev/null +++ b/test/public_function/override_permissive.cpp @@ -0,0 +1,5 @@ + +// Test error when a::f marked override does not override any function. +#define BOOST_CONTRACT_CONFIG_PERMISSIVE +#include "override.i" + diff --git a/test/set/old_pre-error.cpp b/test/set/old_pre_error.cpp similarity index 100% rename from test/set/old_pre-error.cpp rename to test/set/old_pre_error.cpp diff --git a/test/set/post_old-error.cpp b/test/set/post_old_error.cpp similarity index 100% rename from test/set/post_old-error.cpp rename to test/set/post_old_error.cpp diff --git a/test/set/post_pre-error.cpp b/test/set/post_pre_error.cpp similarity index 100% rename from test/set/post_pre-error.cpp rename to test/set/post_pre_error.cpp