Boost logo

Boost :

From: Anthony Williams (anthony.williamsNOSPAM_at_[hidden])
Date: 2003-03-26 10:21:15


Douglas Paul Gregor <gregod_at_[hidden]> writes:

> On 26 Mar 2003, Anthony Williams wrote:
> > It strikes me that if you dereference n iterators, you have n values, and
> > the most natural way to store them is a tuple. Doing anything other than
> > returning this tuple seems to me just complicating the usage.
>
> I would agree if tuples and argument passing were more closely linked,
> i.e., if passing a tuple to a function meant that the tuple would be
> automagically unpacked into separate arguments. But that's not the case,
> so the introduction of tuples as the return type makes it harder to then
> apply a function object to the values.

Whereas having the iterator apply a functor to the values makes it harder to
apply a function object that accepts a tuple.

> For instance, consider an iterator
> that adds the values from two other iterators (with the combining
> iterator):
>
> make_combining_iterator(first.begin(), second.begin(),
> std::plus<int>());
>
> With the tuple iterator, you need to either (a) write your own
> std::plus-like function object that accepts a tuple<int, int> instead of
> two int parameters or (b) use something like Aleksey's apply (?) function
> that unpacks a tuple and applies the separate arguments to a function
> object.

And what's so difficult in doing that? The code has to be there as part of the
current combining iterator anyway, so it's not too bad to have to split it off
to a separate functor. You could make a wrapper function that applied it as
well as the supplied functor:

make_combining_transform_iterator_with_args(first.begin(), second.begin(),
                                            std::plus<int>());

which does:

make_combining_transform_iterator(first.begin(), second.begin(),
                                  split_tuple_and_call(std::plus<int>()));

> > How do you write the following with your combining iterator? (TupleIt is a
> > supposed generalisation of my PairIt which returns a tuple rather than a
> > pair)
> [snip]
> > std::sort(makeTupleIterator(first.begin(),second.begin(),third.begin()),
> > makeTupleIterator(first.end(),second.end(),third.end()), myCompareFunc);
>
> makeTupleIterator(first.begin(), second.begin(), third.begin()) is
> equivalent to
> make_combining_iterator(first.begin(), second.begin(), third.begin(),
> &boost::tuples::make_tuple<int, int, int>);

that'd have to be make_tuple<int&,int&,int&> for sort, so that you can write
back to your vectors. However, then copying the tuple doesn't create a true
copy; it still has references to the original, and a swap along the lines of

X tmp=a;
a=b;
b=tmp;

(such as might be used as part of the implementation of std::sort) would then
not work.

The tuple returned would have to be a custom tuple type, much as the return
type of derefencing my PairIt is a custom pair type, in order to ensure that
things like std::sort work OK, so it would not be as simple as using
boost::tuples::make_tuple, but it could be done with an equivalent
functor. The beauty of having this the main interface is that such reference
vs value details can be taken care of automatically depending on the iterator
category, whereas if you try and do it retrospectively with a functor, then
you have to specify whether to use references or values, which can be error
prone --- I expect that using make_tuple<int,int,int> will compile, yet the
vectors will remain unsorted.

It appears to me that we need both, and we can have both by simply providing
helper functions either way. However, I'm inclined to think it better to
provide the tuple-based dereferencing as default, with a custom functor (and
helper function) to provide the split into function-call arguments, rather
than vice-versa.

Anthony

-- 
Anthony Williams
Senior Software Engineer, Beran Instruments Ltd.
Remove NOSPAM when replying, for timely response.

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk