[iterator] Problems with iterator_facade and const-qualification

I'm trying to use the iterator_facade and am running into trouble. It's not really caused by the iterator_facade, but I figured I'd ask anyway. This takes some explanation, so if anyone reads this, know that I *do* appreciate it. :-) TLDR version: I'm getting a compiler error about converting between pointers of iterator_facade<> instantiations with different const qualifications on the value_type. The code's attached, in addition to mostly appearing (with a few minor formatting changes) inline below. Please help me figure out what to do to fix it. What I'm doing is a bit roundabout, but here goes. One fault with C++ iterators (at least from some point of view) is that if you want to operate on different kinds of iterators you have to care about too much -- you need to know more than just the type that op* returns (which is all you care about most of the time, iterator_traits notwithstanding), but what the underlying iterator type is. This means that if you want to write code that operates on multiple kinds of iterators, it must be a template. Templates don't suit my needs at this time, so I'm trying to write a couple wrapper classes that will do dynamic dispatch to different iterator types. Think of this as writing a C++ version of Java's Iterator interface. Thus I define a base class for my iterators: template<typename ValueType, typename ReferenceType> class iterator_base : public iterator_facade<iterator_base<ValueType, ReferenceType>, ValueType, boost::forward_traversal_tag, ReferenceType> { friend class boost::iterator_core_access; protected: virtual void increment() = 0; virtual ReferenceType dereference() const = 0; virtual bool equal(iterator_base const & other) const = 0; public: virtual iterator_base* clone() const = 0; }; (I'm only worrying about forward iterators at this time. I'm also ignoring ptrdiff_t) The first three functions are straight from the iterator_facade class, and the fourth is your typical clone() method. Now, I define a template that wraps any iterator that doesn't implement my iterator_base; it just stores an instance of the backing iterator type and does the corresponding thing (I'm removing some 'typenames' in there to avoid line wraps): template<typename BackingIter> class cplusplus_iterator_adaptor : public iterator_base<iterator_traits<BackingIter>::value_type, iterator_traits<BackingIter>::reference> { private: BackingIter backing; typedef typename iterator_traits<BackingIter>::value_type ValueType; typedef typename iterator_traits<BackingIter>::reference Reference; public: cplusplus_iterator_adaptor() {} cplusplus_iterator_adaptor(cplusplus_iterator_adaptor const & other) : backing(other.backing) {} explicit cplusplus_iterator_adaptor(BackingIter iter) : backing(iter) {} cplusplus_iterator_adaptor* clone() const { return new cplusplus_iterator_adaptor(*this); } // Actual iterator functions void increment() { ++backing; } bool equal(iterator_base<ValueType, Reference> const& other) const { const cplusplus_iterator_adaptor * p = dynamic_cast<cplusplus_iterator_adaptor const*>(&other); return p && backing == p->backing; } Reference dereference() const { return *backing; } }; Okay, now we have something that we can use like cplusplus_iterator_adaptor<vector<int>::iterator> my_itr(vec.begin()); *my_itr; ++my_itr; and it will work the same as vector<int>::iterator, except that you can treat it polymorphically from an iterator_base<int> reference or pointer, thus freeing you from caring that it wraps a vector iterator instead of just caring that it gives you an int&. There's one problem with this however, which is that if I <i>actually</i> want to deal with it polymorphically, I have to use a reference or pointer; but (1) this kinda sucks and (2) lots of code is written that operates on iterators by value (like, say, the STL). To solve this problem, I implemented a wrapper class that gives value semantics to a iterator_base<> pointer. This wrapper class also implements the elements required of the iterator concept, and additionally behaves like a cloning smart pointer. template<typename ValueType, typename ReferenceType = ValueType &> class iterator_base_wrapper : public iterator_facade<iterator_base_wrapper<ValueType, ReferenceType>, ValueType, boost::forward_traversal_tag, ReferenceType> { private: iterator_base<ValueType, ReferenceType> * backing; public: iterator_base_wrapper() : backing(0) {} iterator_base_wrapper(iterator_base_wrapper const & other) : backing(other.backing->clone()) {} explicit iterator_base_wrapper(iterator_base const & base) : backing(base.clone()) {} private: void swap(iterator_base_wrapper & other) const { std::swap(backing, other.backing); } public: iterator_base_wrapper& operator=(iterator_base_wrapper const&other) { iterator_base_wrapper copy(other); swap(copy); return *this; } ~iterator_base_wrapper() { delete backing; } private: friend class boost::iterator_core_access; virtual void increment() { ++(*backing); } virtual bool equal(iterator_base_wrapper const & other) const { return backing->equal(*other.backing); } ReferenceType dereference() const { return **backing; } }; OK, goal achieved! Somewhat. It's now possible to use the iterator_base_wrapper like you would a normal iterator: int run() { const int arr[] = {0,1,2,3,4,5,6,7,8,9,10}; const int size_arr = sizeof(arr)/sizeof(arr[0]); vector<int> v(arr, arr+size_arr); cplusplus_iterator_adaptor<vector<int>::iterator> a_beg(v.begin()); cplusplus_iterator_adaptor<vector<int>::iterator> a_end(v.end()); iterator_base_wrapper<int> ii(a_beg); iterator_base_wrapper<int> ii_end(a_end); vector<int>::const_iterator vi = v.begin(); while(ii != ii_end) { assert( *vi == *ii ); ++vi; ++ii; } assert( vi == v.end() ); return 0; } (BY the way, I totally intend on writing a couple make_* functions, a la make_pair.) OK, but now, what happens if we change things to be const_iterators? I can still make the cplusplus_iterator_adaptor: cplusplus_iterator_adaptor<vector<int>::const_iterator> a_beg(v.begin()); But I *can't* make my wrapper, using either iterator_base_wrapper<const int, const int &> ii(a_beg); or iterator_base_wrapper<int, const int &> ii(a_begin); because of this error: cannot convert from 'iterator_base<int, const int&>*' to 'iterator_base<const int, const int&>*'. The reason for *this* is that the cplusplus_iterator_adaptor uses the iterator_traits<>::value_type of the underlying iterator -- vector's const_iterator -- which isn't const-qualified when it subclasses iterator_facade. This means that the iterator_facade base class in both cases are different types. What's the best way to fix this? The best I can come up with is to add like a clone_const() function that returns a const-qualified version of the value type. But that seems ugly. Is there something elegant I'm missing? Evan Driscoll

Evan Driscoll wrote:
I'm trying to use the iterator_facade and am running into trouble. It's not really caused by the iterator_facade, but I figured I'd ask anyway. This takes some explanation, so if anyone reads this, know that I *do* appreciate it. :-)
TLDR version: I'm getting a compiler error about converting between pointers of iterator_facade<> instantiations with different const qualifications on the value_type. The code's attached, in addition to mostly appearing (with a few minor formatting changes) inline below. Please help me figure out what to do to fix it.
What I'm doing is a bit roundabout, but here goes.
One fault with C++ iterators (at least from some point of view) is that if you want to operate on different kinds of iterators you have to care about too much -- you need to know more than just the type that op* returns (which is all you care about most of the time, iterator_traits notwithstanding), but what the underlying iterator type is. This means that if you want to write code that operates on multiple kinds of iterators, it must be a template.
You might google any_iterator, and see how other authors have addressed similar problems. Jeff

On 05/06/2011 11:59 AM, Jeff Flinn wrote:
You might google any_iterator, and see how other authors have addressed similar problems.
Great! I tried to find someone who had done something similar, figuring I couldn't have been the first, but all the search terms I tried were too generic. Thanks, Evan

On Fri, May 6, 2011 at 6:51 PM, Evan Driscoll <driscoll@cs.wisc.edu> wrote:
On 05/06/2011 11:59 AM, Jeff Flinn wrote:
You might google any_iterator, and see how other authors have addressed similar problems.
Great! I tried to find someone who had done something similar, figuring I couldn't have been the first, but all the search terms I tried were too generic.
Thanks, Evan
Evan, For the Boost 1.46 release I added two related features to Boost.Range. The two related features are 'any_range' and the Range Adaptor 'type_erased'. These implementations take the any_iterators that you will find commonly on the web and improve their performance by incorporating the 'small buffer optimization' which radically reduces the allocation overhead and improves the locality of data. Hence the performance overhead of this particular implementation is considerably less than others that I have tried. I strongly recommend using the any_range solution rather than redeveloping any_iterators. The relevant reference documentation: http://www.boost.org/doc/libs/1_46_1/libs/range/doc/html/range/reference/ran... http://www.boost.org/doc/libs/1_46_1/libs/range/doc/html/range/reference/ada... I hope this saves you some time. Regards, Neil Groves
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

On Fri, May 6, 2011 at 1:51 PM, Evan Driscoll <driscoll@cs.wisc.edu> wrote:
On 05/06/2011 11:59 AM, Jeff Flinn wrote:
You might google any_iterator, and see how other authors have addressed similar problems.
Great! I tried to find someone who had done something similar, figuring I couldn't have been the first, but all the search terms I tried were too generic.
Thanks, Evan
You can find one in the ASL that's built on iterator_facade. http://stlab.adobe.com/classadobe_1_1any__iterator.html HTH, -- Dave Abrahams BoostPro Computing http://www.boostpro.com
participants (4)
-
Dave Abrahams
-
Evan Driscoll
-
Jeff Flinn
-
Neil Groves