]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/iterator/doc/quickbook/facade_tutorial.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / iterator / doc / quickbook / facade_tutorial.qbk
CommitLineData
7c673cae
FG
1
2[section:facade_tutorial Tutorial]
3
4In this section we'll walk through the implementation of a few
5iterators using `iterator_facade`, based around the simple
6example of a linked list of polymorphic objects. This example was
7inspired by a
8[@http://thread.gmane.org/gmane.comp.lib.boost.user/5100 `posting`]
9by Keith Macdonald on the
10[@http://www.boost.org/more/mailing_lists.htm#users `Boost-Users`]
11mailing list.
12
13
14[h2 The Problem]
15
16
17Say we've written a polymorphic linked list node base class:
18
19 # include <iostream>
20
21 struct node_base
22 {
23 node_base() : m_next(0) {}
24
25 // Each node manages all of its tail nodes
26 virtual ~node_base() { delete m_next; }
27
28 // Access the rest of the list
29 node_base* next() const { return m_next; }
30
31 // print to the stream
32 virtual void print(std::ostream& s) const = 0;
33
34 // double the value
35 virtual void double_me() = 0;
36
37 void append(node_base* p)
38 {
39 if (m_next)
40 m_next->append(p);
41 else
42 m_next = p;
43 }
44
45 private:
46 node_base* m_next;
47 };
48
49Lists can hold objects of different types by linking together
50specializations of the following template:
51
52 template <class T>
53 struct node : node_base
54 {
55 node(T x)
56 : m_value(x)
57 {}
58
59 void print(std::ostream& s) const { s << this->m_value; }
60 void double_me() { m_value += m_value; }
61
62 private:
63 T m_value;
64 };
65
66And we can print any node using the following streaming operator:
67
68 inline std::ostream& operator<<(std::ostream& s, node_base const& n)
69 {
70 n.print(s);
71 return s;
72 }
73
74Our first challenge is to build an appropriate iterator over these
75lists.
76
77[h2 A Basic Iterator Using `iterator_facade`]
78
79We will construct a `node_iterator` class using inheritance from
80`iterator_facade` to implement most of the iterator's operations.
81
82
83 # include "node.hpp"
84 # include <boost/iterator/iterator_facade.hpp>
85
86 class node_iterator
87 : public boost::iterator_facade<...>
88 {
89 ...
90 };
91
92
93
94[h2 Template Arguments for `iterator_facade`]
95
96`iterator_facade` has several template parameters, so we must decide
97what types to use for the arguments. The parameters are `Derived`,
98`Value`, `CategoryOrTraversal`, `Reference`, and `Difference`.
99
100
101[h3 `Derived`]
102
103Because `iterator_facade` is meant to be used with the CRTP
104[Cop95]_ the first parameter is the iterator class name itself,
105`node_iterator`.
106
107[h3 `Value`]
108
109The `Value` parameter determines the `node_iterator`\ 's
110`value_type`. In this case, we are iterating over `node_base`
111objects, so `Value` will be `node_base`.
112
113
114[h3 `CategoryOrTraversal`]
115
116Now we have to determine which `iterator traversal concept`_ our
117`node_iterator` is going to model. Singly-linked lists only have
118forward links, so our iterator can't can't be a `bidirectional
119traversal iterator`_. Our iterator should be able to make multiple
120passes over the same linked list (unlike, say, an
121`istream_iterator` which consumes the stream it traverses), so it
122must be a `forward traversal iterator`_. Therefore, we'll pass
123`boost::forward_traversal_tag` in this position [#category]_.
124
125.. [#category] `iterator_facade` also supports old-style category
126 tags, so we could have passed `std::forward_iterator_tag` here;
127 either way, the resulting iterator's `iterator_category` will
128 end up being `std::forward_iterator_tag`.
129
130[h3 `Reference`]
131
132The `Reference` argument becomes the type returned by
133`node_iterator`\ 's dereference operation, and will also be the
134same as `std::iterator_traits<node_iterator>::reference`. The
135library's default for this parameter is `Value&`; since
136`node_base&` is a good choice for the iterator's `reference`
137type, we can omit this argument, or pass `use_default`.
138
139[h3 `Difference`]
140
141The `Difference` argument determines how the distance between
142two `node_iterator`\ s will be measured and will also be the
143same as `std::iterator_traits<node_iterator>::difference_type`.
144The library's default for `Difference` is `std::ptrdiff_t`, an
145appropriate type for measuring the distance between any two
146addresses in memory, and one that works for almost any iterator,
147so we can omit this argument, too.
148
149The declaration of `node_iterator` will therefore look something
150like:
151
152 # include "node.hpp"
153 # include <boost/iterator/iterator_facade.hpp>
154
155 class node_iterator
156 : public boost::iterator_facade<
157 node_iterator
158 , node_base
159 , boost::forward_traversal_tag
160 >
161 {
162 ...
163 };
164
165
166[h2 Constructors and Data Members]
167
168Next we need to decide how to represent the iterator's position.
169This representation will take the form of data members, so we'll
170also need to write constructors to initialize them. The
171`node_iterator`\ 's position is quite naturally represented using
172a pointer to a `node_base`. We'll need a constructor to build an
173iterator from a `node_base*`, and a default constructor to
174satisfy the `forward traversal iterator`_ requirements [#default]_.
175Our `node_iterator` then becomes:
176
177 # include "node.hpp"
178 # include <boost/iterator/iterator_facade.hpp>
179
180 class node_iterator
181 : public boost::iterator_facade<
182 node_iterator
183 , node_base
184 , boost::forward_traversal_tag
185 >
186 {
187 public:
188 node_iterator()
189 : m_node(0)
190 {}
191
192 explicit node_iterator(node_base* p)
193 : m_node(p)
194 {}
195
196 private:
197 ...
198 node_base* m_node;
199 };
200
201.. [#default] Technically, the C++ standard places almost no
202 requirements on a default-constructed iterator, so if we were
203 really concerned with efficiency, we could've written the
204 default constructor to leave `m_node` uninitialized.
205
206[h2 Implementing the Core Operations]
207
208The last step is to implement the `core operations`_ required by
209the concepts we want our iterator to model. Referring to the
210table__, we can see that the first three rows are applicable
211because `node_iterator` needs to satisfy the requirements for
212`readable iterator`_, `single pass iterator`_, and `incrementable
213iterator`_.
214
215__ `core operations`_
216
217We therefore need to supply `dereference`,
218`equal`, and `increment` members. We don't want these members
219to become part of `node_iterator`\ 's public interface, so we can
220make them private and grant friendship to
221`boost::iterator_core_access`, a "back-door" that
222`iterator_facade` uses to get access to the core operations:
223
224 # include "node.hpp"
225 # include <boost/iterator/iterator_facade.hpp>
226
227 class node_iterator
228 : public boost::iterator_facade<
229 node_iterator
230 , node_base
231 , boost::forward_traversal_tag
232 >
233 {
234 public:
235 node_iterator()
236 : m_node(0) {}
237
238 explicit node_iterator(node_base* p)
239 : m_node(p) {}
240
241 private:
242 friend class boost::iterator_core_access;
243
244 void increment() { m_node = m_node->next(); }
245
246 bool equal(node_iterator const& other) const
247 {
248 return this->m_node == other.m_node;
249 }
250
251 node_base& dereference() const { return *m_node; }
252
253 node_base* m_node;
254 };
255
256Voila; a complete and conforming readable, forward-traversal
257iterator! For a working example of its use, see
258[@../example/node_iterator1.cpp `this program`].
259
260__ ../example/node_iterator1.cpp
261
262[h2 A constant `node_iterator`]
263
264[blurb *Constant and Mutable iterators*[br][br]
265The term **mutable iterator** means an iterator through which
266the object it references (its "referent") can be modified. A
267**constant iterator** is one which doesn't allow modification of
268its referent.[br][br]
269The words *constant* and *mutable* don't refer to the ability to
270modify the iterator itself. For example, an `int const*` is a
271non-\ `const` *constant iterator*, which can be incremented
272but doesn't allow modification of its referent, and `int*
273const` is a `const` *mutable iterator*, which cannot be
274modified but which allows modification of its referent.[br][br]
275Confusing? We agree, but those are the standard terms. It
276probably doesn't help much that a container's constant iterator
277is called `const_iterator`.
278]
279
280Now, our `node_iterator` gives clients access to both `node`\
281's `print(std::ostream&) const` member function, but also its
282mutating `double_me()` member. If we wanted to build a
283*constant* `node_iterator`, we'd only have to make three
284changes:
285
286 class const_node_iterator
287 : public boost::iterator_facade<
288 node_iterator
289 , node_base **const**
290 , boost::forward_traversal_tag
291 >
292 {
293 public:
294 const_node_iterator()
295 : m_node(0) {}
296
297 explicit const_node_iterator(node_base* p)
298 : m_node(p) {}
299
300 private:
301 friend class boost::iterator_core_access;
302
303 void increment() { m_node = m_node->next(); }
304
305 bool equal(const_node_iterator const& other) const
306 {
307 return this->m_node == other.m_node;
308 }
309
310 node_base **const**\ & dereference() const { return \*m_node; }
311
312 node_base **const**\ * m_node;
313 };
314
315[blurb `const` and an iterator's `value_type`[br][br]
316The C++ standard requires an iterator's `value_type` *not* be
317`const`\ -qualified, so `iterator_facade` strips the
318`const` from its `Value` parameter in order to produce the
319iterator's `value_type`. Making the `Value` argument
320`const` provides a useful hint to `iterator_facade` that the
321iterator is a *constant iterator*, and the default `Reference`
322argument will be correct for all lvalue iterators.
323]
324
325As a matter of fact, `node_iterator` and `const_node_iterator`
326are so similar that it makes sense to factor the common code out
327into a template as follows:
328
329 template <class Value>
330 class node_iter
331 : public boost::iterator_facade<
332 node_iter<Value>
333 , Value
334 , boost::forward_traversal_tag
335 >
336 {
337 public:
338 node_iter()
339 : m_node(0) {}
340
341 explicit node_iter(Value* p)
342 : m_node(p) {}
343
344 private:
345 friend class boost::iterator_core_access;
346
347 bool equal(node_iter<Value> const& other) const
348 {
349 return this->m_node == other.m_node;
350 }
351
352 void increment()
353 { m_node = m_node->next(); }
354
355 Value& dereference() const
356 { return *m_node; }
357
358 Value* m_node;
359 };
360 typedef node_iter<node_base> node_iterator;
361 typedef node_iter<node_base const> node_const_iterator;
362
363
364[h2 Interoperability]
365
366Our `const_node_iterator` works perfectly well on its own, but
367taken together with `node_iterator` it doesn't quite meet
368expectations. For example, we'd like to be able to pass a
369`node_iterator` where a `node_const_iterator` was expected,
370just as you can with `std::list<int>`\ 's `iterator` and
371`const_iterator`. Furthermore, given a `node_iterator` and a
372`node_const_iterator` into the same list, we should be able to
373compare them for equality.
374
375This expected ability to use two different iterator types together
376is known as |interoperability|_. Achieving interoperability in
377our case is as simple as templatizing the `equal` function and
378adding a templatized converting constructor [#broken]_ [#random]_:
379
380 template <class Value>
381 class node_iter
382 : public boost::iterator_facade<
383 node_iter<Value>
384 , Value
385 , boost::forward_traversal_tag
386 >
387 {
388 public:
389 node_iter()
390 : m_node(0) {}
391
392 explicit node_iter(Value* p)
393 : m_node(p) {}
394
395 template <class OtherValue>
396 node_iter(node_iter<OtherValue> const& other)
397 : m_node(other.m_node) {}
398
399 private:
400 friend class boost::iterator_core_access;
401 template <class> friend class node_iter;
402
403 template <class OtherValue>
404 bool equal(node_iter<OtherValue> const& other) const
405 {
406 return this->m_node == other.m_node;
407 }
408
409 void increment()
410 { m_node = m_node->next(); }
411
412 Value& dereference() const
413 { return *m_node; }
414
415 Value* m_node;
416 };
417 typedef impl::node_iterator<node_base> node_iterator;
418 typedef impl::node_iterator<node_base const> node_const_iterator;
419
420.. |interoperability| replace:: **interoperability**
421.. _interoperability: new-iter-concepts.html#interoperable-iterators-lib-interoperable-iterators
422
423.. [#broken] If you're using an older compiler and it can't handle
424 this example, see the `example code`__ for workarounds.
425
426.. [#random] If `node_iterator` had been a `random access
427 traversal iterator`_, we'd have had to templatize its
428 `distance_to` function as well.
429
430
431__ ../example/node_iterator2.hpp
432
433You can see an example program which exercises our interoperable
434iterators
435[@../example/node_iterator2.cpp `here`].
436
437
438[h2 Telling the Truth]
439
440Now `node_iterator` and `node_const_iterator` behave exactly as
441you'd expect... almost. We can compare them and we can convert in
442one direction: from `node_iterator` to `node_const_iterator`.
443If we try to convert from `node_const_iterator` to
444`node_iterator`, we'll get an error when the converting
445constructor tries to initialize `node_iterator`\ 's `m_node`, a
446`node*` with a `node const*`. So what's the problem?
447
448The problem is that
449`boost::`\ |is_convertible|_\ `<node_const_iterator,node_iterator>::value`
450will be `true`, but it should be `false`. |is_convertible|_
451lies because it can only see as far as the *declaration* of
452`node_iter`\ 's converting constructor, but can't look inside at
453the *definition* to make sure it will compile. A perfect solution
454would make `node_iter`\ 's converting constructor disappear when
455the `m_node` conversion would fail.
456
457.. |is_convertible| replace:: `is_convertible`
458.. _is_convertible: ../../type_traits/index.html#relationships
459
460In fact, that sort of magic is possible using
461|enable_if|__. By rewriting the converting constructor as
462follows, we can remove it from the overload set when it's not
463appropriate:
464
465 #include <boost/type_traits/is_convertible.hpp>
466 #include <boost/utility/enable_if.hpp>
467
468 ...
469
470 private:
471 struct enabler {};
472
473 public:
474 template <class OtherValue>
475 node_iter(
476 node_iter<OtherValue> const& other
477 , typename boost::enable_if<
478 boost::is_convertible<OtherValue*,Value*>
479 , enabler
480 >::type = enabler()
481 )
482 : m_node(other.m_node) {}
483
484.. |enable_if| replace:: `boost::enable_if`
485__ ../../utility/enable_if.html
486
487
488[h2 Wrap Up]
489
490This concludes our `iterator_facade` tutorial, but before you
491stop reading we urge you to take a look at |iterator_adaptor|__.
492There's another way to approach writing these iterators which might
493even be superior.
494
495.. |iterator_adaptor| replace:: `iterator_adaptor`
496__ iterator_adaptor.html
497
498.. _`iterator traversal concept`: new-iter-concepts.html#iterator-traversal-concepts-lib-iterator-traversal
499.. _`readable iterator`: new-iter-concepts.html#readable-iterators-lib-readable-iterators
500.. _`lvalue iterator`: new-iter-concepts.html#lvalue-iterators-lib-lvalue-iterators
501.. _`single pass iterator`: new-iter-concepts.html#single-pass-iterators-lib-single-pass-iterators
502.. _`incrementable iterator`: new-iter-concepts.html#incrementable-iterators-lib-incrementable-iterators
503.. _`forward traversal iterator`: new-iter-concepts.html#forward-traversal-iterators-lib-forward-traversal-iterators
504.. _`bidirectional traversal iterator`: new-iter-concepts.html#bidirectional-traversal-iterators-lib-bidirectional-traversal-iterators
505.. _`random access traversal iterator`: new-iter-concepts.html#random-access-traversal-iterators-lib-random-access-traversal-iterators
506
507[endsect]