| 1 | |
| 2 | // Copyright (C) 2008-2018 Lorenzo Caminiti |
| 3 | // Distributed under the Boost Software License, Version 1.0 (see accompanying |
| 4 | // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt). |
| 5 | // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html |
| 6 | |
| 7 | //[mitchell02_name_list |
| 8 | #include <boost/contract.hpp> |
| 9 | #include <string> |
| 10 | #include <vector> |
| 11 | #include <algorithm> |
| 12 | #include <cassert> |
| 13 | |
| 14 | // List of names. |
| 15 | class name_list { |
| 16 | friend class boost::contract::access; |
| 17 | |
| 18 | void invariant() const { |
| 19 | BOOST_CONTRACT_ASSERT(count() >= 0); // Non-negative count. |
| 20 | } |
| 21 | |
| 22 | public: |
| 23 | /* Creation */ |
| 24 | |
| 25 | // Create an empty list. |
| 26 | name_list() { |
| 27 | boost::contract::check c = boost::contract::constructor(obj: this) |
| 28 | .postcondition(f: [&] { |
| 29 | BOOST_CONTRACT_ASSERT(count() == 0); // Empty list. |
| 30 | }) |
| 31 | ; |
| 32 | } |
| 33 | |
| 34 | // Destroy list. |
| 35 | virtual ~name_list() { |
| 36 | // Check invariants. |
| 37 | boost::contract::check c = boost::contract::destructor(obj: this); |
| 38 | } |
| 39 | |
| 40 | /* Basic Queries */ |
| 41 | |
| 42 | // Number of names in list. |
| 43 | int count() const { |
| 44 | // Check invariants. |
| 45 | boost::contract::check c = boost::contract::public_function(obj: this); |
| 46 | return names_.size(); |
| 47 | } |
| 48 | |
| 49 | // Is name in list? |
| 50 | bool has(std::string const& name) const { |
| 51 | bool result; |
| 52 | boost::contract::check c = boost::contract::public_function(obj: this) |
| 53 | .postcondition(f: [&] { |
| 54 | // If empty, has not. |
| 55 | if(count() == 0) BOOST_CONTRACT_ASSERT(!result); |
| 56 | }) |
| 57 | ; |
| 58 | |
| 59 | return result = names_.cend() != std::find(first: names_.cbegin(), |
| 60 | last: names_.cend(), val: name); |
| 61 | } |
| 62 | |
| 63 | /* Commands */ |
| 64 | |
| 65 | // Add name to list, if name not already in list. |
| 66 | virtual void put(std::string const& name, |
| 67 | boost::contract::virtual_* v = 0) { |
| 68 | boost::contract::old_ptr<bool> old_has_name = |
| 69 | BOOST_CONTRACT_OLDOF(v, has(name)); |
| 70 | boost::contract::old_ptr<int> old_count = |
| 71 | BOOST_CONTRACT_OLDOF(v, count()); |
| 72 | boost::contract::check c = boost::contract::public_function(v, obj: this) |
| 73 | .precondition(f: [&] { |
| 74 | BOOST_CONTRACT_ASSERT(!has(name)); // Not already in list. |
| 75 | }) |
| 76 | .postcondition(f: [&] { |
| 77 | if(!*old_has_name) { // If-guard allows to relax subcontracts. |
| 78 | BOOST_CONTRACT_ASSERT(has(name)); // Name in list. |
| 79 | BOOST_CONTRACT_ASSERT(count() == *old_count + 1); // Inc. |
| 80 | } |
| 81 | }) |
| 82 | ; |
| 83 | |
| 84 | names_.push_back(x: name); |
| 85 | } |
| 86 | |
| 87 | private: |
| 88 | std::vector<std::string> names_; |
| 89 | }; |
| 90 | |
| 91 | class relaxed_name_list |
| 92 | #define BASES public name_list |
| 93 | : BASES |
| 94 | { |
| 95 | friend class boost::contract::access; |
| 96 | |
| 97 | typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; // Subcontracting. |
| 98 | #undef BASES |
| 99 | |
| 100 | BOOST_CONTRACT_OVERRIDE(put); |
| 101 | |
| 102 | public: |
| 103 | /* Commands */ |
| 104 | |
| 105 | // Add name to list, or do nothing if name already in list (relaxed). |
| 106 | void put(std::string const& name, |
| 107 | boost::contract::virtual_* v = 0) /* override */ { |
| 108 | boost::contract::old_ptr<bool> old_has_name = |
| 109 | BOOST_CONTRACT_OLDOF(v, has(name)); |
| 110 | boost::contract::old_ptr<int> old_count = |
| 111 | BOOST_CONTRACT_OLDOF(v, count()); |
| 112 | boost::contract::check c = boost::contract::public_function< |
| 113 | override_put>(v, &relaxed_name_list::put, obj: this, args: name) |
| 114 | .precondition(f: [&] { // Relax inherited preconditions. |
| 115 | BOOST_CONTRACT_ASSERT(has(name)); // Already in list. |
| 116 | }) |
| 117 | .postcondition(f: [&] { // Inherited post. not checked given if-guard. |
| 118 | if(*old_has_name) { |
| 119 | // Count unchanged if name already in list. |
| 120 | BOOST_CONTRACT_ASSERT(count() == *old_count); |
| 121 | } |
| 122 | }) |
| 123 | ; |
| 124 | |
| 125 | if(!has(name)) name_list::put(name); // Else, do nothing. |
| 126 | } |
| 127 | }; |
| 128 | |
| 129 | int main() { |
| 130 | std::string const js = "John Smith" ; |
| 131 | |
| 132 | relaxed_name_list rl; |
| 133 | rl.put(name: js); |
| 134 | assert(rl.has(js)); |
| 135 | rl.put(name: js); // OK, relaxed contracts allow calling this again (do nothing). |
| 136 | |
| 137 | name_list nl; |
| 138 | nl.put(name: js); |
| 139 | assert(nl.has(js)); |
| 140 | // nl.put(js); // Error, contracts do not allow calling this again. |
| 141 | |
| 142 | return 0; |
| 143 | } |
| 144 | //] |
| 145 | |
| 146 | |