From 73bb28243d397cbf641b212747d259bdf2f20f68 Mon Sep 17 00:00:00 2001 From: Lorenzo Caminiti Date: Fri, 28 Oct 2016 11:46:10 -0700 Subject: [PATCH] x --- Jamroot | 2 +- TODO.txt | 12 + .../boost_contract_no.jam | 0 .../boost_contract_no.jam-gen.py | 0 .../contracts-old_value_bug.hpp | 276 ++++++++++++++++++ 5 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 TODO.txt rename boost_contract_no.jam => build/boost_contract_no.jam (100%) rename boost_contract_no.jam-gen.py => build/boost_contract_no.jam-gen.py (100%) create mode 100644 test/public_function/contracts-old_value_bug.hpp diff --git a/Jamroot b/Jamroot index 006063e..d6dbd28 100644 --- a/Jamroot +++ b/Jamroot @@ -4,7 +4,7 @@ # 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 -import boost_contract_no ; +import build/boost_contract_no ; import testing ; import os ; import feature ; diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..79d4d8f --- /dev/null +++ b/TODO.txt @@ -0,0 +1,12 @@ +./include/boost/contract/core/config.hpp:12:// TODO: Find out the C++ example that gives linker error on global variables that are not static/extern and then test this library against that example (for all boost_link mode: shared, static, and header-only). +./include/boost/contract/old.hpp:228: // TODO: Document that auto old_x = BOOST_CONTRACT_OLD(...) will use old_ptr and not old_ptr_if_copyable (auto will by default not use old_ptr_if_conpyable because old_ptr is more stringent from a type requirement prospective, if users want to relax the copyable type requirements they need to explicitly use old_ptr_if_copyable instead of using auto). +./include/boost/contract/old.hpp:322: // TODO: Will this shared_ptr destroy old_value_copy given its type is erased to void*?? Test it! +./include/boost/contract.hpp:28:// TODO: Document that boost::contract::function() can be used to program contracts for lambda functions. And also "abused" a bit to program pre/postconditions for any arbitrary scope of code in function body. +./include/boost/contract.hpp:30:// TODO: Document that friends do not in general check invariants so their contracts are usually programmed using function(). But if a function friend of an object takes an instance of that object as a parameter and therefore is essentially part of the object's public API, then programmers can make that explicit by using public_function(obj) after function() to program the friend function contract (but note that in general friends functions can take instances of multiple different objects because the same function can be friend of different classes). Also add a test (under test/public_function/...) and an example for friend. +./include/boost/contract.hpp:32:// TODO: Document that noexcept (and exception throw(...)) specifications of the enclosing function apply to contracts. So if a contract handler is set so contract failures throw, noexcept function will still not throw, they will always terminate (because that's what users of such functions except, even if the function fails in any way, including the function contract fails). +./include/boost/contract.hpp:34:// TODO: Document that if installing contract failure handlers that throw, it might be a good idea to check invariants at exit. That way the destructor entry invariant should *in theory* never fail (unless not all public functions, ctors are contracted, or because of non-contracted friends, or because of public data members... all those can still break the invariant before the dtor is called and before this lib will detect the invariants have failed so the dtor entry inv will fail... and what will dtor do in that case? terminate?). +./include/boost/contract.hpp:36:// TODO: Document (in Getting Started) that some example source code has `//[...` and `//]` tags used to import example code in Quickbook docs (so doc's examples always up to date with code). +./include/boost/contract.hpp:38:// TODO: Document that result is NOT accessible in .except's functor because function threw. Old values are accessible in both post and except so OLDOF copies disabled on if both except and post checking disabled (NO_EXCEPTS && NO_POSTCONDITIONS). except will have its own failure handler except_failure, check disabling macro NO_EXCEPTS, etc. +./include/boost/contract.hpp:40:// TODO: Document that contract for constexpr functions cannot be supported at the moment because constexpr functions cannot: (1) declare local variables of (literal) types with non-trivial constexpr destructors (needed by this lib to check inv, post, and except at exit), (2) call other (constexpr) functions with try-catch statements (used by this lib to report assertion failure and catch any other exception that might be thrown by the evaluation of the asserted conditions), (3) use lambda functions (use by this for convenience to program functors that check per and post). Also note that even if supported, contracts for constexpr probably will not use old values (because constexpr prevent the function from having any side effect visible to the caller, variables around such side-effects are usually the candidates for old value copies) and subcontracting (because constexpr functions cannot be virtual). +./include/boost/contract.hpp:42:// TODO: Documentation updates based on all emails to Boost (review all emails). +./include/boost/contract.hpp:44:// TODO: Documentation updates based on all n-papers that I read recently (review notes I wrote on all papers), add those n-papers to the Bibliography section, and add P0380 (the most recent proposal) to the feature comparison table. diff --git a/boost_contract_no.jam b/build/boost_contract_no.jam similarity index 100% rename from boost_contract_no.jam rename to build/boost_contract_no.jam diff --git a/boost_contract_no.jam-gen.py b/build/boost_contract_no.jam-gen.py similarity index 100% rename from boost_contract_no.jam-gen.py rename to build/boost_contract_no.jam-gen.py diff --git a/test/public_function/contracts-old_value_bug.hpp b/test/public_function/contracts-old_value_bug.hpp new file mode 100644 index 0000000..70740b5 --- /dev/null +++ b/test/public_function/contracts-old_value_bug.hpp @@ -0,0 +1,276 @@ + +#ifndef BOOST_CONTRACT_TEST_PUBLIC_FUNCTION_CONTRACTS_HPP_ +#define BOOST_CONTRACT_TEST_PUBLIC_FUNCTION_CONTRACTS_HPP_ + +// Copyright (C) 2008-2016 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 + +// Test public member function subcontracting (also with old and return values). + +#include "../detail/oteststream.hpp" +#include "../detail/counter.hpp" +#include +#include +#include +#include +#include +#include +#include + +boost::contract::test::detail::oteststream out; + +struct s_tag; +typedef boost::contract::test::detail::counter s_type; + +struct except_error {}; + +struct result_type { + std::string value; + explicit result_type(std::string const& s) : value(s) {} + +private: // Test non-copyable and non-default-constructible result. + result_type(); + result_type(result_type const&); + result_type& operator=(result_type const&); +}; + +// Test base without additional bases and pure virtual. +template +struct t { + static void static_invariant() { out << Id << "::static_inv" << std::endl; } + + void invariant() const { + out << Id << "::inv" << std::endl; + BOOST_CONTRACT_ASSERT(z.value != ""); + } + + struct z_tag; + typedef boost::contract::test::detail::counter z_type; + z_type z; + + t() { z.value.push_back(Id); } + + virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0) = 0; +}; + +template // Requires: Only pass lower case Id so it'll never be 'X'. +result_type& t::f(s_type& s, boost::contract::virtual_* v) { + std::ostringstream r; r << "none-" << Id; + static result_type result(r.str()); + boost::contract::old_ptr old_z = + BOOST_CONTRACT_OLD(v, z_type::eval(z)); + boost::contract::old_ptr old_s; + boost::contract::check c = boost::contract::public_function(v, result, this) + .precondition([&] { + out << Id << "::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(s.value[0] == Id || s.value[0] == 'X'); + }) + .old([&] { + out << Id << "::f::old" << std::endl; + old_s = BOOST_CONTRACT_OLD(v, s_type::eval(s)); + }) + .postcondition([&] (result_type const& result) { + out << Id << "::f::post" << std::endl; + BOOST_CONTRACT_ASSERT(z.value == old_z->value + old_s->value); + std::clog << Id << "::f::post OUT" << std::endl; + BOOST_CONTRACT_ASSERT(s.value.find(old_z->value) != + std::string::npos); + BOOST_CONTRACT_ASSERT(result.value == old_s->value); + }) + .except([&] { + out << Id << "::f::except" << std::endl; + BOOST_CONTRACT_ASSERT(z.value == old_z->value); + BOOST_CONTRACT_ASSERT(s.value == old_s->value); + }) + ; + out << "t<" << Id << ">::f::body" << std::endl; + if(s.value == "X") throw except_error(); + return result; +} + +struct u { + u() { std::cout << "!!!!! u default ctor" << std::endl; } + u(u const&) { std::cout << "!!!!! u copy ctor" << std::endl; } + ~u() { std::cout << "!!!!! u dtor" << std::endl; } +private: + u& operator=(u const&); + +}; + +// Test base with other bases, multiple inheritance, and no subcontracting from +// protected and private bases (even if fully contracted). +struct c + #define BASES public t<'d'>, protected t<'p'>, private t<'q'>, public t<'e'> + : BASES +{ + typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; + #undef BASES + + static void static_invariant() { out << "c::static_inv" << std::endl; } + + void invariant() const { + out << "c::inv" << std::endl; + BOOST_CONTRACT_ASSERT(y.value != ""); + } + + struct y_tag; + typedef boost::contract::test::detail::counter y_type; + y_type y; + + c() : uu() { y.value = "c"; } + + u uu; + + virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0) + /* override */ { + static result_type result("none-c"); + boost::contract::old_ptr old_y = + BOOST_CONTRACT_OLD(v, y_type::eval(y)); + boost::contract::old_ptr old_s; + boost::contract::old_ptr old_u; + boost::contract::check c = boost::contract::public_function< + override_f>(v, result, &c::f, this, s) + .precondition([&] { + out << "c::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(s.value == "C" || s.value == "X"); + }) + .old([&] { + out << "c::f::old" << std::endl; + old_s = BOOST_CONTRACT_OLD(v, s_type::eval(s)); + old_u = BOOST_CONTRACT_OLD(v, uu); + }) + .postcondition([&] (result_type const& result) { + out << "c::f::post" << std::endl; + std::clog << "c::f::post OUT" << std::endl; + BOOST_CONTRACT_ASSERT(y.value == old_y->value + old_s->value); + BOOST_CONTRACT_ASSERT(s.value.find(old_y->value) != + std::string::npos); + BOOST_CONTRACT_ASSERT(result.value == old_s->value); + }) + .except([&] { + out << "c::f::except" << std::endl; + BOOST_CONTRACT_ASSERT(y.value == old_y->value); + BOOST_CONTRACT_ASSERT(s.value == old_s->value); + }) + ; + + out << "c::f::body" << std::endl; + if(s.value == "X") throw except_error(); + std::string save_s = s.value; + + std::string save = y.value; + y.value += save_s; + s.value = save; + + save = t<'d'>::z.value; + t<'d'>::z.value += save_s; + s.value += save; + + save = t<'e'>::z.value; + t<'e'>::z.value += save_s; + s.value += save; + + result.value = save_s; + return result; + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +// Test no subcontracting from not (fully) contracted base. +struct b { + static void static_invariant() { out << "b::static_inv" << std::endl; } + void invariant() const { out << "b::inv" << std::endl; } + + virtual ~b() {} + + // No contract (no virtual_ so this is not actually overridden by a::f). + virtual result_type& f(s_type& s) { + static result_type result("none-b"); + out << "b::f::body" << std::endl; + result.value = s.value; + return result; + } +}; + +// Test public function with both non-contracted and contracted bases. +struct a + #define BASES public b, public c + : 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; + BOOST_CONTRACT_ASSERT(x.value != ""); + } + + struct x_tag; + typedef boost::contract::test::detail::counter x_type; + x_type x; + + a() { x.value = "a"; } + + // Must use virtual_ even if no longer decl virtual for correct overloading. + result_type& f(s_type& s, boost::contract::virtual_* v = 0) + /* override */ { + static result_type result("none-a"); + boost::contract::old_ptr old_x = + BOOST_CONTRACT_OLD(v, x_type::eval(x)); + boost::contract::old_ptr old_s; + boost::contract::check c = boost::contract::public_function< + override_f>(v, result, &a::f, this, s) + .precondition([&] { + out << "a::f::pre" << std::endl; + BOOST_CONTRACT_ASSERT(s.value == "A" || s.value == "X"); + }) + .old([&] { + out << "a::f::old" << std::endl; + old_s = BOOST_CONTRACT_OLD(v, s_type::eval(s)); + }) + .postcondition([&] (result_type const& result) { + out << "a::f::post" << std::endl; + BOOST_CONTRACT_ASSERT(x.value == old_x->value + old_s->value); + BOOST_CONTRACT_ASSERT(s.value.find(old_x->value) != + std::string::npos); + BOOST_CONTRACT_ASSERT(result.value == old_s->value); + }) + .except([&] { + out << "a::f::except" << std::endl; + BOOST_CONTRACT_ASSERT(x.value == old_x->value); + BOOST_CONTRACT_ASSERT(s.value == old_s->value); + }) + ; + + out << "a::f::body" << std::endl; + if(s.value == "X") throw except_error(); + std::string save_s = s.value; + + std::string save = x.value; + x.value += save_s; + s.value = save; + + save = y.value; + y.value += save_s; + s.value += save; + + save = t<'d'>::z.value; + t<'d'>::z.value += save_s; + s.value += save; + + save = t<'e'>::z.value; + t<'e'>::z.value += save_s; + s.value += save; + + result.value = save_s; + return result; + } + BOOST_CONTRACT_OVERRIDE(f) +}; + +#endif // #include guard +