diff --git a/iterator_adaptors.htm b/iterator_adaptors.htm new file mode 100644 index 0000000..aa3c42a --- /dev/null +++ b/iterator_adaptors.htm @@ -0,0 +1,892 @@ + + + + + + + + + + Boost Iterator Adaptor Library + + + + + c++boost.gif (8819 bytes) + +

Boost Iterator Adaptor Library

+ +

Introduction

+ +

The Iterator Adaptor library allows you transform an arbitrary ``base'' + type into a standard-conforming iterator with the behaviors you choose. + Doing so is especially easy if the ``base'' type is itself an iterator. The + library also supplies several example adaptors which apply + specific useful behaviors to arbitrary base iterators. + +

Table of Contents

+ + + +

Dave + Abrahams started the library, applying policy class technique and + handling const/non-const iterator interactions. He also contributed the + indirect_ and reverse_ iterator generators, and expanded + counting_iterator_generator to + cover all incrementable types. He edited most of the documentation, + sometimes heavily.
+ Jeremy + Siek contributed the transform + iterator adaptor, the integer-only version of counting_iterator_generator, + the function output iterator + adaptor, and most of the documentation.
+ John + Potter contributed the projection_ and filter_ iterator generators and made some + simplifications to the main iterator_adaptor template.
+ + +

Class template + iterator_adaptor

+ Implementing standard conforming iterators is a non-trivial task. There are + some fine points such as the interactions between an iterator and its + corresponding const_iterator, and there are myriad operators that should be + implemented but are easily forgotten or mishandled, such as + operator->(). Using iterator_adaptor, you can easily + implement an iterator class, and even more easily extend and adapt existing iterator + types. Moreover, it is easy to make a pair of interoperable const + and non-const iterators. + +

iterator_adaptor is declared like this: +

+template <class Base, class Policies, 
+    class ValueOrNamedParam = typename std::iterator_traits<Base>::value_type,
+    class ReferenceOrNamedParam = ...(see below),
+    class PointerOrNamedParam = ...(see below),
+    class CategoryOrNamedParam = typename std::iterator_traits<Base>::iterator_category,
+    class DistanceOrNamedParam = typename std::iterator_traits<Base>::difference_type>
+struct iterator_adaptor;
+
+ +

Template Parameters

+ +

Although iterator_adaptor takes seven template parameters, + defaults have been carefully chosen to minimize the number of parameters + you must supply in most cases, especially if BaseType is an + iterator. + + + + + + + + + + + +
Parameter + + Description + +
BaseType + + The type being wrapped. + +
Policies + + A policy + class that supplies core functionality to the resulting iterator. A + detailed description can be found below. + +
Value + + The value_type of the resulting iterator, unless const. If + Value is const X the + value_type will be (non-const) X[1]. If the value_type you wish to use is an abstract + base class see note [5].
+ Default: + std::iterator_traits<BaseType>::value_type [2] + +
Reference + + The reference type of the resulting iterator, and in + particular, the result type of operator*().
+ Default: If Value is supplied, Value& is + used. Otherwise + std::iterator_traits<BaseType>::reference is used. + +
Pointer + + The pointer type of the resulting iterator, and in + particular, the result type of operator->().
+ Default: If Value was supplied, then Value*, + otherwise std::iterator_traits<BaseType>::pointer. + +
Category + + The iterator_category type for the resulting iterator.
+ Default: + std::iterator_traits<BaseType>::iterator_category + +
Distance + + The difference_type for the resulting iterator.
+ Default: + std::iterator_traits<BaseType>::difference_type + +
NamedParam + + A named template parameter (see below). +
+ +

Named Template Parameters

+ + With seven template parameters, providing arguments for + iterator_adaptor in the correct order can be challenging. + Also, often times one would like to specify the sixth or seventh + template parameter, but use the defaults for the third through + fifth. As a solution to these problems we provide a mechanism for + naming the last five template parameters, and providing them in + any order through a set of named template parameters. The following + classes are provided for specifying the parameters. Any of these + classes can be used for any of the last five template parameters + of iterator_adaptor. +
+
+template <class Value> struct value_type_is;
+template <class Reference> struct reference_is;
+template <class Pointer> struct pointer_is;
+template <class Distance> struct difference_type_is;
+template <class Category> struct iterator_category_is;
+
+
+ + For example, the following adapts foo_iterator to create + an InputIterator + with reference type foo, and whose other traits + are determined according to the defaults described above. + +
+
+typedef iterator_adaptor<foo_iterator, foo_policies,
+  reference_is<foo>, iterator_category_is<std::input_iterator_tag>
+  > MyIterator;
+
+
+ + +

The Policies Class

+ +

The main task in using iterator_adaptor is creating an + appropriate Policies class. The Policies class will become + the functional heart of the resulting iterator, supplying the core + operations that determine its behavior. The iterator_adaptor + template defines all of the operators required of a Random Access + Iterator by dispatching to a Policies object. Your + Policies class must implement a subset of the core iterator + operations below corresponding to the iterator categories you want it to + support.
+
+ + + + + + + + + + + + + + +
+ Core Iterator Operations
+ T: adapted iterator type; p: object of type T; n: T::size_type; x: T::difference_type; p1, p2: iterators +
Operation + + Effects + + Implements Operations + + Required for Iterator Categories + +
initialize + + optionally modify base iterator during iterator construction + + constructors + + Input/ Output/ Forward/ Bidirectional/ + Random + Access + + +
dereference + + returns an element of the iterator's reference type + + *p, p[n] + + +
equal + + tests the iterator for equality + + p1 == p2, p1 != p2 + +
increment + + increments the iterator + + ++p, p++ + +
decrement + + decrements the iterator + + --p, p-- + + Bidirectional/ + Random + Access + +
less + + imposes a Strict Weak + Ordering relation on iterators + + + p1 < p2, + p1 <= p2, + p1 > p2, + p1 >= p2 + + Random + Access + +
distance + + measures the distance between iterators + + p1 - p2 + +
advance + + adds an integer offset to iterators + + +p + x, +x + p, +p += x, +p - x, +p -= x + +
+ +

The library also supplies a "trivial" policy class, + default_iterator_policies, which implements all seven of the core + operations in the usual way. If you wish to create an iterator adaptor that + only changes a few of the base type's behaviors, then you can derive your + new policy class from default_iterator_policies to avoid retyping + the usual behaviors. You should also look at + default_iterator_policies as the ``boilerplate'' for your own + policy classes, defining functions with the same interface. This is the + definition of default_iterator_policies:
+
+ +

+
+struct default_iterator_policies
+{
+  template <class BaseType>
+  void initialize(BaseType&)
+    { }
+
+  template <class Reference, class BaseType>
+  Reference dereference(type<Reference>, const BaseType& x) const
+    { return *x; }
+
+  template <class BaseType>
+  void increment(BaseType& x)
+    { ++x; }
+
+  template <class BaseType1, class BaseType2>
+  bool equal(const BaseType1& x, const BaseType2& y) const
+    { return x == y; }
+
+  template <class BaseType>
+  void decrement(BaseType& x)
+    { --x; }
+
+  template <class BaseType, class DifferenceType>
+  void advance(BaseType& x, DifferenceType n)
+    { x += n; }
+
+  template <class Difference, class BaseType1, class BaseType2>
+  Difference distance(type<Difference>, const BaseType1& x, const BaseType2& y) const
+    { return y - x; }
+
+  template <class BaseType1, class BaseType2>
+  bool less(const BaseType1& x, const BaseType2& y) const
+    { return x < y; }
+};
+
+
+ +

Template member functions are used throughout + default_iterator_policies so that it can be employed with a wide + range of iterators. If we had used concrete types above, we'd have tied the + usefulness of default_iterator_policies to a particular range of + adapted iterators. If you follow the same pattern with your + Policies classes, you can use them to generate more specialized + adaptors along the lines of those supplied by this library. + +

Additional Members

