1 | // |
2 | // Boost.Pointer Container |
3 | // |
4 | // Copyright Thorsten Ottosen 2003-2005. Use, modification and |
5 | // distribution is subject to the Boost Software License, Version |
6 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
7 | // http://www.boost.org/LICENSE_1_0.txt) |
8 | // |
9 | // For more information, see http://www.boost.org/libs/ptr_container/ |
10 | // |
11 | |
12 | // |
13 | // This example is intended to get you started. |
14 | // Notice how the smart container |
15 | // |
16 | // 1. takes ownership of objects |
17 | // 2. transfers ownership |
18 | // 3. applies indirection to iterators |
19 | // 4. clones objects from other smart containers |
20 | // |
21 | |
22 | // |
23 | // First we select which container to use. |
24 | // |
25 | #include <boost/ptr_container/ptr_deque.hpp> |
26 | |
27 | // |
28 | // we need these later in the example |
29 | // |
30 | #include <boost/assert.hpp> |
31 | #include <string> |
32 | #include <exception> |
33 | |
34 | |
35 | // |
36 | // Then we define a small polymorphic class |
37 | // hierarchy. |
38 | // |
39 | |
40 | class animal : boost::noncopyable |
41 | { |
42 | virtual std::string do_speak() const = 0; |
43 | std::string name_; |
44 | |
45 | protected: |
46 | // |
47 | // Animals cannot be copied... |
48 | // |
49 | animal( const animal& r ) : name_( r.name_ ) { } |
50 | void operator=( const animal& ); |
51 | |
52 | private: |
53 | // |
54 | // ...but due to advances in genetics, we can clone them! |
55 | // |
56 | |
57 | virtual animal* do_clone() const = 0; |
58 | |
59 | public: |
60 | animal( const std::string& name ) : name_(name) { } |
61 | virtual ~animal() throw() { } |
62 | |
63 | std::string speak() const |
64 | { |
65 | return do_speak(); |
66 | } |
67 | |
68 | std::string name() const |
69 | { |
70 | return name_; |
71 | } |
72 | |
73 | animal* clone() const |
74 | { |
75 | return do_clone(); |
76 | } |
77 | }; |
78 | |
79 | // |
80 | // An animal is still not Clonable. We need this last hook. |
81 | // |
82 | // Notice that we pass the animal by const reference |
83 | // and return by pointer. |
84 | // |
85 | |
86 | animal* new_clone( const animal& a ) |
87 | { |
88 | return a.clone(); |
89 | } |
90 | |
91 | // |
92 | // We do not need to define 'delete_clone()' since |
93 | // since the default is to call the default 'operator delete()'. |
94 | // |
95 | |
96 | const std::string muuuh = "Muuuh!" ; |
97 | const std::string oiink = "Oiiink" ; |
98 | |
99 | class cow : public animal |
100 | { |
101 | virtual std::string do_speak() const |
102 | { |
103 | return muuuh; |
104 | } |
105 | |
106 | virtual animal* do_clone() const |
107 | { |
108 | return new cow( *this ); |
109 | } |
110 | |
111 | public: |
112 | cow( const std::string& name ) : animal(name) { } |
113 | }; |
114 | |
115 | class pig : public animal |
116 | { |
117 | virtual std::string do_speak() const |
118 | { |
119 | return oiink; |
120 | } |
121 | |
122 | virtual animal* do_clone() const |
123 | { |
124 | return new pig( *this ); |
125 | } |
126 | |
127 | public: |
128 | pig( const std::string& name ) : animal(name) { } |
129 | }; |
130 | |
131 | // |
132 | // Then we, of course, need a place to put all |
133 | // those animals. |
134 | // |
135 | |
136 | class farm |
137 | { |
138 | // |
139 | // This is where the smart containers are handy |
140 | // |
141 | typedef boost::ptr_deque<animal> barn_type; |
142 | barn_type barn; |
143 | |
144 | #if !defined(BOOST_NO_CXX11_SMART_PTR) && !(defined(BOOST_MSVC) && BOOST_MSVC == 1600) && !BOOST_WORKAROUND(BOOST_GCC, < 40600) |
145 | typedef std::unique_ptr<barn_type> raii_ptr; |
146 | #else |
147 | typedef std::auto_ptr<barn_type> raii_ptr; |
148 | #endif |
149 | |
150 | // |
151 | // An error type |
152 | // |
153 | struct farm_trouble : public std::exception { }; |
154 | |
155 | public: |
156 | // |
157 | // We would like to make it possible to |
158 | // iterate over the animals in the farm |
159 | // |
160 | typedef barn_type::iterator animal_iterator; |
161 | |
162 | // |
163 | // We also need to count the farm's size... |
164 | // |
165 | typedef barn_type::size_type size_type; |
166 | |
167 | // |
168 | // And we also want to transfer an animal |
169 | // safely around. The easiest way to think |
170 | // about '::auto_type' is to imagine a simplified |
171 | // 'std::auto_ptr<T>' ... this means you can expect |
172 | // |
173 | // T* operator->() |
174 | // T* release() |
175 | // deleting destructor |
176 | // |
177 | // but not more. |
178 | // |
179 | typedef barn_type::auto_type animal_transport; |
180 | |
181 | // |
182 | // Create an empty farm. |
183 | // |
184 | farm() { } |
185 | |
186 | // |
187 | // We need a constructor that can make a new |
188 | // farm by cloning a range of animals. |
189 | // |
190 | farm( animal_iterator begin, animal_iterator end ) |
191 | : |
192 | // |
193 | // Objects are always cloned before insertion |
194 | // unless we explicitly add a pointer or |
195 | // use 'release()'. Therefore we actually |
196 | // clone all animals in the range |
197 | // |
198 | barn( begin, end ) { } |
199 | |
200 | // |
201 | // ... so we need some other function too |
202 | // |
203 | |
204 | animal_iterator begin() |
205 | { |
206 | return barn.begin(); |
207 | } |
208 | |
209 | animal_iterator end() |
210 | { |
211 | return barn.end(); |
212 | } |
213 | |
214 | // |
215 | // Here it is quite ok to have an 'animal*' argument. |
216 | // The smart container will handle all ownership |
217 | // issues. |
218 | // |
219 | void buy_animal( animal* a ) |
220 | { |
221 | barn.push_back( x: a ); |
222 | } |
223 | |
224 | // |
225 | // The farm can also be in economical trouble and |
226 | // therefore be in the need to sell animals. |
227 | // |
228 | animal_transport sell_animal( animal_iterator to_sell ) |
229 | { |
230 | if( to_sell == end() ) |
231 | throw farm_trouble(); |
232 | |
233 | // |
234 | // Here we remove the animal from the barn, |
235 | // but the animal is not deleted yet...it's |
236 | // up to the buyer to decide what |
237 | // to do with it. |
238 | // |
239 | return barn.release( where: to_sell ); |
240 | } |
241 | |
242 | // |
243 | // How big a farm do we have? |
244 | // |
245 | size_type size() const |
246 | { |
247 | return barn.size(); |
248 | } |
249 | |
250 | // |
251 | // If things are bad, we might choose to sell all animals :-( |
252 | // |
253 | raii_ptr sell_farm() |
254 | { |
255 | return barn.release(); |
256 | } |
257 | |
258 | // |
259 | // However, if things are good, we might buy somebody |
260 | // else's farm :-) |
261 | // |
262 | |
263 | void buy_farm( raii_ptr other ) |
264 | { |
265 | // |
266 | // This line inserts all the animals from 'other' |
267 | // and is guaranteed either to succeed or to have no |
268 | // effect |
269 | // |
270 | barn.transfer( before: barn.end(), // insert new animals at the end |
271 | from&: *other ); // we want to transfer all animals, |
272 | // so we use the whole container as argument |
273 | // |
274 | // You might think you would have to do |
275 | // |
276 | // other.release(); |
277 | // |
278 | // but '*other' is empty and can go out of scope as it wants |
279 | // |
280 | BOOST_ASSERT( other->empty() ); |
281 | } |
282 | |
283 | }; // class 'farm'. |
284 | |
285 | int main() |
286 | { |
287 | // |
288 | // First we make a farm |
289 | // |
290 | farm animal_farm; |
291 | BOOST_ASSERT( animal_farm.size() == 0u ); |
292 | |
293 | animal_farm.buy_animal( a: new pig("Betty" ) ); |
294 | animal_farm.buy_animal( a: new pig("Benny" ) ); |
295 | animal_farm.buy_animal( a: new pig("Jeltzin" ) ); |
296 | animal_farm.buy_animal( a: new cow("Hanz" ) ); |
297 | animal_farm.buy_animal( a: new cow("Mary" ) ); |
298 | animal_farm.buy_animal( a: new cow("Frederik" ) ); |
299 | BOOST_ASSERT( animal_farm.size() == 6u ); |
300 | |
301 | // |
302 | // Then we make another farm...it will actually contain |
303 | // a clone of the other farm. |
304 | // |
305 | farm new_farm( animal_farm.begin(), animal_farm.end() ); |
306 | BOOST_ASSERT( new_farm.size() == 6u ); |
307 | |
308 | // |
309 | // Is it really clones in the new farm? |
310 | // |
311 | BOOST_ASSERT( new_farm.begin()->name() == "Betty" ); |
312 | |
313 | // |
314 | // Then we search for an animal, Mary (the Crown Princess of Denmark), |
315 | // because we would like to buy her ... |
316 | // |
317 | typedef farm::animal_iterator iterator; |
318 | iterator to_sell; |
319 | for( iterator i = animal_farm.begin(), |
320 | end = animal_farm.end(); |
321 | i != end; ++i ) |
322 | { |
323 | if( i->name() == "Mary" ) |
324 | { |
325 | to_sell = i; |
326 | break; |
327 | } |
328 | } |
329 | |
330 | farm::animal_transport mary = animal_farm.sell_animal( to_sell ); |
331 | |
332 | |
333 | if( mary->speak() == muuuh ) |
334 | // |
335 | // Great, Mary is a cow, and she may live longer |
336 | // |
337 | new_farm.buy_animal( a: mary.release() ); |
338 | else |
339 | // |
340 | // Then the animal would be destroyed (!) |
341 | // when we go out of scope. |
342 | // |
343 | ; |
344 | |
345 | // |
346 | // Now we can observe some changes to the two farms... |
347 | // |
348 | BOOST_ASSERT( animal_farm.size() == 5u ); |
349 | BOOST_ASSERT( new_farm.size() == 7u ); |
350 | |
351 | // |
352 | // The new farm has however underestimated how much |
353 | // it cost to feed Mary and its owner is forced to sell the farm... |
354 | // |
355 | animal_farm.buy_farm( other: new_farm.sell_farm() ); |
356 | |
357 | BOOST_ASSERT( new_farm.size() == 0u ); |
358 | BOOST_ASSERT( animal_farm.size() == 12u ); |
359 | } |
360 | |