diff --git a/iterator_adaptor_examples.cpp b/iterator_adaptor_examples.cpp new file mode 100644 index 0000000..aa0affb --- /dev/null +++ b/iterator_adaptor_examples.cpp @@ -0,0 +1,45 @@ +// (C) Copyright Jeremy Siek 2000. Permission to copy, use, modify, sell and +// distribute this software is granted provided this copyright notice appears +// in all copies. This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. + + +#include +#include +#include +#include + +int +main(int, char*[]) +{ + // This is a simple example of using the transform_iterators class to + // generate iterators that multiply the value returned by dereferencing + // the iterator. In this case we are multiplying by 2. + + int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + typedef std::binder1st< std::multiplies > Function; + typedef boost::transform_iterator + >::type doubling_iterator; + + doubling_iterator i(x, std::bind1st(std::multiplies(), 2)), + i_end(x + sizeof(x)/sizeof(int), std::bind1st(std::multiplies(), 2)); + + std::cout << "multiplying the array by 2:" << std::endl; + while (i != i_end) + std::cout << *i++ << " "; + std::cout << std::endl; + + // Here is an example of counting from 0 to 5 using the integer_range class. + + boost::integer_range r(0,5); + + std::cout << "counting to from 0 to 4:" << std::endl; + std::copy(r.begin(), r.end(), std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + return 0; +} + + diff --git a/iterator_adaptor_test.cpp b/iterator_adaptor_test.cpp new file mode 100644 index 0000000..9366776 --- /dev/null +++ b/iterator_adaptor_test.cpp @@ -0,0 +1,133 @@ +// Demonstrate and test boost/operators.hpp on std::iterators -------------// + +// (C) Copyright Jeremy Siek 1999. Permission to copy, use, modify, +// sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +// See http://www.boost.org for most recent version including documentation. + +// Revision History +// 13 Jun 00 Added const version of the iterator tests (Jeremy Siek) +// 12 Dec 99 Initial version with iterator operators (Jeremy Siek) + +#include + +#include +#include +#include + +struct my_iterator_tag : public std::random_access_iterator_tag { }; + + +using boost::dummyT; + +struct my_iter_traits { + typedef dummyT value_type; + typedef dummyT* pointer; + typedef dummyT& reference; + typedef my_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; +}; + +struct my_const_iter_traits { + typedef dummyT value_type; + typedef const dummyT* pointer; + typedef const dummyT& reference; + typedef my_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; +}; + +typedef boost::iterator_adaptors + My; + +struct mult_functor { + typedef int result_type; + typedef int argument_type; + // Functors used with transform_iterator must be + // DefaultConstructible, as the transform_iterator must be + // DefaultConstructible to satisfy the requirements for + // TrivialIterator. + mult_functor() { } + mult_functor(int aa) : a(aa) { } + int operator()(int b) const { return a * b; } + int a; +}; + +int +main() +{ + dummyT array[] = { dummyT(0), dummyT(1), dummyT(2), + dummyT(3), dummyT(4), dummyT(5) }; + const int N = sizeof(array)/sizeof(dummyT); + + // sanity check, if this doesn't pass the test is buggy + boost::random_access_iterator_test(array,N,array); + + // Test the iterator_adaptors + { + My::iterator i = array; + boost::random_access_iterator_test(i, N, array); + + My::const_iterator j = array; + boost::random_access_iterator_test(j, N, array); + boost::const_nonconst_iterator_test(i, ++j); + } + // Test transform_iterator + { + int x[N], y[N]; + for (int k = 0; k < N; ++k) + x[k] = k; + std::copy(x, x + N, y); + + for (int k = 0; k < N; ++k) + x[k] = x[k] * 2; + + boost::transform_iterator::type i(y, mult_functor(2)); + boost::random_access_iterator_test(i, N, x); + } + // Test indirect_iterator + { + dummyT* ptr[N]; + for (int k = 0; k < N; ++k) + ptr[k] = array + k; + + typedef dummyT* DummyPtr; + typedef boost::indirect_iterators Indirect; + Indirect::iterator i = ptr; + boost::random_access_iterator_test(i, N, array); + + Indirect::const_iterator j = ptr; + boost::random_access_iterator_test(j, N, array); + + boost::const_nonconst_iterator_test(i, ++j); + } + // Test reverse_iterators + { + dummyT reversed[N]; + std::copy(array, array + N, reversed); + std::reverse(reversed, reversed + N); + + typedef boost::reverse_iterators Reverse; + Reverse::iterator i = reversed + N; + boost::random_access_iterator_test(i, N, array); + + Reverse::const_iterator j = reversed + N; + boost::random_access_iterator_test(j, N, array); + + boost::const_nonconst_iterator_test(i, ++j); + } + + // Test integer_range's iterators + { + int int_array[] = { 0, 1, 2, 3, 4, 5 }; + boost::integer_range r(0, 5); + boost::random_access_iterator_test(r.begin(), r.size(), int_array); + } + + std::cout << "test successful " << std::endl; + + return 0; +} diff --git a/iterator_adaptors.htm b/iterator_adaptors.htm new file mode 100644 index 0000000..51c675b --- /dev/null +++ b/iterator_adaptors.htm @@ -0,0 +1,631 @@ + + + + + + +Header boost/iterator_adaptors.hpp Documentation + + + + +

c++boost.gif (8819 bytes)Header +boost/iterator_adaptors.hpp

