Boost logo

Boost :

From: Anthony Williams (anthony.williamsNOSPAM_at_[hidden])
Date: 2003-03-28 03:57:20


Thomas Becker <tmbecker1_at_[hidden]> writes:
> --- Anthony Williams
> <anthony.williamsNOSPAM_at_[hidden]> wrote:
> > OTOH, the situations I am thinking of require that
> > the tuple of containers is
> > really treated as a container of tuples, complete
> > with the ability to update
> > the source containers by writing to the tuple
> > obtained by the dereferencing,
> > but maintaining the value semantics when copied.
> > This is really hard to do in
> > any other way than by having *it return a "magic
> > tuple", IMHO, though if you
> > can find a way of doing it as a functor that "just
> > works" without the user
> > having to know too much, I'll concede the point.
>
> Because of my tilted perception, I sort of dismissed
> that scenario by saying to myself, ah, shucks, just
> write a functional that returns a suitable reference,
> and you're good. You are now forcing me to think about
> this more carefully, for which I am grateful to you.
> Unless I'm missing something, I believe that my
> original rough intuition was ok. Given my combining
> iterator, it seems very easy to write your tuple
> iterator by providing a simple generic
> "reference-tuple-making" functional. As always in
> these situations, it's a little messy to provide one
> functional that will work for all n (n=number of
> member iterators), so for simplicity, here's the case
> n=2:
>
> template<typename Type1_, typename Type2_>
> class FunMakeReferenceTuple_2
> {
> public:
> typedef boost::tuple<Type1_&, Type2_&> result_type;
> boost::tuple<Type1_&, Type2_&> operator()(
> Type1_& refFirst,
> Type2_& refSecond
> ) const
> {
> return boost::tuple<Type1_&, Type2_&>(
> refFirst,
> refSecond
> );
> }
> };
>
> I've tested this in several examples, e.g., I created
> two vectors of ints, filled them with values to equal
> length, then created a combining_iterator from the two
> begin positions and the functional
>
> FunMakeReferenceTuple_2<int, int>
>
> Once I have this combining iterator, I can read and
> write both vector elements to my hearts delight. E.g.,
>
> std::swap(it->get<0>(), it->get<1>());
>
> will swap the values at the current position, or,
>
> *it = boost::make_tuple(42, 43);
>
> will assign 42 and 43 to the respective vector
> elements.

The returned tuple of references is not copyable though --- the copies will
still refer to the originals. I went through all this with my pair iterator;
try using std::sort on your pair of vectors, especially with vectors of a
non-POD UDT with no swap specialization, just to avoid any optimizations in
your standard library. A plain tuple-of-references will screw up the vectors,
you need something more complex.

Also, you can't use a tuple-of-references with input iterators, since they
return values not references when dereferenced, so you need to know the
iterator category and use a tuple of values in this case.

Finally, output iterators are even more bizarre, as writing to the result of
dereferencing your combined output iterator needs to write to the result of
dereferencing each of the individual output iterators, and there is no telling
what type you get when you dereference an output iterator.
 
> If you want to, you can easily wrap all this up and
> provide a tuple_iterator which internally uses my
> combining iterator in conjunction with the
> reference-tuple-making functional. I now strongly
> believe that that's the way to go. The combining
> iterator handles for you everything that has to do
> with the parallel-iteration. Via the funcional, you
> decide how to process the dereferenced iterators. The
> tuple_iterator is the special case (or rather, the
> large subclass of cases) where that processing
> consists of making a tuple of references. There's
> certainly more to think about, like, what about const
> tuple iterators, but I believe that the questions and
> answers are the same whether you write the tuple
> iterator from scratch or use my combining iterator. In
> fact, doing it with the combining iterator seems
> somehow "intellectually cleaner" to me, because all
> the parallel iteration stuff is hidden and out of the
> way, so you can focus on the tuple issues.

I agree that separating the parallel iteration from everything else is a good
idea.

> BTW, in my software, I do have cases where I need to
> parallel iterate and write to the current positions,
> but in those cases, I always deal with sequences of
> possibly different length (think portfolio of assets
> with different dates of inception). Now
> parallel-iteration is really ugly business. There's
> always a bigger can of worms...

Always.

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