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 policies 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.
Jeremy Siek contributed the transform iterator adaptor, the integer-only version of counting_iterator_generator, 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.

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();
    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;
    operator_arrow_result_type operator->() const; [2]
    value_type operator[](difference_type n) const;

    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 !=, <, <=, >=, >

Example

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

The main task in using iterator_adaptor is creating an appropriate Policies class. The Policies class that you pass in will become the heart of the iterator adaptor, supplying the core iterator operations that will determine how your new adaptor class will behave. The core iterator operations are:

The Policies class must implement three, four, or seven of the core iterator operations depending on whether you wish the new iterator adaptor class to be a ForwardIterator, BidirectionalIterator, or RandomAccessIterator. The iterator_category type of the traits class you pass in must match the category of iterator that you want to create. The default policy class, default_iterator_policies, 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 default_iterator_policies to avoid retyping the usual behaviors. You should also look at default_iterator_policies as the ``boiler-plate'' for your own policy classes. The following is the definition of the default_iterator_policies class:

struct default_iterator_policies
{
  // required for a ForwardIterator
  template <class Reference, class Iterator>
  Reference dereference(type<Reference>, const Iterator& x) const
    { return *x; }

  template <class Iterator>
  static void increment(Iterator& x)
    { ++x; }

  template <class Iterator1, class Iterator2>
  bool equal(Iterator1& x, Iterator2& y) const
    { return x == y; }

  // required for a BidirectionalIterator
  template <class Iterator>
  static void decrement(Iterator& x)
    { --x; }

  // required for a RandomAccessIterator
  template <class Iterator, class DifferenceType>
  static void advance(Iterator& x, DifferenceType n)
    { x += n; }

  template <class Difference, class Iterator1, class Iterator2>
  Difference distance(type<Difference>, Iterator1& x, Iterator2& y) const
    { return y - x; }

  template <class Iterator1, class Iterator2>
  bool less(Iterator1& x, Iterator2& y) const
    { return x < y; }
};
To implement a transform iterator we will only change one of the base iterator's behaviors, so the transform_iterator_policies class will inherit the rest from default_iterator_policies. In addition, we will need a function object to apply, so the policies class will have a template parameter for the function object and it will have a data member of that type. The function will take one argument (the value type of the base iterator) and we will need to know the result_type of the function, so AdaptableUnaryFunction is the correct concept to choose for the function object type. Inside of transform_iterator_policies we will implement the dereference() member function. This member function will dereference the base iterator (the second parameter of dereference()) and apply the function object. The type<Reference> class used below is there to convey the reference type of the iterator, which is handy when writing generic iterator adaptors such as this one. The following is the complete code for the transform_iterator_policies class.

  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 class to construct the transform iterator type. The nicest way to package up the construction of the transform iterator is to create a type generator. The first template parameter of the generator will be the type of the function object and the second will be the base iterator type. Inside the transform_iterator_generator class we use the iterator_adaptor class to create the transform iterator type.

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);
}

The following is an example of how to use a transform iterator to iterate through a range of numbers, multiplying each of them by 2 when they are dereferenced 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

Template Parameters

Parameter Description
Base The type being wrapped.
Value The value_type of the resulting iterator, unless const. If const, a conforming compiler strips constness for the value_type. Typically the default for this parameter is the appropriate type[1].
Default: std::iterator_traits<BaseIterator>::value_type
Pointer The pointer type of the resulting iterator, and in particular, the result type of operator->(). Typically the default for this parameter is the appropriate type.
Default: If Value was supplied, then Value*, otherwise std::iterator_traits<BaseIterator>::pointer.
Reference The reference type of the resulting iterator, and in particular, the result type of operator*(). Typically the default for this parameter is the appropriate type.
Default: If Value is supplied, Value& is used. Otherwise std::iterator_traits<BaseIterator>::reference is used.
Category The iterator_category type for the resulting iterator. Typically the default for this parameter is the appropriate type.
Default: std::iterator_traits<BaseIterator>::iterator_category
Distance The difference_type for the resulting iterator. Typically the default for this parameter is the appropriate type.
Default: std::iterator_traits<BaseIterator>::difference_type

Model of

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

Members

In addition to all of the member functions required of a Random Access Iterator, the iterator_adaptor class template defines the following members.

iterator_adaptor(const Base&, const Policies& = Policies())
Construct an iterator adaptor from a base object and a policies object.
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 mutable to constant iterator adaptors. This assumes that the type B is convertible to the type Base.
base_type base() const;

Return a copy of the base object.

Operators

The iterator_adaptor class defines all of the operators required of a Random Access Iterator. Also, the operators of the iterator_adaptor class that take two iterator adaptor arguments (difference and comparisons) are defined in such a way that the operators work on pairs of iterator_adaptor types that have different template parameters, provided that the base types of the iterator adaptors are interoperable. The reason for this is so that mutable and constant versions of an iterator adaptor can be created that will be interoperable with eachother.

Challenge

There is an unlimited number of ways the 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.

Notes

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

[2] 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.

Implemenation Notes


Revised 10 Feb 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.