+ In addition to all of the member functions required of a Random Access + Iterator, the iterator_adaptor class template defines the + following members.
+
+ + + + + + +
explicit iterator_adaptor(const Base&, const Policies& = + Policies()) +

+ Construct an adapted iterator from a base object and a policies + object. As this constructor is explicit, it does not + provide for implicit conversions from the Base type to + the iterator adaptor. + +
template <class B, class V, class R, class P>
+ iterator_adaptor(const + iterator_adaptor<B,Policies,V,R,P,Category,Distance>&)
+

+ This constructor allows for conversion from non-const to + constant adapted iterators. See below for more details.
+ Requires: B is convertible to Base. + +
base_type base() const; +

+ Return a copy of the base object. +
+ +

Example

+ +

It is often useful to automatically apply some function to the value + returned by dereferencing an iterator. The transform iterator makes it easy to create + an iterator adaptor which does just that. Here we will show how easy it is + to implement the transform iterator using the iterator_adaptor + template. + +

We want to be able to adapt a range of iterators and functions, so the + policies class will have a template parameter for the function type and it + will have a data member of that type. We know that the function takes one + argument and that we'll need to be able to deduce the result_type + of the function so we can use it for the adapted iterator's + value_type. AdaptableUnaryFunction + is the Concept + that fulfills those requirements. + +

To implement a transform iterator we will only change one of the base + iterator's behaviors, so the transform_iterator_policies class can + inherit the rest from default_iterator_policies. We will define + the dereference() member function, which is used to implement + operator*() of the adapted iterator. The implementation will + dereference the base iterator and apply the function object. The + type<Reference> parameter is used to convey the appropriate + return type. The complete code for transform_iterator_policies + is:
+
+ + +

+
+  template <class AdaptableUnaryFunction>
+  struct transform_iterator_policies : public default_iterator_policies
+  {
+    transform_iterator_policies() { }
+
+    transform_iterator_policies(const AdaptableUnaryFunction& f)
+      : m_f(f) { }
+
+    template <class Reference, class BaseIterator>
+    Reference dereference(type<Reference>, const BaseIterator& i) const
+      { return m_f(*i); }
+
+    AdaptableUnaryFunction m_f;
+  };
+
+
+ +

The next step is to use the iterator_adaptor template to + construct the transform iterator type. The nicest way to package the + construction of the transform iterator is to create a type generator. + The first template parameter to the generator will be the type of the + function object and the second will be the base iterator type. We use + iterator_adaptor to define the transform iterator type as a nested + typedef inside the transform_iterator_generator class. + Because the function may return by-value, we must limit the + iterator_category to Input Iterator, and + the iterator's reference type cannot be a true reference (the + standard allows this for input iterators), so in this case we can use few + of iterator_adaptor's default template arguments.
+
+ + +

+
+template <class AdaptableUnaryFunction, class Iterator>
+struct transform_iterator_generator
+{
+    typedef typename AdaptableUnaryFunction::result_type value_type;
+public:
+    typedef iterator_adaptor<Iterator, 
+        transform_iterator_policies<AdaptableUnaryFunction>,
+        value_type, value_type, value_type*, std::input_iterator_tag>
+      type;
+};
+
+
+ +

As a finishing touch, we will create an object generator + for the transform iterator. This is a function that makes it more + convenient to create a transform iterator.
+
+ + +

+
+template <class AdaptableUnaryFunction, class Iterator>
+typename transform_iterator_generator<AdaptableUnaryFunction,Iterator>::type
+make_transform_iterator(Iterator base,
+                        const AdaptableUnaryFunction& f = AdaptableUnaryFunction())
+{
+    typedef typename transform_iterator_generator<AdaptableUnaryFunction,
+      Iterator>::type result_t;
+    return result_t(base, f);
+}
+
+
+ +

Here is an example that shows how to use a transform iterator to iterate + through a range of numbers, multiplying each of them by 2 and printing the + result to standard output.
+
+ + +

