diff --git a/include/boost/contract.hpp b/include/boost/contract.hpp index e015110..96d7b67 100644 --- a/include/boost/contract.hpp +++ b/include/boost/contract.hpp @@ -24,20 +24,8 @@ // TODO: Document that there is a MSVC 2010 bug for which lambdas cannot be used in template constructor intialization list (this was fixed in MSVC 2013). Therefore, an external (static member) function must be used (possibly with bind and cref) to program constructor preconditions on MSVC 2010 instead of using lambdas. -// TODO: Test contract assertion disabled while checking assertions adding a test to disable/ similar to n1962/factorial.cpp, where contract recursively calls the function itself in the assertions. - -// TODO: Test overloading for all contract types. Also test subcontracting of overloaded virtual functions (overload both overridden and overriding functions). - -// TODO: Test what this lib does when derived public function overrides base's protected virtual function (substitution principle does not apply here because base's protected virtual functions cannot be called by users so it should not be subcontracted...). - -// TODO: Test with function and array argument types like `void f(int x[2][3], int (*g)(int))`. This should work... but it might require to specialize some of this lib's internal templates... - -// TODO: Test there is never ambiguity in resolving calls to public_function overloads (e.g., between public_function(v, some_func_ptr, this) and public_function(v, &a::f, this)...). - // TODO: Document that users could program (scoped) locks at top of function definitions and before the contract code so to deal with contracts in multi-threaded situations. Would these locks need to be recursive for virtual calls? Test that... -// TODO: Look at Andrzej's discussion on preconditions and either support that in this lib or at least mention/compare it in the docs: https://akrzemi1.wordpress.com/2013/01/04/preconditions-part-i/ - // TODO: Document pre > old > post are all optional but when they are specified they must be specified in this order (rationale: this is the logical order in which to specify them, other contract programming framework allow to mix this order, that could have been implemented in this lib too but it would complicated a bit the implementation to then allow for mixed orders that will be less clear logically). #endif // #include guard diff --git a/include/boost/contract/aux_/check_guard.hpp b/include/boost/contract/aux_/check_guard.hpp index aee87bd..f02a4b0 100644 --- a/include/boost/contract/aux_/check_guard.hpp +++ b/include/boost/contract/aux_/check_guard.hpp @@ -23,7 +23,7 @@ private: static bool checking_; }; -// TODO: This and all other lib states must go into a .cpp with dyn linking (DLL). Also move as much as code as possible to .cpp files (and try to minimize template is not strictly necessary) so to speed up compilation. +// TODO: This and all other lib states (failure handler functors from exception.hpp, etc.) must go into a .cpp with dyn linking (DLL). Also move as much as code as possible to .cpp files (and try to minimize template is not strictly necessary) so to speed up compilation. bool check_guard::checking_ = false; } } } // namespace diff --git a/include/boost/contract/aux_/operation/function.hpp b/include/boost/contract/aux_/operation/function.hpp index 9b530be..25f6411 100644 --- a/include/boost/contract/aux_/operation/function.hpp +++ b/include/boost/contract/aux_/operation/function.hpp @@ -22,10 +22,11 @@ private: #if BOOST_CONTRACT_PRECONDITIONS || BOOST_CONTRACT_POSTCONDITIONS void init() /* override */ { if(check_guard::checking()) return; - #if BOOST_CONTRACT_PRECONDITIONS { - check_guard checking; + #ifndef BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING + check_guard checking; + #endif this->check_pre(); } #endif diff --git a/include/boost/contract/aux_/operation/public_function.hpp b/include/boost/contract/aux_/operation/public_function.hpp index 1e7cc09..60a6dba 100644 --- a/include/boost/contract/aux_/operation/public_function.hpp +++ b/include/boost/contract/aux_/operation/public_function.hpp @@ -59,15 +59,23 @@ private: BOOST_CONTRACT_POSTCONDITIONS || \ BOOST_CONTRACT_INVARIANTS if(check_guard::checking()) return; - { + { // Acquire check guard. check_guard checking; #if BOOST_CONTRACT_ENTRY_INVARIANTS this->check_subcontracted_entry_inv(); #endif #if BOOST_CONTRACT_PRECONDITIONS - this->check_subcontracted_pre(); + #ifndef \ + BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING + this->check_subcontracted_pre(); + } // Release check guard. + #else + } // Release check guard. + this->check_subcontracted_pre(); + #endif + #else + } // Release check guard. #endif - } #if BOOST_CONTRACT_POSTCONDITIONS this->copy_subcontracted_old(); #endif diff --git a/include/boost/contract/aux_/operation/public_static_function.hpp b/include/boost/contract/aux_/operation/public_static_function.hpp index 16e0c79..9c6ea7f 100644 --- a/include/boost/contract/aux_/operation/public_static_function.hpp +++ b/include/boost/contract/aux_/operation/public_static_function.hpp @@ -29,15 +29,23 @@ private: void init() /* override */ { if(check_guard::checking()) return; #if BOOST_CONTRACT_ENTRY_INVARIANTS || BOOST_CONTRACT_PRECONDITIONS - { + { // Acquire check guard. check_guard checking; #if BOOST_CONTRACT_ENTRY_INVARIANTS this->check_entry_static_inv(); #endif #if BOOST_CONTRACT_PRECONDITIONS - this->check_pre(); + #ifndef \ + BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING + this->check_pre(); + } // Release check guard. + #else + } // Release check guard. + this->check_pre(); + #endif + #else + } // Release check guard #endif - } #endif #if BOOST_CONTRACT_POSTCONDITIONS this->copy_old(); diff --git a/include/boost/contract/constructor.hpp b/include/boost/contract/constructor.hpp index 4268e8d..3d47b3b 100644 --- a/include/boost/contract/constructor.hpp +++ b/include/boost/contract/constructor.hpp @@ -10,12 +10,13 @@ #endif #include #include +#include namespace boost { namespace contract { template set_old_postcondition<> constructor(C* obj) { - // Must also check ..._PRECONDITIONS here because set_... is generic. + // Must #if also on ..._PRECONDITIONS here because set_... is generic. #if BOOST_CONTRACT_CONSTRUCTORS || BOOST_CONTRACT_PRECONDITIONS return set_old_postcondition<>( new boost::contract::aux::constructor(obj)); @@ -31,9 +32,14 @@ public: template explicit constructor_precondition(F const& f) { + if(check_guard::checking()) return; #if BOOST_CONTRACT_PRECONDITIONS - try { f(); } - catch(...) { precondition_failure(from_constructor); } + try { + #ifndef BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING + check_guard checking; + #endif + f(); + } catch(...) { precondition_failure(from_constructor); } #endif } diff --git a/include/boost/contract/core/config.hpp b/include/boost/contract/core/config.hpp index 4ec440c..8d79872 100644 --- a/include/boost/contract/core/config.hpp +++ b/include/boost/contract/core/config.hpp @@ -10,8 +10,6 @@ // TODO: Can I compile lib1 with some contract on/off, lib2 with other contracts on/off and then link the two together? -// TODO: Add a config macro PRECONDITIONS_DISABLE_NOTHING to avoid disabling contract assertions within assertions checking for preconditions (to avoid passing unchecked arguments to function body...). This is what N1962 does... but this macro should be #undef by default. - #ifndef BOOST_CONTRACT_CONFIG_MAX_ARGS # define BOOST_CONTRACT_CONFIG_MAX_ARGS 10 #endif @@ -49,6 +47,15 @@ // #define BOOST_CONTRACT_CONFIG_ON_MISSING_GUARD { throw my_logic_error(); } // BOOST_CONTRACT_CONFIG_ON_MISSING_GUARD +// Contract checking is not disable while checking preconditions. +// This is what N1962 does by default. N1962 authors indicated it can be shown +// that unchecked arguments are passed to function bodies if contract checking +// is disable within precondition checking). +// However, not disabling contract checking while checking preconditions can +// lead to infinite recursive call in user code so by default this macro is +// not defined. +// BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING + // BOOST_CONTRACT_CONFIG_NO_PRECONDITIONS // BOOST_CONTRACT_CONFIG_NO_POSTCONDITIONS // BOOST_CONTRACT_CONFIG_NO_ENTRY_INVARIANTS diff --git a/include/boost/contract/core/exception.hpp b/include/boost/contract/core/exception.hpp index 279b693..6aa3025 100644 --- a/include/boost/contract/core/exception.hpp +++ b/include/boost/contract/core/exception.hpp @@ -144,8 +144,6 @@ namespace exception_ { std::terminate(); // Default handlers log and call terminate. } - // TODO: These (and some of the related def code) should be moved in a .cpp. - //boost::mutex pre_failure_mutex; assertion_failure_handler pre_failure_handler = &default_handler; diff --git a/include/boost/contract/function.hpp b/include/boost/contract/function.hpp index b84514a..fbb1f37 100644 --- a/include/boost/contract/function.hpp +++ b/include/boost/contract/function.hpp @@ -10,8 +10,8 @@ namespace boost { namespace contract { set_precondition_old_postcondition<> function() { - // Must check ..._INVARIANTS here because set_... is generic. - #if BOOST_CONTRACT_FUNCTIONS + // Must #if also on ..._INVARIANTS here because set_... is generic. + #if BOOST_CONTRACT_FUNCTIONS || BOOST_CONTRACT_INVARIANTS return set_precondition_old_postcondition<>( new boost::contract::aux::function()); #else diff --git a/include/boost/contract/public_function.hpp b/include/boost/contract/public_function.hpp index 7c00d44..5b8577d 100644 --- a/include/boost/contract/public_function.hpp +++ b/include/boost/contract/public_function.hpp @@ -2,7 +2,9 @@ #ifndef BOOST_CONTRACT_PUBLIC_FUNCTION_HPP_ #define BOOST_CONTRACT_PUBLIC_FUNCTION_HPP_ -// TODO: Check all #includes for all files... and make sure that #include not of this library are within @cond ... @endcond. Also disable #include when not needed based on BOOST_CONTRACT_PRECONDITIONS, etc. +// TODO: See if by reimplementing a few Boost.MPL constructs (vector, for_each, etc.) using variadic template, I can support truly any number of function arguments on C++11. + +// TODO: Check all #includes for all files... and make sure that #include not of this library are within @cond ... @endcond. Also disable #include when not needed based on BOOST_CONTRACT_PRECONDITIONS, etc. Also all public header files should include *all* core/*.hpp so users never have to (I could use a aux_/all_core_headers.hpp that includes all core/*.hpp and always #include aux_/core.hpp in all public headers instead of including core/... directly from public headers). #include #include diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 008a764..ff5a730 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -112,6 +112,8 @@ test-suite public_function [ subdir-run-withno public_function : bases_virtual ] [ subdir-run-withno public_function : bases_branch ] [ subdir-run-withno public_function : bases_sparse ] + [ subdir-run-withno public_function : bases_protected ] + [ subdir-compile-fail public_function : bases_protected_error ] [ subdir-run-withno public_function : access ] [ subdir-run-withno public_function : no_contracts ] @@ -127,6 +129,9 @@ test-suite public_function [ subdir-run-withno public_function : max_args2_no_tva ] [ subdir-run-withno public_function : max_args ] [ subdir-run-withno public_function : max_args_no_tva ] + + [ subdir-run-withno public_function : overload ] + [ subdir-run-withno public_function : overload_no_tva ] [ subdir-compile-fail public_function : override_error ] [ subdir-run-withno public_function : override_permissive ] @@ -205,6 +210,7 @@ test-suite old test-suite disable : [ subdir-run-withno disable : checking ] + [ subdir-run-withno disable : checking_pre_disable_nothing ] ; test-suite set diff --git a/test/disable/checking.cpp b/test/disable/checking.cpp index 5d62184..3204259 100644 --- a/test/disable/checking.cpp +++ b/test/disable/checking.cpp @@ -1,132 +1,6 @@ -// Test contract checking and old value copies disabled within contracts. +// Test other contract checking disabled within contract checking. -#include "../aux_/oteststream.hpp" -#include "../aux_/counter.hpp" -#include -#include -#include -#include -#include -#include - -boost::contract::aux::test::oteststream out; - -struct a { - static void static_invariant() { out << "a::static_inv" << std::endl; } - void invariant() const { out << "a::inv" << std::endl; } - - struct x_tag; - typedef boost::contract::aux::test::counter x_type; - - int f(x_type& x) { - int result; - boost::contract::old_ptr old_x = BOOST_CONTRACT_OLDOF( - x_type::eval(x)); - boost::contract::guard c = boost::contract::public_function(this) - .precondition([&] { out << "a::f::pre" << std::endl; }) - .old([&] { out << "a::f::old" << std::endl; }) - .postcondition([&] { - out << "a::f::post" << std::endl; - BOOST_CONTRACT_ASSERT(x.value == -old_x->value); - BOOST_CONTRACT_ASSERT(result == old_x->value); - }) - ; - out << "a::f::body" << std::endl; - result = x.value; - x.value = -x.value; - return result; - } -}; - -bool call_f() { - a aa; - a::x_type x; x.value = -123; - return aa.f(x) == -123; -} - -struct b { - static void static_invariant() { out << "b::static_inv" << std::endl; } - void invariant() const { out << "b::inv" << std::endl; } - - void g() { - boost::contract::guard c = boost::contract::public_function(this) - .precondition([&] { - out << "b::g::pre" << std::endl; - BOOST_CONTRACT_ASSERT(call_f()); - }) - .old([&] { out << "b::g::old" << std::endl; }) - .postcondition([&] { - out << "b::g::post" << std::endl; - BOOST_CONTRACT_ASSERT(call_f()); - }) - ; - out << "b::g::body" << std::endl; - } -}; - -int main() { - std::ostringstream ok; - b bb; - - out.str(""); - bb.g(); - ok.str(""); ok - #if BOOST_CONTRACT_ENTRY_INVARIANTS - << "b::static_inv" << std::endl - << "b::inv" << std::endl - #endif - #if BOOST_CONTRACT_PRECONDITIONS - << "b::g::pre" << std::endl - // Test call while checking executes body (but no contracts). - << "a::f::body" << std::endl - #endif - #if BOOST_CONTRACT_POSTCONDITIONS - << "b::g::old" << std::endl - #endif - << "b::g::body" << std::endl - #if BOOST_CONTRACT_EXIT_INVARIANTS - << "b::static_inv" << std::endl - << "b::inv" << std::endl - #endif - #if BOOST_CONTRACT_POSTCONDITIONS - << "b::g::post" << std::endl - // Test call while checking executes body (but no contracts). - << "a::f::body" << std::endl - #endif - ; - BOOST_TEST(out.eq(ok.str())); - - // Test old values not copied for disabled contracts. - BOOST_TEST_EQ(a::x_type::copies(), 0); BOOST_TEST_EQ(a::x_type::evals(), 0); - - out << std::endl; - - out.str(""); - call_f(); - // Double check a call to f outside another contract checks f's contracts. - ok.str(""); ok - #if BOOST_CONTRACT_ENTRY_INVARIANTS - << "a::static_inv" << std::endl - << "a::inv" << std::endl - #endif - #if BOOST_CONTRACT_PRECONDITIONS - << "a::f::pre" << std::endl - #endif - #if BOOST_CONTRACT_POSTCONDITIONS - << "a::f::old" << std::endl - #endif - << "a::f::body" << std::endl - #if BOOST_CONTRACT_EXIT_INVARIANTS - << "a::static_inv" << std::endl - << "a::inv" << std::endl - #endif - #if BOOST_CONTRACT_POSTCONDITIONS - << "a::f::post" << std::endl - #endif - ; - BOOST_TEST(out.eq(ok.str())); - - return boost::report_errors(); -} +#undef BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING +#include "checking.hpp" diff --git a/test/disable/checking.hpp b/test/disable/checking.hpp new file mode 100644 index 0000000..8ffca7a --- /dev/null +++ b/test/disable/checking.hpp @@ -0,0 +1,161 @@ + +// Test contract checking and old value copies disabled within contracts. + +#include "../aux_/oteststream.hpp" +#include "../aux_/counter.hpp" +#include +#include +#include +#include +#include +#include + +boost::contract::aux::test::oteststream out; + +struct a { + static void static_invariant() { out << "a::static_inv" << std::endl; } + void invariant() const { out << "a::inv" << std::endl; } + + struct x_tag; + typedef boost::contract::aux::test::counter x_type; + + int f(x_type& x) { + int result; + boost::contract::old_ptr old_x = BOOST_CONTRACT_OLDOF( + x_type::eval(x)); + boost::contract::guard c = boost::contract::public_function(this) + .precondition([&] { out << "a::f::pre" << std::endl; }) + .old([&] { out << "a::f::old" << std::endl; }) + .postcondition([&] { + out << "a::f::post" << std::endl; + BOOST_CONTRACT_ASSERT(x.value == -old_x->value); + BOOST_CONTRACT_ASSERT(result == old_x->value); + }) + ; + out << "a::f::body" << std::endl; + result = x.value; + x.value = -x.value; + return result; + } +}; + +bool call_f() { + a aa; + a::x_type x; x.value = -123; + return aa.f(x) == -123; +} + +struct b { + static void static_invariant() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + + void g() { + boost::contract::guard c = boost::contract::public_function(this) + .precondition([&] { + out << "b::g::pre" << std::endl; + BOOST_CONTRACT_ASSERT(call_f()); + }) + .old([&] { out << "b::g::old" << std::endl; }) + .postcondition([&] { + out << "b::g::post" << std::endl; + BOOST_CONTRACT_ASSERT(call_f()); + }) + ; + out << "b::g::body" << std::endl; + } +}; + +int main() { + std::ostringstream ok; + b bb; + + out.str(""); + bb.g(); + ok.str(""); ok + #if BOOST_CONTRACT_ENTRY_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + #endif + #if BOOST_CONTRACT_PRECONDITIONS + << "b::g::pre" << std::endl + #ifndef BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING + // Test call while checking executes body (but no contracts). + << "a::f::body" << std::endl + #else + // Test preconditions have disabled no contract. + #if BOOST_CONTRACT_ENTRY_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + << "a::f::pre" << std::endl + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f::old" << std::endl + #endif + << "a::f::body" << std::endl + #if BOOST_CONTRACT_EXIT_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f::post" << std::endl + #endif + #endif + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "b::g::old" << std::endl + #endif + << "b::g::body" << std::endl + #if BOOST_CONTRACT_EXIT_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "b::g::post" << std::endl + // Test call while checking executes body (but no contracts). + << "a::f::body" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + // Test old values not copied for disabled contracts. + unsigned const cnt = + #if defined(BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING) && \ + BOOST_CONTRACT_PRECONDITIONS && BOOST_CONTRACT_POSTCONDITIONS + 1 + #else + 0 + #endif + ; + BOOST_TEST_EQ(a::x_type::copies(), cnt); + BOOST_TEST_EQ(a::x_type::evals(), cnt); + + out << std::endl; + + out.str(""); + call_f(); + // Double check a call to f outside another contract checks f's contracts. + ok.str(""); ok + #if BOOST_CONTRACT_ENTRY_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_PRECONDITIONS + << "a::f::pre" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f::old" << std::endl + #endif + << "a::f::body" << std::endl + #if BOOST_CONTRACT_EXIT_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} + diff --git a/test/disable/checking_pre_disable_nothing.cpp b/test/disable/checking_pre_disable_nothing.cpp new file mode 100644 index 0000000..07c8119 --- /dev/null +++ b/test/disable/checking_pre_disable_nothing.cpp @@ -0,0 +1,6 @@ + +// Test other contract checking but pre disabled within contract checking. + +#define BOOST_CONTRACT_CONFIG_PRECONDITIONS_DISABLE_NOTHING +#include "checking.hpp" + diff --git a/test/public_function/bases_protected.cpp b/test/public_function/bases_protected.cpp new file mode 100644 index 0000000..7fa3743 --- /dev/null +++ b/test/public_function/bases_protected.cpp @@ -0,0 +1,82 @@ + +// Test overriding function never overrides protected function contract. + +#include "../aux_/oteststream.hpp" +#include +#include +#include +#include +#include +#include + +boost::contract::aux::test::oteststream out; + +// NOTE: This is the correct way of programming contracts for base protected +// and public overriding function. + +struct b { + static void static_invariant() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + +protected: + virtual void f() { // Protected do not use public_function (or virtual_). + boost::contract::guard c = boost::contract::function() + .precondition([] { out << "b::f::pre" << std::endl; }) + .old([] { out << "b::f::old" << std::endl; }) + .postcondition([] { out << "b::f::post" << std::endl; }) + ; + out << "b::f::body" << std::endl; + } +}; +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; } + + void f(boost::contract::virtual_* v = 0) /* not override */ { + // C++ func a::f overrides b::f, but contracts don't (so no override_f). + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([] { out << "a::f::pre" << std::endl; }) + .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(); + ok.str(""); ok + #if BOOST_CONTRACT_ENTRY_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_PRECONDITIONS + << "a::f::pre" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f::old" << std::endl + #endif + << "a::f::body" << std::endl + #if BOOST_CONTRACT_EXIT_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} + diff --git a/test/public_function/bases_protected_error.cpp b/test/public_function/bases_protected_error.cpp new file mode 100644 index 0000000..bb88c89 --- /dev/null +++ b/test/public_function/bases_protected_error.cpp @@ -0,0 +1,43 @@ + +// Test a public function contract cannot override from a protected one. + +#include +#include +#include +#include + +struct b { +protected: + virtual void f(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this); + } + + friend class boost::contract::access; // Test this cannot prevent error. +}; +struct a + #define BASES public b + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + void f(boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function( + v, &a::f, this); + } + + // GCC and CLang cannot correctly even see b::f as part of overloaded bases + // because it is protected. MSVC at least still fails compilation where + // override_f below tries to call b::f because it is protected so it cannot + // be seen from within override_f. + BOOST_CONTRACT_OVERRIDE(f) + + friend class boost::contract::access; // Test this cannot prevent error. +}; + +int main() { + a aa; + aa.f(); + return 0; +} + diff --git a/test/public_function/overload.cpp b/test/public_function/overload.cpp new file mode 100644 index 0000000..7e04c6b --- /dev/null +++ b/test/public_function/overload.cpp @@ -0,0 +1,5 @@ + +// Test public function overloads (with variadic templates if supported). + +#include "overload.hpp" + diff --git a/test/public_function/overload.hpp b/test/public_function/overload.hpp new file mode 100644 index 0000000..febc0ad --- /dev/null +++ b/test/public_function/overload.hpp @@ -0,0 +1,338 @@ + +// Test public function overloads. + +#include "../aux_/oteststream.hpp" +#include +#include +#include +#include +#include +#include +#include + +boost::contract::aux::test::oteststream out; + +struct b { + static void static_invariant() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + + virtual void f(int x, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([] { out << "b::f(int)::pre" << std::endl; }) + .old([] { out << "b::f(int)::old" << std::endl; }) + .postcondition([] { out << "b::f(int)::post" << std::endl; }) + ; + out << "b::f(int)::body" << std::endl; + } + + virtual void f(char const* x, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([] { out << "b::f(char const*)::pre" << std::endl; }) + .old([] { out << "b::f(char const*)::old" << std::endl; }) + .postcondition( + [] { out << "b::f(char const*)::post" << std::endl; }) + ; + out << "b::f(char const*)::body" << std::endl; + } + + virtual void f(int x, int y, boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([] { out << "b::f(int, int)::pre" << std::endl; }) + .old([] { out << "b::f(int, int)::old" << std::endl; }) + .postcondition([] { out << "b::f(int, int)::post" << std::endl; }) + ; + out << "b::f(int, int)::body" << std::endl; + } + + virtual void f(boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([] { out << "b::f()::pre" << std::endl; }) + .old([] { out << "b::f()::old" << std::endl; }) + .postcondition([] { out << "b::f()::post" << std::endl; }) + ; + out << "b::f()::body" << std::endl; + } + + void f(int x[2][3], boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition([] { out << "b::f(int[2][3])::pre" << std::endl; }) + .old([] { out << "b::f(int[2][3])::old" << std::endl; }) + .postcondition([] { out << "b::f(int[2][3])::post" << std::endl; }) + ; + out << "b::f(int[2][3])::body" << std::endl; + } + + void f(void (*x)(int), boost::contract::virtual_* v = 0) { + boost::contract::guard c = boost::contract::public_function(v, this) + .precondition( + [] { out << "b::f(void (*)(int))::pre" << std::endl; }) + .old( + [] { out << "b::f(void (*)(int))::old" << std::endl; }) + .postcondition( + [] { out << "b::f(void (*)(int))::post" << std::endl; }) + ; + out << "b::f(void (*)(int))::body" << std::endl; + } +}; + +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; } + + void f(int x, boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function( + v, + static_cast(&a::f), + this, x + ) + .precondition([] { out << "a::f(int)::pre" << std::endl; }) + .old([] { out << "a::f(int)::old" << std::endl; }) + .postcondition([] { out << "a::f(int)::post" << std::endl; }) + ; + out << "a::f(int)::body" << std::endl; + } + + // Test overload via argument type. + void f(char const* x, boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function( + v, + static_cast( + &a::f), + this, x + ) + .precondition([] { out << "a::f(char const*)::pre" << std::endl; }) + .old([] { out << "a::f(char const*)::old" << std::endl; }) + .postcondition( + [] { out << "a::f(char const*)::post" << std::endl; }) + ; + out << "a::f(char const*)::body" << std::endl; + } + + // Test overload via argument count. + void f(int x, int y, boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function( + v, + static_cast( + &a::f), + this, x, y + ) + .precondition([] { out << "a::f(int, int)::pre" << std::endl; }) + .old([] { out << "a::f(int, int)::old" << std::endl; }) + .postcondition([] { out << "a::f(int, int)::post" << std::endl; }) + ; + out << "a::f(int, int)::body" << std::endl; + } + + // Test overload via template argument type. + template + void f(T x) { // Template cannot be virtual (or override) in C++. + boost::contract::guard c = boost::contract::public_function(this) + .precondition([] { out << "a::f(T)::pre" << std::endl; }) + .old([] { out << "a::f(T)::old" << std::endl; }) + .postcondition([] { out << "a::f(T)::post" << std::endl; }) + ; + out << "a::f(T)::body" << std::endl; + } + + // Test no overload ambiguity in public_function called by these two cases. + + // NOTE: In *all* other cases, public_function is always called with a + // different number of arguments so there cannot be ambiguity either + // (0 args for static, 1 arg for non-virtual, 2 or 3 args for virtual, + // >= 3 for override, so only in cases below of 3 args for virtual and 3 + // for override there could be ambiguity but there is not because of + // presence or absence of override_... template parameter). + + typedef void (a::* f0_ptr)(boost::contract::virtual_*); + + void f(boost::contract::virtual_* v = 0) /* override */ { + f0_ptr f0 = static_cast(&a::f); + // Test this and public_function call in func below both take same 3 + // args but they are ambiguous because of presence override_f. + boost::contract::guard c = boost::contract::public_function( + v, f0, this) + .precondition([] { out << "a::f()::pre" << std::endl; }) + .old([] { out << "a::f()::old" << std::endl; }) + .postcondition([] { out << "a::f()::post" << std::endl; }) + ; + out << "a::f()::body" << std::endl; + } + + virtual f0_ptr f(bool x, boost::contract::virtual_* v = 0) + /* not an override */ { + f0_ptr f0 = static_cast(&a::f); + // Test this and public_function call in func above both take same 3 + // args but they are ambiguous because of lack of override_f. + boost::contract::guard c = boost::contract::public_function( + v, f0, this) + .precondition([] { out << "a::f(bool)::pre" << std::endl; }) + .old([] { out << "a::f(bool)::old" << std::endl; }) + .postcondition([] (f0_ptr const&) { + out << "a::f(bool)::post" << std::endl; }) + ; + out << "a::f(bool)::body" << std::endl; + return f0; + } + + // Test overload with array parameter. + void f(int x[2][3], boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function( + v, + static_cast( + &a::f), + this, x + ) + .precondition([] { out << "a::f(int[2][3])::pre" << std::endl; }) + .old([] { out << "a::f(int[2][3])::old" << std::endl; }) + .postcondition([] { out << "a::f(int[2][3])::post" << std::endl; }) + ; + out << "a::f(int[2][3])::body" << std::endl; + } + + // Test overload with function pointer parameter. + void f(void (*x)(int), boost::contract::virtual_* v = 0) /* override */ { + boost::contract::guard c = boost::contract::public_function( + v, + static_cast( + &a::f), + this, x + ) + .precondition( + [] { out << "a::f(void (*)(int))::pre" << std::endl; }) + .old( + [] { out << "a::f(void (*)(int))::old" << std::endl; }) + .postcondition( + [] { out << "a::f(void (*)(int))::post" << std::endl; }) + ; + out << "a::f(void (*)(int))::body" << std::endl; + } + + BOOST_CONTRACT_OVERRIDE(f) +}; + +void g(int) {} + +std::string ok_args(std::string const& args) { + std::ostringstream ok; ok + #if BOOST_CONTRACT_ENTRY_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_PRECONDITIONS + << "b::f(" << args << ")::pre" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "b::f(" << args << ")::old" << std::endl + << "a::f(" << args << ")::old" << std::endl + #endif + << "a::f(" << args << ")::body" << std::endl + #if BOOST_CONTRACT_EXIT_INVARIANTS + << "b::static_inv" << std::endl + << "b::inv" << std::endl + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "b::f(" << args << ")::old" << std::endl + << "b::f(" << args << ")::post" << std::endl + << "a::f(" << args << ")::post" << std::endl + #endif + ; + return ok.str(); +} + +int main() { + std::ostringstream ok; + a aa; + + out.str(""); + aa.f(123); + ok.str(""); ok << ok_args("int"); + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + aa.f("abc"); + ok.str(""); ok << ok_args("char const*"); + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + aa.f(123, 456); + ok.str(""); ok << ok_args("int, int"); + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + struct {} zz; + aa.f(zz); // Call template (so no override because no virtual). + ok.str(""); ok + #if BOOST_CONTRACT_ENTRY_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_PRECONDITIONS + << "a::f(T)::pre" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f(T)::old" << std::endl + #endif + << "a::f(T)::body" << std::endl + #if BOOST_CONTRACT_EXIT_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f(T)::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + aa.f(); + ok.str(""); ok << ok_args(""); + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + aa.f(true); // This does not override (public_function ambiguity testing). + ok.str(""); ok + #if BOOST_CONTRACT_ENTRY_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_PRECONDITIONS + << "a::f(bool)::pre" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f(bool)::old" << std::endl + #endif + << "a::f(bool)::body" << std::endl + #if BOOST_CONTRACT_EXIT_INVARIANTS + << "a::static_inv" << std::endl + << "a::inv" << std::endl + #endif + #if BOOST_CONTRACT_POSTCONDITIONS + << "a::f(bool)::post" << std::endl + #endif + ; + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + int i[2][3]; + aa.f(i); + ok.str(""); ok << ok_args("int[2][3]"); + BOOST_TEST(out.eq(ok.str())); + + out.str(""); + aa.f(&g); + ok.str(""); ok << ok_args("void (*)(int)"); + BOOST_TEST(out.eq(ok.str())); + + return boost::report_errors(); +} + diff --git a/test/public_function/overload_no_tva.cpp b/test/public_function/overload_no_tva.cpp new file mode 100644 index 0000000..4a47c6b --- /dev/null +++ b/test/public_function/overload_no_tva.cpp @@ -0,0 +1,6 @@ + +// Test public function overloads (always without variadic templates). + +#define BOOST_NO_CXX11_VARIADIC_TEMPLATES +#include "overload.hpp" +