]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | .. Copyright David Abrahams 2004. Use, modification and distribution is |
2 | .. subject to the Boost Software License, Version 1.0. (See accompanying | |
3 | .. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
4 | ||
5 | In this section we'll walk through the implementation of a few | |
6 | iterators using ``iterator_facade``, based around the simple | |
7 | example of a linked list of polymorphic objects. This example was | |
8 | inspired by a `posting`__ by Keith Macdonald on the `Boost-Users`_ | |
9 | mailing list. | |
10 | ||
11 | .. _`Boost-Users`: http://www.boost.org/more/mailing_lists.htm#users | |
12 | ||
13 | __ http://thread.gmane.org/gmane.comp.lib.boost.user/5100 | |
14 | ||
15 | The Problem | |
16 | ----------- | |
17 | ||
18 | Say we've written a polymorphic linked list node base class:: | |
19 | ||
20 | # include <iostream> | |
21 | ||
22 | struct node_base | |
23 | { | |
24 | node_base() : m_next(0) {} | |
25 | ||
26 | // Each node manages all of its tail nodes | |
27 | virtual ~node_base() { delete m_next; } | |
28 | ||
29 | // Access the rest of the list | |
30 | node_base* next() const { return m_next; } | |
31 | ||
32 | // print to the stream | |
33 | virtual void print(std::ostream& s) const = 0; | |
34 | ||
35 | // double the value | |
36 | virtual void double_me() = 0; | |
37 | ||
38 | void append(node_base* p) | |
39 | { | |
40 | if (m_next) | |
41 | m_next->append(p); | |
42 | else | |
43 | m_next = p; | |
44 | } | |
45 | ||
46 | private: | |
47 | node_base* m_next; | |
48 | }; | |
49 | ||
50 | Lists can hold objects of different types by linking together | |
51 | specializations of the following template:: | |
52 | ||
53 | template <class T> | |
54 | struct node : node_base | |
55 | { | |
56 | node(T x) | |
57 | : m_value(x) | |
58 | {} | |
59 | ||
60 | void print(std::ostream& s) const { s << this->m_value; } | |
61 | void double_me() { m_value += m_value; } | |
62 | ||
63 | private: | |
64 | T m_value; | |
65 | }; | |
66 | ||
67 | And we can print any node using the following streaming operator:: | |
68 | ||
69 | inline std::ostream& operator<<(std::ostream& s, node_base const& n) | |
70 | { | |
71 | n.print(s); | |
72 | return s; | |
73 | } | |
74 | ||
75 | Our first challenge is to build an appropriate iterator over these | |
76 | lists. | |
77 | ||
78 | A Basic Iterator Using ``iterator_facade`` | |
79 | ------------------------------------------ | |
80 | ||
81 | We will construct a ``node_iterator`` class using inheritance from | |
82 | ``iterator_facade`` to implement most of the iterator's operations. | |
83 | ||
84 | :: | |
85 | ||
86 | # include "node.hpp" | |
87 | # include <boost/iterator/iterator_facade.hpp> | |
88 | ||
89 | class node_iterator | |
90 | : public boost::iterator_facade<...> | |
91 | { | |
92 | ... | |
93 | }; | |
94 | ||
95 | ||
96 | ||
97 | Template Arguments for ``iterator_facade`` | |
98 | .......................................... | |
99 | ||
100 | ``iterator_facade`` has several template parameters, so we must decide | |
101 | what types to use for the arguments. The parameters are ``Derived``, | |
102 | ``Value``, ``CategoryOrTraversal``, ``Reference``, and ``Difference``. | |
103 | ||
104 | ||
105 | ``Derived`` | |
106 | ''''''''''' | |
107 | ||
108 | Because ``iterator_facade`` is meant to be used with the CRTP | |
109 | [Cop95]_ the first parameter is the iterator class name itself, | |
110 | ``node_iterator``. | |
111 | ||
112 | ``Value`` | |
113 | ''''''''' | |
114 | ||
115 | The ``Value`` parameter determines the ``node_iterator``\ 's | |
116 | ``value_type``. In this case, we are iterating over ``node_base`` | |
117 | objects, so ``Value`` will be ``node_base``. | |
118 | ||
119 | ||
120 | ``CategoryOrTraversal`` | |
121 | ''''''''''''''''''''''' | |
122 | ||
123 | Now we have to determine which `iterator traversal concept`_ our | |
124 | ``node_iterator`` is going to model. Singly-linked lists only have | |
125 | forward links, so our iterator can't can't be a `bidirectional | |
126 | traversal iterator`_. Our iterator should be able to make multiple | |
127 | passes over the same linked list (unlike, say, an | |
128 | ``istream_iterator`` which consumes the stream it traverses), so it | |
129 | must be a `forward traversal iterator`_. Therefore, we'll pass | |
130 | ``boost::forward_traversal_tag`` in this position [#category]_. | |
131 | ||
132 | .. [#category] ``iterator_facade`` also supports old-style category | |
133 | tags, so we could have passed ``std::forward_iterator_tag`` here; | |
134 | either way, the resulting iterator's ``iterator_category`` will | |
135 | end up being ``std::forward_iterator_tag``. | |
136 | ||
137 | ``Reference`` | |
138 | ''''''''''''' | |
139 | ||
140 | The ``Reference`` argument becomes the type returned by | |
141 | ``node_iterator``\ 's dereference operation, and will also be the | |
142 | same as ``std::iterator_traits<node_iterator>::reference``. The | |
143 | library's default for this parameter is ``Value&``; since | |
144 | ``node_base&`` is a good choice for the iterator's ``reference`` | |
145 | type, we can omit this argument, or pass ``use_default``. | |
146 | ||
147 | ``Difference`` | |
148 | '''''''''''''' | |
149 | ||
150 | The ``Difference`` argument determines how the distance between | |
151 | two ``node_iterator``\ s will be measured and will also be the | |
152 | same as ``std::iterator_traits<node_iterator>::difference_type``. | |
153 | The library's default for ``Difference`` is ``std::ptrdiff_t``, an | |
154 | appropriate type for measuring the distance between any two | |
155 | addresses in memory, and one that works for almost any iterator, | |
156 | so we can omit this argument, too. | |
157 | ||
158 | The declaration of ``node_iterator`` will therefore look something | |
159 | like:: | |
160 | ||
161 | # include "node.hpp" | |
162 | # include <boost/iterator/iterator_facade.hpp> | |
163 | ||
164 | class node_iterator | |
165 | : public boost::iterator_facade< | |
166 | node_iterator | |
167 | , node_base | |
168 | , boost::forward_traversal_tag | |
169 | > | |
170 | { | |
171 | ... | |
172 | }; | |
173 | ||
174 | Constructors and Data Members | |
175 | ............................. | |
176 | ||
177 | Next we need to decide how to represent the iterator's position. | |
178 | This representation will take the form of data members, so we'll | |
179 | also need to write constructors to initialize them. The | |
180 | ``node_iterator``\ 's position is quite naturally represented using | |
181 | a pointer to a ``node_base``. We'll need a constructor to build an | |
182 | iterator from a ``node_base*``, and a default constructor to | |
183 | satisfy the `forward traversal iterator`_ requirements [#default]_. | |
184 | Our ``node_iterator`` then becomes:: | |
185 | ||
186 | # include "node.hpp" | |
187 | # include <boost/iterator/iterator_facade.hpp> | |
188 | ||
189 | class node_iterator | |
190 | : public boost::iterator_facade< | |
191 | node_iterator | |
192 | , node_base | |
193 | , boost::forward_traversal_tag | |
194 | > | |
195 | { | |
196 | public: | |
197 | node_iterator() | |
198 | : m_node(0) | |
199 | {} | |
200 | ||
201 | explicit node_iterator(node_base* p) | |
202 | : m_node(p) | |
203 | {} | |
204 | ||
205 | private: | |
206 | ... | |
207 | node_base* m_node; | |
208 | }; | |
209 | ||
210 | .. [#default] Technically, the C++ standard places almost no | |
211 | requirements on a default-constructed iterator, so if we were | |
212 | really concerned with efficiency, we could've written the | |
213 | default constructor to leave ``m_node`` uninitialized. | |
214 | ||
215 | Implementing the Core Operations | |
216 | ................................ | |
217 | ||
218 | The last step is to implement the `core operations`_ required by | |
219 | the concepts we want our iterator to model. Referring to the | |
220 | table__, we can see that the first three rows are applicable | |
221 | because ``node_iterator`` needs to satisfy the requirements for | |
222 | `readable iterator`_, `single pass iterator`_, and `incrementable | |
223 | iterator`_. | |
224 | ||
225 | __ `core operations`_ | |
226 | ||
227 | We therefore need to supply ``dereference``, | |
228 | ``equal``, and ``increment`` members. We don't want these members | |
229 | to become part of ``node_iterator``\ 's public interface, so we can | |
230 | make them private and grant friendship to | |
231 | ``boost::iterator_core_access``, a "back-door" that | |
232 | ``iterator_facade`` uses to get access to the core operations:: | |
233 | ||
234 | # include "node.hpp" | |
235 | # include <boost/iterator/iterator_facade.hpp> | |
236 | ||
237 | class node_iterator | |
238 | : public boost::iterator_facade< | |
239 | node_iterator | |
240 | , node_base | |
241 | , boost::forward_traversal_tag | |
242 | > | |
243 | { | |
244 | public: | |
245 | node_iterator() | |
246 | : m_node(0) {} | |
247 | ||
248 | explicit node_iterator(node_base* p) | |
249 | : m_node(p) {} | |
250 | ||
251 | private: | |
252 | friend class boost::iterator_core_access; | |
253 | ||
254 | void increment() { m_node = m_node->next(); } | |
255 | ||
256 | bool equal(node_iterator const& other) const | |
257 | { | |
258 | return this->m_node == other.m_node; | |
259 | } | |
260 | ||
261 | node_base& dereference() const { return *m_node; } | |
262 | ||
263 | node_base* m_node; | |
264 | }; | |
265 | ||
266 |