Topic: proposal and implementation to be able to adapt c++0x iterators to existing ones.


Author: german diago <germandiago@gmail.com>
Date: Tue, 6 Oct 2009 12:57:54 CST
Raw View
Hello. I commented in the last c++0x post that it would be a good idea
to
be able to retroactively adapt existing iterators to be STL
conformant.

The problem: you have to stick to operator++ * and so on to be able to
use iterators as STL ones.
It would be great if you could use your own iterators with stl ones
adapting them retroactively. This is going to be proposed for
allocators as well.

In allocator_traits, besides the traits for pointers, value_type, etc,
you can define some default functions.
The STL will call, for example allocator_traits<MyAlloc>::allocate
(alloc, ..) instead of alloc.allocate(),
so that an existing allocator class can be adapted.

The proposal is the same for STL iterators. I prepared a little
(incomplete, but working) specification.
The proposal is the following:

In the iterator_traits template, add members for dereferencing,
incrementing, decrementing, equality comparison, less than comparison
(if it's a random_access_iterator).
The following code works, but obviously will have lots of things I
didn't considered.

There are some traits classes to identify if an iterator meets one of
the five categories, to be able
to use SFINAE in iterator traits so that every iterator_category has
just the corresponding operations.
The defaults are given for every operation (it's incomplete). The
operations are called with names: dereference, increment, etc. just as
in boost.iterator library.

So the generic algorithms should use, for example
allocator_traits<iter>::dereference(it) instead of
the more expressive *it. The calls must be indirect in order to
support adaptation.

The (incomplete) implementation of the idea is here. Compiled under g+
+-4.4.1. It includes:
iterator_traits for the general case. iterator_traits for pointer
types. An iterator made by me and retroactively adapted and an
implementation of the copy std algorithm.
The class iterator_traits has been implemented with the _new suffix so
that it doesn't conflict with
std::copy and the same for allocator_traits. Here it is:


#include <type_traits>
#include <iterator>
#include <array>
#include <iostream>
#include <vector>



//Iterator to adapt to an stl-like one
template <class T>
class MyVecIter {
public:
     T * elem_;

     MyVecIter(T * elem) : elem_(elem) {}

     void next()    { ++elem_; }
     T & get() { return *elem_; }

     bool equals(const MyVecIter & other) const { return this->elem_ ==
other.elem_; }
};


namespace std {


template <class T>
struct is_input_iterator_category : false_type {};

template <class T>
struct is_forward_iterator_category : false_type {};

template <class T>
struct is_bidirectional_iterator_category : false_type {};

template <class T>
struct is_random_access_iterator_category : false_type {};


template <>
struct
is_random_access_iterator_category<random_access_iterator_tag> :
true_type {};

template <>
struct
is_bidirectional_iterator_category<random_access_iterator_tag> :
true_type {};

template <>
struct
is_bidirectional_iterator_category<bidirectional_iterator_tag> :
true_type {};


template <>
struct is_forward_iterator_category<random_access_iterator_tag> :
true_type {};

template <>
struct is_forward_iterator_category<bidirectional_iterator_tag> :
true_type {};

template <>
struct is_forward_iterator_category<forward_iterator_tag> : true_type
{};


template <>
struct is_input_iterator_category<random_access_iterator_tag> :
true_type {};

template <>
struct is_input_iterator_category<bidirectional_iterator_tag> :
true_type {};

template <>
struct is_input_iterator_category<forward_iterator_tag> : true_type
{};

template <>
struct is_input_iterator_category<input_iterator_tag> : true_type {};


/*Iterator traits enabled for retroactive adaptation*/
template <class Iter>
struct iterator_traits_new {
     typedef typename Iter::value_type            value_type;
     typedef typename Iter::iterator_category     iterator_category;
     typedef typename Iter::difference_type       difference_type;
     typedef typename Iter::pointer               pointer;
     typedef typename Iter::reference             reference;


     static reference dereference(Iter & iter) { return *iter; }
     static bool      equal(const Iter & lhs, const Iter & rhs)
{ return lhs == rhs; }
     static Iter &    increment(Iter && iter) { return ++iter; }

     //Just if is_bidirectional_iterator_category
     //static Iter & decrement(Iter && iter) { return --iter; }

       //Not sure about these. I think they're not needed.
//    static void advance(Iter && iter, int n) { std::advance(iter,
n); }
//    static int distance_to(Iter && lhs, Iter && rhs) { return
std::distance(lhs, rhs); }

     //Incomplete set of operations. Must implement all of
random_access with SFINAE

};



template <class T>
struct iterator_traits_new<T*> {
     typedef random_access_iterator_tag iterator_category;
     typedef T                          value_type;
     typedef ptrdiff_t                  difference_type;
     typedef T*                         pointer;
     typedef T&                         reference;


     static reference dereference(T* iter) { return *iter; }
     static bool      equal(const T* lhs, const T* rhs) { return lhs ==
rhs; }

     static T * & increment(T * && iter) { return ++iter; }
     static T * & decrement(T * && iter) { return --iter; }


//    static void advance(T * iter, int n) { std::advance(iter, n); }
//    static int distance_to(T * lhs, T * rhs) { return std::distance
(lhs, rhs); }
};



template <class T>
struct iterator_traits_new<MyVecIter<T>> {
     typedef forward_iterator_tag iterator_category;
     typedef T                          value_type;
     typedef ptrdiff_t                  difference_type;
     typedef T*                         pointer;
     typedef T&                         reference;

     static reference dereference(MyVecIter<T> & iter) { return iter.get
(); }
     static bool      equal(const MyVecIter<T> & lhs,
                            const MyVecIter<T> & rhs) { lhs.equals
(rhs); }

     static MyVecIter<T> & increment(MyVecIter<T> && iter) { iter.next
(); return iter; }
};


template <class FwdIter, class FwdIter2>
void copy_new(FwdIter && itbeg, FwdIter && itend, FwdIter2 &&
itbegcopy)
{
     typedef std::iterator_traits_new<typename
std::remove_reference<FwdIter>::type> tr;
     typedef std::iterator_traits_new<typename
std::remove_reference<FwdIter2>::type> tr2;


     while (!tr::equal(itbeg,itend)) {
         tr2::dereference(itbegcopy) = tr::dereference(itbeg);
         tr::increment(itbeg);
         tr2::increment(itbegcopy);
     }

}

}


using namespace std;


int main(int argc, char * argv[])
{
     int arr[10] = {3, 4, 5, 1, 2, 7};
     std::vector<int> arr2(10);
     copy_new(&arr[0], &arr[0] + 6, arr2.begin());
     MyVecIter<int> veciter(arr2.data()), veciter2(arr2.data() +
arr2.size());

     std::copy_new(veciter, veciter2, &arr[0]);


}

Your opinions are welcome. Thanks.



--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]