+
+#include <functional>
+#include <algorithm>
+#include <iostream>
+#include <boost/iterator_adaptors.hpp>
+
+int main(int, char*[])
+{
+  int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+  const int N = sizeof(x)/sizeof(int);
+  std::cout << "multiplying the array by 2:" << std::endl;
+  std::copy(boost::make_transform_iterator(x, std::bind1st(std::multiplies<int>(), 2)),
+      boost::make_transform_iterator(x + N, std::bind1st(std::multiplies<int>(), 2)),
+      std::ostream_iterator<int>(std::cout, " "));
+  std::cout << std::endl;
+  return 0;
+}
+
+ This output is: +
+2 4 6 8 10 12 14 16
+
+
+ +

Iterator Interactions

+ +

C++ allows const and non-const pointers to interact in + the following intuitive ways: + +

+ + Getting user-defined iterators to work together that way is nontrivial (see + here for an example of where + the C++ standard got it wrong), but iterator_adaptor can make it + easy. The rules are as follows: + + + +

Example

+ +

The Projection Iterator adaptor is similar to the transform iterator adaptor in that +its operator*() applies some function to the result of +dereferencing the base iterator and then returns the result. The +difference is that the function must return a reference to some +existing object (for example, a data member within the +value_type of the base iterator). + +

+The projection_iterator_pair_generator template + is a special two-type generator for mutable and constant versions of a + projection iterator. It is defined as follows: +

+
+template <class AdaptableUnaryFunction, class Iterator, class ConstIterator>
+struct projection_iterator_pair_generator {
+    typedef typename AdaptableUnaryFunction::result_type value_type;
+    typedef projection_iterator_policies<AdaptableUnaryFunction> policies;
+public:
+    typedef iterator_adaptor<Iterator,policies,value_type> iterator;
+    typedef iterator_adaptor<ConstIterator,policies,value_type,
+        const value_type&,const value_type*> const_iterator;
+};
+
+
+ +

It is assumed that the Iterator and ConstIterator arguments are corresponding mutable +and constant iterators.

+ +

Challenge

+ +

There is an unlimited number of ways the iterator_adaptors + class can be used to create iterators. One interesting exercise would be to + re-implement the iterators of std::list and std::slist + using iterator_adaptors, where the adapted Iterator types + would be node pointers. + +

Concept Model

+ Depending on the Base and Policies template parameters, + an iterator_adaptor can be a Input Iterator, Forward + Iterator, Bidirectional + Iterator, or Random Access + Iterator. + +

Declaration Synopsis

+
+template <class Base, class Policies, 
+    class Value = typename std::iterator_traits<Base>::value_type,
+    class Reference = ...(see below),
+    class Pointer = ...(see below),
+    class Category = typename std::iterator_traits<Base>::iterator_category,
+    class Distance = typename std::iterator_traits<Base>::difference_type
+         >
+struct iterator_adaptor
+{
+    typedef Distance difference_type;
+    typedef typename boost::remove_const<Value>::type value_type;
+    typedef Pointer pointer;
+    typedef Reference reference;
+    typedef Category iterator_category;
+    typedef Base base_type;
+    typedef Policies policies_type;
+
+    iterator_adaptor();
+    explicit iterator_adaptor(const Base&, const Policies& = Policies());
+
+    base_type base() const;
+
+    template <class B, class V, class R, class P>
+    iterator_adaptor(
+        const iterator_adaptor<B,Policies,V,R,P,Category,Distance>&);
+
+    reference operator*() const; [6]
+    operator_arrow_result_type operator->() const; [3]
+    value_type operator[](difference_type n) const; [4], [6]
+
+    iterator_adaptor& operator++();
+    iterator_adaptor& operator++(int);
+    iterator_adaptor& operator--();
+    iterator_adaptor& operator--(int);
+
+    iterator_adaptor& operator+=(difference_type n);
+    iterator_adaptor& operator-=(difference_type n);
+
+    iterator_adaptor& operator-(Distance x) const;
+};
+
+template <class B, class P, class V, class R, class Ptr, 
+    class C, class D1, class D2>
+iterator_adaptor<B,P,V,R,Ptr,C,D1>
+operator+(iterator_adaptor<B,P,V,R,Ptr,C,D1>, D2);
+
+template <class B, class P, class V, class R, class Ptr,
+    class C, class D1, class D2>
+iterator_adaptor<B,P,V,R,P,C,D1>
+operator+(D2, iterator_adaptor<B,P,V,R,Ptr,C,D1> p);
+
+template <class B1, class B2, class P, class V1, class V2,
+    class R1, class R2, class P1, class P2, class C, class D>
+Distance operator-(const iterator_adaptor<B1,P,V1,R1,P1,C,D>&, 
+                   const iterator_adaptor<B2,P,V2,R2,P2,C,D>&);
+
+template <class B1, class B2, class P, class V1, class V2,
+    class R1, class R2, class P1, class P2, class C, class D>
+bool operator==(const iterator_adaptor<B1,P,V1,R1,P1,C,D>&, 
+                const iterator_adaptor<B2,P,V2,R2,P2,C,D>&);
+
+// and similarly for operators !=, <, <=, >=, >
+
+ +

