[Iterator] Zip iterator for back_insert_iterators?

I just tried to make a zip iterator out of two vector back_insert_iterators and got a very curious compile failure: /usr/include/boost/iterator/iterator_facade.hpp:653: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:653: error: in declaration 'typename boost::detail::operator_brackets_result<Derived, Value, Reference>::type boost::iterator_facade<I, V, TC, R, D>::operator[](Difference) const' /usr/include/boost/iterator/iterator_facade.hpp:693: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:693: error: in declaration 'Derived& boost::iterator_facade<I, V, TC, R, D>::operator+=(Difference)' /usr/include/boost/iterator/iterator_facade.hpp:699: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:699: error: in declaration 'Derived& boost::iterator_facade<I, V, TC, R, D>::operator-=(Difference)' /usr/include/boost/iterator/iterator_facade.hpp:705: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:705: error: in declaration 'Derived boost::iterator_facade<I, V, TC, R, D>::operator-(Difference) const' It seems that even though back_insert_iterator has an output_iterator tag, iterator_facade still trying to define random access operators. If assignment and incrementing are the only valid operations on an iterator (as is the case with output iterators), then doesn't it make sense to support a difference_type of void? Is this a curiosity of my stdlib implementation? A Boost.Iterator library oversight? A Boost.Iterator conscious design decision? Or a loose nut behind the keyboard trying something he shouldn't? I'm using version 1.35.0 on Ubuntu 8.04 x86_64 with g++ version 4.2.3. Here's the code I used to get this error: #include <iterator> #include <iostream> #include <boost/iterator/zip_iterator.hpp> #include <vector> #include <algorithm> using namespace std; using namespace boost; int main() { vector<int> ha1; vector<double> ha2; make_zip_iterator( make_tuple( back_inserter(ha1), back_inserter(ha2) ) ); return 0; } Thanks, Kris

on Tue Sep 09 2008, "Kris Rousey" <krousey-AT-gmail.com> wrote:
I just tried to make a zip iterator out of two vector back_insert_iterators and got a very curious compile failure:
/usr/include/boost/iterator/iterator_facade.hpp:653: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:653: error: in declaration 'typename boost::detail::operator_brackets_result<Derived, Value, Reference>::type boost::iterator_facade<I, V, TC, R, D>::operator[](Difference) const' /usr/include/boost/iterator/iterator_facade.hpp:693: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:693: error: in declaration 'Derived& boost::iterator_facade<I, V, TC, R, D>::operator+=(Difference)' /usr/include/boost/iterator/iterator_facade.hpp:699: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:699: error: in declaration 'Derived& boost::iterator_facade<I, V, TC, R, D>::operator-=(Difference)' /usr/include/boost/iterator/iterator_facade.hpp:705: error: invalid parameter type 'void' /usr/include/boost/iterator/iterator_facade.hpp:705: error: in declaration 'Derived boost::iterator_facade<I, V, TC, R, D>::operator-(Difference) const'
It seems that even though back_insert_iterator has an output_iterator tag, iterator_facade still trying to define random access operators.
That's not exactly true; the operator[] is only supposed to be instantiated if used, but we never considered that someone would try to use Difference=void.
If assignment and incrementing are the only valid operations on an iterator (as is the case with output iterators), then doesn't it make sense to support a difference_type of void?
I guess. It couldn't hurt, though, to leave the Difference parameter as std::ptrdiff_t. If you really want std::iterator_traits<I>::difference_type to be void, you'd normally just typedef void difference_type; inside your derived iterator body. However, you're using zip_iterator.
Is this a curiosity of my stdlib implementation? A Boost.Iterator library oversight?
Probably the latter.
A Boost.Iterator conscious design decision? Or a loose nut behind the keyboard trying something he shouldn't?
Heh, maybe both. We didn't test the library too well on output iterators. Try this patch to boost/iterator/iterator_facade.hpp; does it fix the problem for you? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

2008/9/9 David Abrahams <dave@boostpro.com>:
Try this patch to boost/iterator/iterator_facade.hpp; does it fix the problem for you?
Yes. Well... It fixed THAT problem. After I took it a step further and tried to use the iterator like this: *iter = make_tuple(i, i*2.0); I then get this error: boost/tuple/detail/tuple_basic.hpp:377: error: no match for 'operator=' in '((boost::tuples::cons<void, boost::tuples::cons<void, boost::tuples::null_type> >*)this)->boost::tuples::cons<void, boost::tuples::cons<void, boost::tuples::null_type> >::head = u->boost::tuples::cons<int, boost::tuples::cons<double, boost::tuples::null_type> >::head' After looking into it further, it seems that std::back_insert_iterator inherits from std::iterator<std::output_iterator_tag, void, void, void, void>. Since it obviously has no interest in populating the right type information, would it make sense add partial specializations to boost/detail/iterator.hpp to grab the appropriate types from the container? e.g. template <class CONTAINER> struct iterator_traits<std::back_insert_iterator<CONTAINER> > { typedef typename CONTAINER::value_type value_type; typedef typename CONTAINER::reference reference; typedef typename CONTAINER::pointer pointer; typedef typename CONTAINER::difference_type difference_type; typedef std::output_iterator_tag iterator_category; }; I'll try this out and see if it works, but what is this the right way to go about it? I'm fairly new to C++ and boost development, so I'm looking to learn the correct ways. Thanks, Kris

