utility/iterator_adaptors.htm
2000-09-28 12:35:46 +00:00

630 lines
23 KiB
HTML
Raw Blame History

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>Header boost/iterator_adaptors.hpp Documentation</title>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<img src="../../c++boost.gif" alt="c++boost.gif (8819 bytes)"
align="center" width="277" height="86">
<h1>Header
<a href="../../boost/pending/iterator_adaptors.hpp">boost/iterator_adaptors.hpp</a></h1>
<p>The file <tt>boost/iterator_adaptors.hpp</tt>
includes the main <tt>iterator_adaptors</tt> class and several other classes
for constructing commonly used iterator adaptors.</p>
<ul>
<li><a href="#iterator_adaptors"><tt>iterator_adaptors</tt></a>.
<li><a href="#iterator_adaptor"><tt>iterator_adaptor</tt></a>.
<li><a href="#transform_iterator"><tt>transform_iterator</tt></a>
<li><a href="#indirect_iterators"><tt>indirect_iterators</tt></a>
<li><a href="#reverse_iterators"><tt>reverse_iterators</tt></a>
<li><a href="#integer_range"><tt>integer_range</tt></a>
</ul>
<!-- put in something about Andrei Alexandrescu's contribution? -->
<p><a href="http://www.boost.org/people/dave_abrahams.htm">Dave
Abrahams</a> started the library, coming up with the idea to use
policy classes and how to handle the const/non-const iterator
interactions. He also contributed the <tt>indirect_iterators</tt> and
<tt>reverse_iterators</tt> classes.<br>
<a href="http://www.boost.org/people/jeremy_siek.htm">Jeremy Siek</a>
contributed <tt>transform_iterator</tt>, <tt>integer_range</tt>,
and this documentation.
<h3><a name="iterator_adaptors">The Iterator Adaptors Class</a></h3>
Implementing standard conforming iterators is a non-trivial task.
There are some fine-points such as iterator/const_iterator
interactions and there are the myriad of operators that should be
implemented but are easily forgotten such as
<tt>operator-&gt;()</tt>. The purpose of the
<tt>iterator_adaptors</tt> class is to make it easier to implement an
iterator class, and even easier to extend and adapt existing iterator
types. The <tt>iterator_adaptors</tt> class itself is not an adaptor
class but a <i>type generator</i>. It generates a pair of adaptor classes,
one class for the mutable iterator and one class for the const
iterator. The definition of the <tt>iterator_adaptors</tt> class is as
follows:
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
template &lt;class Iterator,
class ConstIterator,
class Traits = std::iterator_traits&lt;Iterator&gt;,
class ConstTraits = std::iterator_traits&lt;ConstIterator&gt;,
class Policies = default_iterator_policies&gt;
struct iterator_adaptors
{
typedef ... iterator;
typedef ... const_iterator;
};
</PRE></TD></TABLE>
<p>The <tt>Iterator</tt> and <tt>ConstIterator</tt> template parameters
are the iterator types that you want to adapt. The <tt>Traits</tt> and
<tt>ConstTraits</tt> must be iterator traits classes. The traits
parameters default to the specialization of the
<tt>std::iterator_traits</tt> class for the adapted iterators. If you
want the traits for your new iterator adaptor (<tt>value_type</tt>,
<tt>iterator_category</tt>, etc.) to be the same as the adapted
iterator then use the default, otherwise create your own traits
classes and pass them in <a href="#1">[1]</a>.
<p>The <tt>Policies</tt> class that you pass in will become the heart of
the iterator adaptor. The policy class determines how your new adaptor
class will behave. The <tt>Policies</tt> class must implement 3, 4, or
7 of the core iterator operations depending on whether you wish the
new iterator adaptor class to be a
<a href="http://www.sgi.com/Technology/STL/ForwardIterator.html">
ForwardIterator</a>,
<a href="http://www.sgi.com/Technology/STL/BidirectionalIterator.html">
BidirectionalIterator</a>, or <a
href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
RandomAccessIterator</a>. Make sure that the
<tt>iterator_category</tt> type of the traits class you pass in
matches the category of iterator that you want to create. The default
policy class, <tt>default_iterator_policies</tt>, implements all 7 of
the core operations in the usual way. If you wish to create an
iterator adaptor that only changes a few of the iterator's behaviors,
then you can have your new policy class inherit from
<tt>default_iterator_policies</tt> to avoid retyping the usual
behaviours. You should also look at <tt>default_iterator_policies</tt>
as the &quot;boiler-plate&quot; for your own policy classes. The
following is definition of the <tt>default_iterator_policies</tt>
class:
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
struct default_iterator_policies
{
// required for a ForwardIterator
template &lt;class Reference, class Iterator&gt;
Reference dereference(type&lt;Reference&gt;, const Iterator& x) const
{ return *x; }
template &lt;class Iterator&gt;
void increment(Iterator& x) const
{ ++x; }
template &lt;class Iterator1, class Iterator2&gt;
bool equal(Iterator1& x, Iterator2& y) const
{ return x == y; }
// required for a BidirectionalIterator
template &lt;class Iterator&gt;
void decrement(Iterator& x) const
{ --x; }
// required for a RandomAccessIterator
template &lt;class Iterator, class DifferenceType&gt;
void advance(Iterator& x, DifferenceType n) const
{ x += n; }
template &lt;class Difference, class Iterator1, class Iterator2&gt;
Difference distance(type&lt;Difference&gt;, Iterator1& x, Iterator2& y) const
{ return y - x; }
template &lt;class Iterator1, class Iterator2&gt;
bool less(Iterator1& x, Iterator2& y) const
{ return x &lt; y; }
};
</PRE></TD></TABLE>
<p>
The generated iterator adaptor types will have the following
constructors.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
<i>iterator</i>(const Iterator& i, const Policies& p = Policies())
<i>const_iterator</i>(const ConstIterator& i, const Policies& p = Policies())
</PRE></TD></TABLE>
<h3><a name="iterator_adaptor">The Iterator Adaptor Class</a></h3>
This is the class used inside of the <tt>iterator_adaptors</tt> type
generator. Use this class directly (instead of using
<tt>iterator_adaptors</tt>) when there is no difference between the
const and non-const versions of the iterator type. Often this is
because there is only a const (read-only) version of the iterator, as
is the case for <tt>std::set</tt>'s iterators. Use the same type for
the <tt>Iterator</tt> and <tt>NonconstIterator</tt> template
arguments.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
template &lt;class Iterator,
class Policies = default_iterator_policies,
class NonconstIterator = Iterator,
class Traits = std::iterator_traits&lt;Iterator&gt; &gt;
struct iterator_adaptor;
</PRE></TD></TABLE>
<p>
Next we will look at some iterator adaptors that are examples of how
to use the iterator adaptors class, and that are useful iterator
adaptors in their own right.
<h3><a name="transform_iterator">The Transform Iterator Class</a></h3>
It is often useful to automatically apply some function to the value
returned by dereferencing (<tt>operator*()</tt>) an iterator. The
<tt>transform_iterators</tt> class makes it easy to create an iterator
adaptor that does just that.
First let us consider what the <tt>Policies</tt> class for the transform
iterator should look like. We are only changing one of the iterator
behaviours, so we will inherit from
<tt>default_iterator_policies</tt>. In addition, we will need a
function object to apply, so we will have a template parameter and a
data member for the function object. The function will take one
argument (the dereferenced value) and we will need to know the
<tt>result_type</tt> of the function, so <a
href="http://www.sgi.com/Technology/STL/AdaptableUnaryFunction.html">
AdaptableUnaryFunction</a> is the corrent concept to choose for the
function object type. Now for the heart of our iterator adaptor, we
implement the <tt>dereference</tt> method, applying the function
object to <tt>*i</tt>. The <tt>type&lt;Reference&gt;</tt> class is
there to tell you what the reference type of the iterator is, which is
handy when writing generic iterator adaptors such as this one <a
href="#2">[2]</a>.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
template &lt;class AdaptableUnaryFunction&gt;
struct transform_iterator_policies : public default_iterator_policies
{
transform_iterator_policies(const AdaptableUnaryFunction& f) : m_f(f) { }
template &lt;class Reference, class Iterator&gt;
Reference dereference(type&lt;Reference&gt;, const Iterator& i) const
{ return m_f(*i); }
AdaptableUnaryFunction m_f;
};
</PRE></TD></TABLE>
Next we need to create the traits class for our new iterator. In some
situations you may need to create a separate traits class for the
const and non-const iterator types, but here a single traits class
will do. The <tt>value_type</tt> and <tt>reference</tt> type of our
transform iterator will be the <tt>result_type</tt> of the function
object. The <tt>difference_type</tt> and <tt>iterator_category</tt>
will be the same as the adapted iterator.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
template &lt;class AdaptableUnaryFunction, class IteratorTraits&gt;
struct transform_iterator_traits {
typedef typename AdaptableUnaryFunction::result_type value_type;
typedef value_type reference;
typedef value_type* pointer;
typedef typename IteratorTraits::difference_type difference_type;
typedef typename IteratorTraits::iterator_category iterator_category;
};
</PRE></TD></TABLE>
The final step is to use the <tt>iterator_adaptor</tt> class to
construct our transform iterator. We will use the single iterator
adaptor version because we will not need to create both a mutable and
const version of the transform iterator. The transform iterator is
inherently a read-only iterator. The nicest way to package up our new
transform iterator is to create a type generator similar to
<tt>iterator_adaptor</tt>. The first template parameter will be the
type of the function object. The second parameter will be the adapted
iterator type. The third parameter is the trait class for
the adapted iterator. Inside the <tt>transform_iterators</tt> class
we use the <tt>transform_iterator_traits</tt> class defined above to
create the traits class for the new transform iterator. We then use
the <tt>iterator_adaptor</tt> class to extract the generated
iterator adaptor type.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
template &lt;class AdaptableUnaryFunction,
class Iterator,
class Traits = std::iterator_traits&lt;Iterator&gt;
&gt;
struct transform_iterator
{
typedef transform_iterator_traits&lt;AdaptableUnaryFunction,Traits&gt;
TransTraits;
typedef iterator_adaptor&lt;Iterator, TransTraits,
transform_iterator_policies&lt;AdaptableUnaryFunction&gt; &gt;::type type;
};
</PRE></TD></TABLE>
<p>
The following is a simple example of how to use the
<tt>transform_iterators</tt> class to iterate through a range of
numbers, multiplying each of them by 2 when they are dereferenced.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;boost/iterator_adaptors.hpp&gt;
int
main(int, char*[])
{
int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
typedef std::binder1st&lt; std::multiplies&lt;int&gt; &gt; Function;
typedef boost::transform_iterator&lt;Function, int*,
boost::iterator&lt;std::random_access_iterator_tag, int&gt;
&gt;::type doubling_iterator;
doubling_iterator i(x, std::bind1st(std::multiplies&lt;int&gt;(), 2)),
i_end(x + sizeof(x)/sizeof(int), std::bind1st(std::multiplies&lt;int&gt;(), 2));
std::cout &lt;&lt; "multiplying the array by 2:" &lt;&lt; std::endl;
while (i != i_end)
std::cout &lt;&lt; *i++ &lt;&lt; " ";
std::cout &lt;&lt; std::endl;
return 0;
}
</PRE></TD></TABLE>
<h3><a name="indirect_iterators">The Indirect Iterators Class</a></h3>
It is not all that uncommon to create data structures that consist of
pointers to pointers. For such a structure it might be nice to have an
iterator that applies a double-dereference inside the
<tt>operator*()</tt>. The implementation of this is similar to the
<tt>transform_iterators</tt><a href="#3">[3]</a>. We first create a
policies class which does a double-dereference in the
<tt>dereference()</tt> method. We then create a traits class, this
time also including a template parameter for the traits of the second
level iterators as well as the first. Lastly we wrap this up in the
type generator <tt>indirect_iterators</tt>, using
<tt>iterator_adaptors</tt> to do most of the work.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
struct indirect_iterator_policies : public default_iterator_policies
{
template &lt;class Reference, class Iterator&gt;
Reference dereference(type&lt;Reference&gt;, const Iterator& x) const
{ return **x; }
};
template &lt;class IndirectIterator,
class IndirectTraits = std::iterator_traits&lt;IndirectIterator&gt;,
class Traits =
std::iterator_traits&lt;typename IndirectTraits::value_type&gt;
&gt;
struct indirect_traits
{
typedef typename IndirectTraits::difference_type difference_type;
typedef typename Traits::value_type value_type;
typedef typename Traits::pointer pointer;
typedef typename Traits::reference reference;
typedef typename IndirectTraits::iterator_category iterator_category;
};
template &lt;class IndirectIterator, class ConstIndirectIterator,
class IndirectTraits =
std::iterator_traits&lt;IndirectIterator&gt;,
class ConstIndirectTraits =
std::iterator_traits&lt;ConstIndirectIterator&gt;,
class Traits =
std::iterator_traits&lt;typename IndirectTraits::value_type&gt;
&gt;
struct indirect_iterators
{
typedef typename IndirectTraits::value_type Iterator;
typedef typename Traits::value_type ValueType;
typedef iterator_adaptors&lt;IndirectIterator, ConstIndirectIterator,
indirect_traits&lt;IndirectIterator, IndirectTraits, Traits&gt;,
indirect_traits&lt;ConstIndirectIterator, ConstIndirectTraits, Traits&gt;,
indirect_iterator_policies
&gt; Adaptors;
typedef typename Adaptors::iterator iterator;
typedef typename Adaptors::const_iterator const_iterator;
};
</PRE></TD></TABLE>
<h3><a name="reverse_iterators">The Reverse Iterators Class</a></h3>
<p>
Yes, there is already a <tt>reverse_iterator</tt> adaptor class
defined in the C++ Standard, but using the <tt>iterator_adaptors</tt>
class we can re-implement this classic adaptor in a more succinct and
elegant fashion. Also, this makes for a good example of using
<tt>iterator_adaptors</tt> that is in familiar territory.
<p>
The first step is to create the <tt>Policies</tt> class. As in the
<tt>std::reverse_iterator</tt> class, we need to flip all the
operations of the iterator. Increment will become decrement, advancing
by <tt>n</tt> will become retreating by <tt>n</tt>, etc.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
struct reverse_iterator_policies
{
template &lt;class Reference, class Iterator&gt;
Reference dereference(type&lt;Reference&gt;, const Iterator& x) const
{ return *boost::prior(x); }
// this is equivalent to { Iterator tmp = x; return *--tmp; }
template &lt;class Iterator&gt;
void increment(Iterator& x) const
{ --x; }
template &lt;class Iterator&gt;
void decrement(Iterator& x) const
{ ++x; }
template &lt;class Iterator, class DifferenceType&gt;
void advance(Iterator& x, DifferenceType n) const
{ x -= n; }
template &lt;class Difference, class Iterator1, class Iterator2&gt;
Difference distance(type&lt;Difference&gt;, Iterator1& x, Iterator2& y) const
{ return x - y; }
template &lt;class Iterator1, class Iterator2&gt;
bool equal(Iterator1& x, Iterator2& y) const
{ return x == y; }
template &lt;class Iterator1, class Iterator2&gt;
bool less(Iterator1& x, Iterator2& y) const
{ return y &lt; x; }
};
</PRE></TD></TABLE>
Since the traits of the reverse iterator adaptor will be the same as
the adapted iterator's traits, we do not need to create new traits
classes as was the case for <tt>transform_iterator</tt>. We can skip to
the final stage of creating a type generator class for our reverse
iterators using the <tt>iterator_adaptor</tt> class.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
template &lt;class Iterator, class ConstIterator,
class Traits = std::iterator_traits&lt;Iterator&gt;,
class ConstTraits = std::iterator_traits&lt;ConstIterator&gt;
&gt;
struct reverse_iterators
{
typedef iterator_adaptors&lt;Iterator,ConstIterator,Traits,ConstTraits,
reverse_iterator_policies&gt; Adaptor;
typedef typename Adaptor::iterator iterator;
typedef typename Adaptor::const_iterator const_iterator;
};
</PRE></TD></TABLE>
A typical use of the <tt>reverse_iterators</tt> class is in
user-defined container types. You can use the
<tt>reverse_iterators</tt> class to generate the reverse iterators for
your container.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
class my_container {
...
typedef ... iterator;
typedef ... const_iterator;
typedef reverse_iterators&lt;iterator, const_iterator&gt; RevIters;
typedef typename RevIters::iterator reverse_iterator;
typedef typename RevIters::const_iterator const_reverse_iterator;
...
};
</PRE></TD></TABLE>
<h3><a name="integer_range">The Integer Range Class</a></h3>
The <tt>iterator_adaptors</tt> class can not only be used for adapting
iterators, but it can also be used to take a non-iterator type and use
it to build an iterator. An especially simple example of this is
turning an integer type into an iterator, a counting iterator. The
builtin integer types of C++ are almost iterators. They have
<tt>operator++()</tt>, <tt>operator--()</tt>, etc. The one operator
they are lacking is the <tt>operator*()</tt>, which we will want to
simply return the current value of the integer. The following few
lines of code implement the policy and traits class for the counting
iterator.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
struct counting_iterator_policies : public default_iterator_policies
{
template &lt;class IntegerType&gt;
IntegerType dereference(type&lt;IntegerType&gt;, const IntegerType& i) const
{ return i; }
};
template &lt;class IntegerType&gt;
struct counting_iterator_traits {
typedef IntegerType value_type;
typedef IntegerType reference;
typedef value_type* pointer;
typedef std::ptrdiff_t difference_type;
typedef std::random_access_iterator_tag iterator_category;
};
</PRE></TD></TABLE>
Typically we will want to count the integers in some range, so a nice
interface would be to have a fake container that represents the range
of integers. The following is the definition of such a class called
<tt>integer_range</tt>.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
template &lt;class IntegerType&gt;
struct integer_range {
typedef typename iterator_adaptor&lt;IntegerType,
counting_iterator_traits&lt;IntegerType&gt;,
counting_iterator_policies &gt;::type iterator;
typedef iterator const_iterator;
typedef IntegerType value_type;
typedef std::ptrdiff_t difference_type;
typedef IntegerType reference;
typedef IntegerType* pointer;
typedef IntegerType size_type;
integer_range(IntegerType start, IntegerType finish)
: m_start(start), m_finish(finish) { }
iterator begin() const { return iterator(m_start); }
iterator end() const { return iterator(m_finish); }
size_type size() const { return m_finish - m_start; }
bool empty() const { return m_finish == m_start; }
void swap(integer_range& x) {
std::swap(m_start, x.m_start);
std::swap(m_finish, x.m_finish);
}
protected:
IntegerType m_start, m_finish;
};
</PRE></TD></TABLE>
<p>
The following is an example of how to use the
<tt>integer_range</tt> class to count from 0 to 4.
<p>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 COLS=2>
<TR><TD WIDTH=30 VALIGN=TOP></TD><TD>
<PRE>
boost::integer_range&lt;int&gt; r(0,5);
cout &lt;&lt; "counting to from 0 to 4:" &lt;&lt; endl;
std::copy(r.begin(), r.end(), ostream_iterator&lt;int&gt;(cout, " "));
cout &lt;&lt; endl;
</PRE></TD></TABLE>
<h3>Challenge</h3>
<p>
There is an unlimited number of ways the the
<tt>iterator_adaptors</tt> class can be used to create iterators. One
interesting exercise would be to re-implement the iterators of
<tt>std::list</tt> and <tt>std::slist</tt> using
<tt>iterator_adaptors</tt>, where the adapted <tt>Iterator</tt> types
would be node pointers.
<h3>Notes</h3>
<p>
<a name="1">[1]</a>
If your compiler does not support partial specialization and hence
does not have a working <tt>std::iterator_traits</tt> class, you will
not be able to use the defaults and will need to supply your own
<tt>Traits</tt> and <tt>ConstTraits</tt> classes.
<p>
<a name="2">[2]</a>
The reference type could also be obtained from
<tt>std::iterator_traits</tt>, but that is not portable on compilers
that do not support partial specialization.
<p>
<a name="3">[3]</a>
It would have been more elegant to implement <tt>indirect_iterators</tt>
using <tt>transform_iterators</tt>, but for subtle reasons that would require
the use of <tt>boost::remove_cv</tt> which is not portable.
<h3>Implementation Notes</h3>
The code is somewhat complicated because there are three iterator
adaptor class: <tt>forward_iterator_adaptor</tt>,
<tt>bidirectional_iterator_adaptor</tt>, and
<tt>random_access_iterator_adaptor</tt>. The alternative would be to
just have one iterator adaptor equivalent to the
<tt>random_access_iterator_adaptor</tt>. The reason for going with
the three adaptors is that according to 14.5.3p5 in the C++ Standard,
friend functions defined inside a template class body are instantiated
when the template class is instantiated. This means that if we only
used the one iterator adaptor, then if the adapted iterator did not
meet all of the requirements for a
<a href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
RandomAccessIterator</a> then a compiler error should occur. Many
current compilers in fact do not instantiate the friend functions
unless used, so we could get away with the one iterator adaptor in
most cases. However, out of respect for the standard this implementation
uses the three adaptors.
<hr>
<p>Revised <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %b %Y" startspan -->27 Sep 2000<!--webbot bot="Timestamp" endspan i-checksum="14936" --></p>
<p><EFBFBD> Copyright Jeremy Siek 2000. Permission to copy, use,
modify, sell and distribute this document is granted provided this copyright
notice appears in all copies. This document is provided &quot;as is&quot;
without express or implied warranty, and with no claim as to its suitability for
any purpose.</p>
</body>
</html>