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