on Tue Sep 09 2008, "Kris Rousey" <krousey-AT-gmail.com> wrote:
2008/9/9 David Abrahams <dave@boostpro.com>:
Try this patch to boost/iterator/iterator_facade.hpp; does it fix the problem for you?
Yes. Well... It fixed THAT problem. After I took it a step further and tried to use the iterator like this:
*iter = make_tuple(i, i*2.0);
I then get this error:
boost/tuple/detail/tuple_basic.hpp:377: error: no match for 'operator=' in '((boost::tuples::cons<void, boost::tuples::cons<void, boost::tuples::null_type> >*)this)->boost::tuples::cons<void, boost::tuples::cons<void, boost::tuples::null_type> >::head = u->boost::tuples::cons<int, boost::tuples::cons<double, boost::tuples::null_type> >::head'
After looking into it further, it seems that std::back_insert_iterator inherits from std::iterator<std::output_iterator_tag, void, void, void, void>. Since it obviously has no interest in populating the right type information, would it make sense add partial specializations to boost/detail/iterator.hpp to grab the appropriate types from the container? e.g.
template <class CONTAINER> struct iterator_traits<std::back_insert_iterator<CONTAINER> > { typedef typename CONTAINER::value_type value_type; typedef typename CONTAINER::reference reference; typedef typename CONTAINER::pointer pointer; typedef typename CONTAINER::difference_type difference_type; typedef std::output_iterator_tag iterator_category; };
Well, that's a very clever idea. It subverts the purpose of boost/detail/iterator.hpp which was intended to be not much more than a wrapper for std::iterator_traits to work around compiler/library implementation deficiencies, but something like that just might work. That said, the reference type is definitely wrong in your specialization; it should be the same as the type returned by dereferencing the back_insert_iterator (I think that might be the back_insert_iterator type itself, IIRC).
I'll try this out and see if it works, but what is this the right way to go about it?
I'm fairly new to C++ and boost development, so I'm looking to learn the correct ways.
Well, the *really* right way to go about it is to write a fix that's not specific to back_insert_iterator, but that works for all kinds of output iterators that are not also forward iterators. That would take a little thought to get right, but it would be a good project for you to take on. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Well, that's a very clever idea. It subverts the purpose of boost/detail/iterator.hpp which was intended to be not much more than a wrapper for std::iterator_traits to work around compiler/library implementation deficiencies, but something like that just might work. That said, the reference type is definitely wrong in your specialization; it should be the same as the type returned by dereferencing the back_insert_iterator (I think that might be the back_insert_iterator type itself, IIRC).
-- snip --
Well, the *really* right way to go about it is to write a fix that's not specific to back_insert_iterator, but that works for all kinds of output iterators that are not also forward iterators. That would take a little thought to get right, but it would be a good project for you to take on.
Thanks for the catching the reference error. I did manage to get this working, but only for back_insert_iterators. I did run into a few troubles in the iterator_facade.hpp file. It seems that back_insert_iterator doesn't offer a const dereference operator member, and this caused issues in the dereference_iterator functor. Changing it to cast away the const of the iterator made everything work as expected. I don't like this solution, as it potentially breaks a const contract for so many other use-cases. I went back and changed it to only cast away the const if it had the output_iterator_tag. This is slightly better in my opinion as I don't see much use in a const output iterator. It still doesn't sit well with me. Maybe after I'm less distracted by my day job, I can figure out a better approach. I'm open to suggestions :) . I'll also look into doing this generically, though I believe I'll just get into enumerating the major output iterators (e.g. container inserter, stream inserter, ...). Here's the modified dereference_iterator functor. Feel free to rip into it: struct dereference_iterator { template<typename Iterator> struct apply { typedef typename iterator_traits<Iterator>::reference type; }; template<typename Iterator> typename apply<Iterator>::type operator()(Iterator const& it) { typename iterator_traits<Iterator>::iterator_category category; return dereference_dispatch(it, category); } template<typename Iterator, typename IteratorCategory> typename apply<Iterator>::type dereference_dispatch(Iterator const& it, IteratorCategory) { return *it; } template<typename Iterator> typename apply<Iterator>::type dereference_dispatch(Iterator const& it, std::output_iterator_tag) { return *const_cast<Iterator&>(it); } }; Cheers, Kris
participants (2)
-
David Abrahams
-
Kris Rousey