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_simple_queue
8 #include <boost/contract.hpp>
9 #include <boost/optional.hpp>
10 #include <vector>
11 #include <cassert>
12
13 template<typename T>
14 class simple_queue
15 #define BASES private boost::contract::constructor_precondition< \
16 simple_queue<T> >
17 : BASES
18 {
19 friend class boost::contract::access;
20
21 typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types;
22 #undef BASES
23
invariant() const24 void invariant() const {
25 BOOST_CONTRACT_ASSERT(count() >= 0); // Non-negative count.
26 }
27
28 public:
29 /* Creation */
30
31 // Create empty queue.
simple_queue(int a_capacity)32 explicit simple_queue(int a_capacity) :
33 boost::contract::constructor_precondition<simple_queue>([&] {
34 BOOST_CONTRACT_ASSERT(a_capacity > 0); // Positive capacity.
35 })
36 {
37 boost::contract::check c = boost::contract::constructor(this)
__anon1d9f74c00202null38 .postcondition([&] {
39 // Capacity set.
40 BOOST_CONTRACT_ASSERT(capacity() == a_capacity);
41 BOOST_CONTRACT_ASSERT(is_empty()); // Empty.
42 })
43 ;
44
45 items_.reserve(a_capacity);
46 }
47
48 // Destroy queue.
~simple_queue()49 virtual ~simple_queue() {
50 // Check invariants.
51 boost::contract::check c = boost::contract::destructor(this);
52 }
53
54 /* Basic Queries */
55
56 // Items in queue (in their order).
57 // (Somewhat exposes implementation but allows to check more contracts.)
items() const58 std::vector<T> const& items() const {
59 // Check invariants.
60 boost::contract::check c = boost::contract::public_function(this);
61 return items_;
62 }
63
64 // Max number of items queue can hold.
capacity() const65 int capacity() const {
66 // Check invariants.
67 boost::contract::check c = boost::contract::public_function(this);
68 return items_.capacity();
69 }
70
71 /* Derived Queries */
72
73 // Number of items.
count() const74 int count() const {
75 int result;
76 boost::contract::check c = boost::contract::public_function(this)
77 .postcondition([&] {
78 // Return items count.
79 BOOST_CONTRACT_ASSERT(result == int(items().size()));
80 })
81 ;
82
83 return result = items_.size();
84 }
85
86 // Item at head.
head() const87 T const& head() const {
88 boost::optional<T const&> result;
89 boost::contract::check c = boost::contract::public_function(this)
90 .precondition([&] {
91 BOOST_CONTRACT_ASSERT(!is_empty()); // Not empty.
92 })
93 .postcondition([&] {
94 // Return item on top.
95 BOOST_CONTRACT_ASSERT(*result == items().at(0));
96 })
97 ;
98
99 return *(result = items_.at(0));
100 }
101
102 // If queue contains no item.
is_empty() const103 bool is_empty() const {
104 bool result;
105 boost::contract::check c = boost::contract::public_function(this)
106 .postcondition([&] {
107 // Consistent with count.
108 BOOST_CONTRACT_ASSERT(result == (count() == 0));
109 })
110 ;
111
112 return result = (items_.size() == 0);
113 }
114
115 // If queue has no room for another item.
is_full() const116 bool is_full() const {
117 bool result;
118 boost::contract::check c = boost::contract::public_function(this)
119 .postcondition([&] {
120 BOOST_CONTRACT_ASSERT( // Consistent with size and capacity.
121 result == (capacity() == int(items().size())));
122 })
123 ;
124
125 return result = (items_.size() == items_.capacity());
126 }
127
128 /* Commands */
129
130 // Remove head itme and shift all other items.
remove()131 void remove() {
132 // Expensive all_equal postcond. and old_items copy might be skipped.
133 boost::contract::old_ptr<std::vector<T> > old_items;
134 #ifdef BOOST_CONTRACT_AUDIITS
135 = BOOST_CONTRACT_OLDOF(items())
136 #endif // Else, leave old pointer null...
137 ;
138 boost::contract::old_ptr<int> old_count = BOOST_CONTRACT_OLDOF(count());
139 boost::contract::check c = boost::contract::public_function(this)
140 .precondition([&] {
141 BOOST_CONTRACT_ASSERT(!is_empty()); // Not empty.
142 })
143 .postcondition([&] {
144 BOOST_CONTRACT_ASSERT(count() == *old_count - 1); // Count dec.
145 // ...following skipped #ifndef AUDITS.
146 if(old_items) all_equal(items(), *old_items, /* shifted = */ 1);
147 })
148 ;
149
150 items_.erase(items_.begin());
151 }
152
153 // Add item to tail.
put(T const & item)154 void put(T const& item) {
155 // Expensive all_equal postcond. and old_items copy might be skipped.
156 boost::contract::old_ptr<std::vector<T> > old_items;
157 #ifdef BOOST_CONTRACT_AUDITS
158 = BOOST_CONTRACT_OLDOF(items())
159 #endif // Else, leave old pointer null...
160 ;
161 boost::contract::old_ptr<int> old_count = BOOST_CONTRACT_OLDOF(count());
162 boost::contract::check c = boost::contract::public_function(this)
163 .precondition([&] {
164 BOOST_CONTRACT_ASSERT(count() < capacity()); // Room for add.
165 })
166 .postcondition([&] {
167 BOOST_CONTRACT_ASSERT(count() == *old_count + 1); // Count inc.
168 // Second to last item.
169 BOOST_CONTRACT_ASSERT(items().at(count() - 1) == item);
170 // ...following skipped #ifndef AUDITS.
171 if(old_items) all_equal(items(), *old_items);
172 })
173 ;
174
175 items_.push_back(item);
176 }
177
178 private:
179 // Contract helper.
all_equal(std::vector<T> const & left,std::vector<T> const & right,unsigned offset=0)180 static bool all_equal(std::vector<T> const& left,
181 std::vector<T> const& right, unsigned offset = 0) {
182 boost::contract::check c = boost::contract::function()
183 .precondition([&] {
184 // Correct offset.
185 BOOST_CONTRACT_ASSERT(right.size() == left.size() + offset);
186 })
187 ;
188
189 for(unsigned i = offset; i < right.size(); ++i) {
190 if(left.at(i - offset) != right.at(i)) return false;
191 }
192 return true;
193 }
194
195 std::vector<T> items_;
196 };
197
main()198 int main() {
199 simple_queue<int> q(10);
200 q.put(123);
201 q.put(456);
202
203 assert(q.capacity() == 10);
204 assert(q.head() == 123);
205
206 assert(!q.is_empty());
207 assert(!q.is_full());
208
209 std::vector<int> const& items = q.items();
210 assert(items.at(0) == 123);
211 assert(items.at(1) == 456);
212
213 q.remove();
214 assert(q.count() == 1);
215
216 return 0;
217 }
218 //]
219
220