The file boost/iterator_adaptors.hpp includes the main iterator_adaptors class and several other classes for constructing commonly used iterator adaptors.
The file boost/integer_range.hpp includes a class that uses iterator adaptors to create an iterator that increments over a range of integers. The file also includes a "container" type that creates a container-interface for the range of integers.
Dave
Abrahams 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 indirect_iterators and
reverse_iterators classes.
Jeremy Siek
contributed transform_iterator, integer_range,
and this documentation.
John Potter
contributed indirect_iterator and projection_iterator
and made some simplifications to iterator_adaptor.
template <class Iterator, class ConstIterator, class Traits = std::iterator_traits<Iterator>, class ConstTraits = std::iterator_traits<ConstIterator>, class Policies = default_iterator_policies> struct iterator_adaptors { typedef ... iterator; typedef ... const_iterator; }; |
The Iterator and ConstIterator template parameters are the iterator types that you want to adapt. The Traits and ConstTraits must be iterator traits classes. The traits parameters default to the specialization of the std::iterator_traits class for the adapted iterators. If you want the traits for your new iterator adaptor (value_type, iterator_category, etc.) to be the same as the adapted iterator then use the default, otherwise create your own traits classes and pass them in [1].
The Policies 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 Policies 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 ForwardIterator, BidirectionalIterator, or RandomAccessIterator. Make sure that the iterator_category type of the traits class you pass in matches 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 behaviours. You should also look at default_iterator_policies as the "boiler-plate" for your own policy classes. The following is 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; } }; |
The generated iterator adaptor types will have the following constructors.
iterator(const Iterator& i, const Policies& p = Policies()) const_iterator(const ConstIterator& i, const Policies& p = Policies()) |
template <class Iterator, class Policies = default_iterator_policies, class Traits = std::iterator_traits<Iterator> > struct iterator_adaptor; |
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.
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 Iterator> Reference dereference(type<Reference>, const Iterator& i) const { return m_f(*i); } AdaptableUnaryFunction m_f; }; |
template <class AdaptableUnaryFunction, class IteratorTraits> 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; }; |
template <class AdaptableUnaryFunction, class Iterator, class Traits = std::iterator_traits<Iterator> > struct transform_iterator { typedef transform_iterator_traits<AdaptableUnaryFunction,Traits> TransTraits; typedef iterator_adaptor<Iterator, TransTraits, transform_iterator_policies<AdaptableUnaryFunction> >::type type; }; |
The following is a simple example of how to use the transform_iterators class to iterate through a range of numbers, multiplying each of them by 2 when they are dereferenced.
#include <functional> #include <iostream> #include <boost/iterator_adaptors.hpp> int main(int, char*[]) { int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; typedef std::binder1st< std::multiplies<int> > Function; typedef boost::transform_iterator<Function, int*, boost::iterator<std::random_access_iterator_tag, int> >::type doubling_iterator; doubling_iterator i(x, std::bind1st(std::multiplies<int>(), 2)), i_end(x + sizeof(x)/sizeof(int), std::bind1st(std::multiplies<int>(), 2)); std::cout << "multiplying the array by 2:" << std::endl; while (i != i_end) std::cout << *i++ << " "; std::cout << std::endl; return 0; } |
struct indirect_iterator_policies : public default_iterator_policies { template <class Reference, class Iterator> Reference dereference(type<Reference>, const Iterator& x) const { return **x; } }; |
template <class OuterIterator, class InnerIterator, class OuterTraits = std::iterator_traits<OuterIterator>, class InnerTraits = std::iterator_traits<InnerIterator> > struct indirect_traits { typedef typename OuterTraits::difference_type difference_type; typedef typename InnerTraits::value_type value_type; typedef typename InnerTraits::pointer pointer; typedef typename InnerTraits::reference reference; typedef typename OuterTraits::iterator_category iterator_category; }; |
template <class OuterIterator, class InnerIterator, class OuterTraits = std::iterator_traits<OuterIterator>, class InnerTraits = std::iterator_traits<InnerIterator> > struct indirect_iterator { typedef iterator_adaptor<OuterIterator, indirect_iterator_policies, indirect_traits<OuterIterator, InnerIterator, OuterTraits, InnerTraits> > type; }; template <class OuterIterator, // Mutable or Immutable, does not matter class InnerIterator, // Mutable class ConstInnerIterator, // Immutable class OuterTraits = std::iterator_traits<OuterIterator>, class InnerTraits = std::iterator_traits<InnerIterator>, class ConstInnerTraits = std::iterator_traits<ConstInnerIterator> > struct indirect_iterators { typedef iterator_adaptors<OuterIterator, OuterIterator, indirect_traits<OuterIterator, InnerIterator, OuterTraits, InnerTraits>, indirect_traits<OuterIterator, ConstInnerIterator, OuterTraits, ConstInnerTraits>, indirect_iterator_policies > Adaptors; typedef typename Adaptors::iterator iterator; typedef typename Adaptors::const_iterator const_iterator; }; |
template <class Pair> struct select1st_ : public std::unary_function<Pair, typename Pair::first_type> { const typename Pair::first_type& operator()(const Pair& x) const { return x.first; } typename Pair::first_type& operator()(Pair& x) const { return x.first; } }; |
template <class AdaptableUnaryFunction> struct projection_iterator_policies : public default_iterator_policies { projection_iterator_policies() { } projection_iterator_policies(const AdaptableUnaryFunction& f) : m_f(f) { } template <class Reference, class Iterator> Reference dereference (type<Reference>, Iterator const& iter) const { return m_f(*iter); } AdaptableUnaryFunction m_f; }; |
template <class AdaptableUnaryFunction, class Traits> struct projection_iterator_traits { typedef typename AdaptableUnaryFunction::result_type value_type; typedef value_type& reference; typedef value_type* pointer; typedef typename Traits::difference_type difference_type; typedef typename Traits::iterator_category iterator_category; }; template <class AdaptableUnaryFunction, class Traits> struct const_projection_iterator_traits { typedef typename AdaptableUnaryFunction::result_type value_type; typedef value_type const& reference; typedef value_type const* pointer; typedef typename Traits::difference_type difference_type; typedef typename Traits::iterator_category iterator_category; }; |
template <class AdaptableUnaryFunction, class Iterator, class Traits = std::iterator_traits<Iterator> > struct projection_iterator { typedef projection_iterator_traits<AdaptableUnaryFunction, Traits> Projection_Traits; typedef iterator_adaptor<Iterator, projection_iterator_policies<AdaptableUnaryFunction>, Projection_Traits> type; }; template <class AdaptableUnaryFunction, class Iterator, class Traits = std::iterator_traits<Iterator> > struct const_projection_iterator { typedef const_projection_iterator_traits<AdaptableUnaryFunction, Traits> Projection_Traits; typedef iterator_adaptor<Iterator, projection_iterator_policies<AdaptableUnaryFunction>, Projection_Traits> type; }; template <class AdaptableUnaryFunction, class Iterator, class ConstIterator, class Traits = std::iterator_traits<Iterator>, class ConstTraits = std::iterator_traits<ConstIterator> > struct projection_iterators { typedef projection_iterator_traits<AdaptableUnaryFunction, Traits> Projection_Traits; typedef const_projection_iterator_traits<AdaptableUnaryFunction, ConstTraits> Const_Projection_Traits; typedef iterator_adaptors<Iterator, ConstIterator, Projection_Traits, Const_Projection_Traits, projection_iterator_policies<AdaptableUnaryFunction> > Adaptors; typedef typename Adaptors::iterator iterator; typedef typename Adaptors::const_iterator const_iterator; }; |
Yes, there is already a reverse_iterator adaptor class defined in the C++ Standard, but using the iterator_adaptors class we can re-implement this classic adaptor in a more succinct and elegant fashion. Also, this makes for a good example of using iterator_adaptors that is in familiar territory.
The first step is to create the Policies class. As in the std::reverse_iterator class, we need to flip all the operations of the iterator. Increment will become decrement, advancing by n will become retreating by n, etc.
struct reverse_iterator_policies { template <class Reference, class Iterator> Reference dereference(type<Reference>, const Iterator& x) const { return *boost::prior(x); } // this is equivalent to { Iterator tmp = x; return *--tmp; } template <class Iterator> void increment(Iterator& x) const { --x; } template <class Iterator> void decrement(Iterator& x) const { ++x; } template <class Iterator, class DifferenceType> void advance(Iterator& x, DifferenceType n) const { x -= n; } template <class Difference, class Iterator1, class Iterator2> Difference distance(type<Difference>, Iterator1& x, Iterator2& y) const { return x - y; } template <class Iterator1, class Iterator2> bool equal(Iterator1& x, Iterator2& y) const { return x == y; } template <class Iterator1, class Iterator2> bool less(Iterator1& x, Iterator2& y) const { return y < x; } }; |
template <class Iterator, class ConstIterator, class Traits = std::iterator_traits<Iterator>, class ConstTraits = std::iterator_traits<ConstIterator> > struct reverse_iterators { typedef iterator_adaptors<Iterator,ConstIterator,Traits,ConstTraits, reverse_iterator_policies> Adaptor; typedef typename Adaptor::iterator iterator; typedef typename Adaptor::const_iterator const_iterator; }; |
class my_container { ... typedef ... iterator; typedef ... const_iterator; typedef reverse_iterators<iterator, const_iterator> RevIters; typedef typename RevIters::iterator reverse_iterator; typedef typename RevIters::const_iterator const_reverse_iterator; ... }; |
template <class IntegerType> struct counting_iterator_policies : public default_iterator_policies { IntegerType dereference(type<IntegerType>, const IntegerType& i) const { return i; } }; template <class IntegerType> 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; }; |
template <class IntegerType> struct integer_range { typedef typename iterator_adaptor<IntegerType, counting_iterator_traits<IntegerType>, counting_iterator_policies >::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; }; |
The following is an example of how to use the integer_range class to count from 0 to 4.
boost::integer_range<int> r(0,5); cout << "counting to from 0 to 4:" << endl; std::copy(r.begin(), r.end(), ostream_iterator<int>(cout, " ")); cout << endl; |
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.
[1] If your compiler does not support partial specialization and hence does not have a working std::iterator_traits class, you will not be able to use the defaults and will need to supply your own Traits and ConstTraits classes.
[2] The reference type could also be obtained from std::iterator_traits, but that is not portable on compilers that do not support partial specialization.
[3] It would have been more elegant to implement indirect_iterators using transform_iterators, but for subtle reasons that would require the use of boost::remove_cv which is not portable.
Revised 27 Nov 2000
© 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 "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.