+ +

Header boost/iterator_adaptors.hpp +

+ +

The file boost/iterator_adaptors.hpp +includes the main iterator_adaptors class and several other classes +for constructing commonly used iterator adaptors.

+ + + + + + +

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. + +

The Iterator Adaptors Class

+ +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 +operator->(). The purpose of the +iterator_adaptors class is to make it easier to implement an +iterator class, and even easier to extend and adapt existing iterator +types. The iterator_adaptors class itself is not an adaptor +class but a type generator. It generates a pair of adaptor classes, +one class for the mutable iterator and one class for the const +iterator. The definition of the iterator_adaptors class is as +follows: + +

+ +
+
+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>
+  void increment(Iterator& x) const
+    { ++x; }
+
+  template <class Iterator1, class Iterator2>
+  bool equal(Iterator1& x, Iterator2& y) const
+    { return x == y; }
+
+  // required for a BidirectionalIterator
+  template <class Iterator>
+  void decrement(Iterator& x) const
+    { --x; }
+
+  // required for a RandomAccessIterator
+  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 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())
+
+ +

The Iterator Adaptor Class

+ +This is the class used inside of the iterator_adaptors type +generator. Use this class directly (instead of using +iterator_adaptors) 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 std::set's iterators. Use the same type for +the Iterator and NonconstIterator template +arguments. + +

+ +
+
+template <class Iterator, 
+          class Policies = default_iterator_policies,
+          class NonconstIterator = Iterator,         
+          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. + +

The Transform Iterator Class

+ +It is often useful to automatically apply some function to the value +returned by dereferencing (operator*()) an iterator. The +transform_iterators class makes it easy to create an iterator +adaptor that does just that. + +First let us consider what the Policies class for the transform +iterator should look like. We are only changing one of the iterator +behaviours, so we will inherit from +default_iterator_policies. 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 +result_type of the function, so +AdaptableUnaryFunction is the corrent concept to choose for the +function object type. Now for the heart of our iterator adaptor, we +implement the dereference method, applying the function +object to *i. The type<Reference> 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 [2]. + + +

+ +
+
+  template <class AdaptableUnaryFunction>
+  struct transform_iterator_policies : public default_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;
+  };
+
+ +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 value_type and reference type of our +transform iterator will be the result_type of the function +object. The difference_type and iterator_category +will be the same as the adapted iterator. + +

+ +
+
+  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;
+  };
+
+ +The final step is to use the iterator_adaptor 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 +iterator_adaptor. 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 transform_iterators class +we use the transform_iterator_traits class defined above to +create the traits class for the new transform iterator. We then use +the iterator_adaptor class to extract the generated +iterator adaptor type. + +

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

The Indirect Iterators Class

+ +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 +operator*(). The implementation of this is similar to the +transform_iterators[3]. We first create a +policies class which does a double-dereference in the +dereference() 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 indirect_iterators, using +iterator_adaptors to do most of the work. + +

+ +
+
+  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 IndirectIterator,
+	    class IndirectTraits = std::iterator_traits<IndirectIterator>,
+	    class Traits = 
+	      std::iterator_traits<typename IndirectTraits::value_type>
+	   >
+  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 <class IndirectIterator, class ConstIndirectIterator,
+            class IndirectTraits = 
+              std::iterator_traits<IndirectIterator>,
+            class ConstIndirectTraits = 
+              std::iterator_traits<ConstIndirectIterator>,
+            class Traits =
+              std::iterator_traits<typename IndirectTraits::value_type>
+           >
+  struct indirect_iterators
+  {
+    typedef typename IndirectTraits::value_type Iterator;
+    typedef typename Traits::value_type ValueType;
+    typedef iterator_adaptors<IndirectIterator, ConstIndirectIterator,
+      indirect_traits<IndirectIterator, IndirectTraits, Traits>,
+      indirect_traits<ConstIndirectIterator, ConstIndirectTraits, Traits>,
+      indirect_iterator_policies
+      > Adaptors;
+    typedef typename Adaptors::iterator iterator;
+    typedef typename Adaptors::const_iterator const_iterator;
+  };
+
+ +

The Reverse Iterators Class

+ +

+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; }
+};
+
+ +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 transform_iterator. We can skip to +the final stage of creating a type generator class for our reverse +iterators using the iterator_adaptor class. + +

+ +
+
+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;
+};
+
+ +A typical use of the reverse_iterators class is in +user-defined container types. You can use the +reverse_iterators class to generate the reverse iterators for +your container. + +

+ +
+
+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;
+  ...
+};
+
+ + +

The Integer Range Class

+ +The iterator_adaptors 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 +operator++(), operator--(), etc. The one operator +they are lacking is the operator*(), 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. + +

+ +
+
+struct counting_iterator_policies : public default_iterator_policies
+{
+  template <class IntegerType>
+  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;
+};
+
+ +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 +integer_range. + +

+ +
+
+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;
+
+ +

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 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. + +

Implementation Notes

+ +The code is somewhat complicated because there are three iterator +adaptor class: forward_iterator_adaptor, +bidirectional_iterator_adaptor, and +random_access_iterator_adaptor. The alternative would be to +just have one iterator adaptor equivalent to the +random_access_iterator_adaptor. 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 + +RandomAccessIterator 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. + + + +
+

Revised 17 Jun 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.

+ + + +