Notes

+ +

[1] The standard specifies that the value_type + of const iterators to T (e.g. const T*) is + non-const T, while the pointer and + reference types for all Forward Iterators are + const T* and const T&, respectively. Stripping the + const-ness of Value allows you to easily + make a const iterator adaptor by supplying a const type + for Value, and allowing the defaults for the Pointer and + Reference parameters to take effect. Although compilers that don't + support partial specialization won't strip const for you, having a + const value_type is often harmless in practice. + +

[2] If your compiler does not support partial + specialization and the base iterator is a builtin pointer type, you + will not be able to use the default for Value and will have to + specify this type explicitly. + +

[3] The result type for the operator->() + depends on the category and value type of the iterator and is somewhat + complicated to describe. But be assured, it works in a stardard conforming + fashion, providing access to members of the objects pointed to by the + iterator. + +

[4] The result type of operator[]() is + value_type instead of reference as might be expected. + There are two reasons for this choice. First, the C++ standard only + requires that the return type of an arbitrary Random Access + Iterator's operator[]be ``convertible to T'' (Table 76), so + when adapting an arbitrary base iterator we may not have a reference to + return. Second, and more importantly, for certain kinds of iterators, + returning a reference could cause serious memory problems due to the + reference being bound to a temporary object whose lifetime ends inside of + the operator[]. + +

[5] + The value_type of an iterator may not be + an abstract base class, however many common uses of iterators + never need the value_type, only the reference type. + If you wish to create such an iterator adaptor, use a dummy + type such as char for the Value parameter, + and use a reference to your abstract base class for + the Reference parameter. Note that such an iterator + does not fulfill the C++ standards requirements for a + + Forward Iterator, so you will need to use a less restrictive + iterator category such as std::input_iterator_tag. + +

[6] + There is a common misconception that an iterator should have two + versions of operator* and of operator[], one + version that is a const member function and one version + that is non-const. Perhaps the source of this + misconception is that containers typically have const and + non-const versions of many of their member functions. Iterators, + however, are different. A particular iterator type can be either + mutable or constant (but not both). One can assign + to and change the object pointed to by a mutable iterator whereas a + constant iterator returns constant objects when dereferenced. Whether + the iterator object itself is const has nothing to do with + whether the iterator is mutable or constant. This is analogous to + the way built-in pointer types behave. For example, one can + modify objects pointed to by a const pointer +

+    int* const x = new int;
+    int i = 3;
+    *x = i;
+
+ but one cannot modify objects pointed to by a pointer + to const +
+    int const* x = new int;
+    int i = 3;
+    *x = i;
+
+ +
+ +

Revised + 18 Sep 2001 + + +

© Copyright Dave Abrahams and Jeremy Siek 2001. Permission to copy, + use, modify, sell and distribute this document is granted provided this + copyright notice appears in all copies. This document is provided "as is" + without express or implied warranty, and with no claim as to its + suitability for any purpose. + + + + + + + + + + + + + + +