Arno Schödl | 29 Aug 12:10
Favicon

lifetime of ranges vs. iterators

Hello,

some adaptor ranges need certain constant information about their underlying ranges. For example,
iterating over a forward "difference_range", the difference between two sorted ranges, requires
access to the ends of the underlying ranges. These two additional iterators are constant, they do not
change when the difference_iterator is incremented.

Now, where should this additional constant information be stored?

a) In the difference_range, and each iterator holds a reference to the difference_range? That requires
the difference_range to be alive as long as its iterators are alive. This may be difficult to ensure if the
difference_range is a temporary.

b) In the iterators, so the lifetime of adaptor iterators is independent of the adaptor range. Of course,
one can use shared_ptrs and so on, but still the iterators are ultimately responsible to keep that
information available.

This initially seemed more natural to me, but if implemented naively by storing the additional iterators
in the iterator itself, it leads to storage bloat: each difference_iterator needs 4 instead of 2 of its
underlying iterators. When stacking difference iterators, the difference in required storage grows
exponentially: instead of 2^N, you need 4^N storage...

I feel that in order to solidify the range concept, one needs to make a choice whether a) or b) is "standard",
because any user of ranges would need to know. Any thoughts on this?

Arno

--
Dr. Arno Schoedl · aschoedl <at> think-cell.com 
Technical Director 
(Continue reading)

Neil Groves | 29 Aug 12:59

Re: lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 11:12 AM, Arno Schödl <aschoedl <at> think-cell.com>wrote:

> Hello,
>
>
>
> some adaptor ranges need certain constant information about their
> underlying ranges. For example, iterating over a forward "difference_range",
> the difference between two sorted ranges, requires access to the ends of the
> underlying ranges. These two additional iterators are constant, they do not
> change when the difference_iterator is incremented.
>
>
>
> Now, where should this additional constant information be stored?
>
>
>
> a) In the difference_range, and each iterator holds a reference to the
> difference_range? That requires the difference_range to be alive as long as
> its iterators are alive. This may be difficult to ensure if the
> difference_range is a temporary.
>
>
Option a, is the solution that I prefer due to the lower memory overhead. I
am of the opinion that there is no need to provide a uniform lifetime
arrangement since it would often make optimal implementations impossible.
The difference_range should be implemented with the requirement that it be
held for as long as the iterators. The iterator invalidation of the standard
containers being different for same named operations on different classes is
(Continue reading)

David Abrahams | 29 Aug 13:00
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Fri Aug 29 2008, Arno Schödl <aschoedl-AT-think-cell.com> wrote:

> Hello,
>
>  
>
> some adaptor ranges need certain constant information about their
> underlying ranges. For example, iterating over a forward
> "difference_range", the difference between two sorted ranges, requires
> access to the ends of the underlying ranges. These two additional
> iterators are constant, they do not change when the
> difference_iterator is incremented.
>
>  
>
> Now, where should this additional constant information be stored?
>
>  
>
> a) In the difference_range, and each iterator holds a reference to the
> difference_range? That requires the difference_range to be alive as
> long as its iterators are alive. This may be difficult to ensure if
> the difference_range is a temporary.
>
> b) In the iterators, so the lifetime of adaptor iterators is
> independent of the adaptor range. Of course, one can use shared_ptrs
> and so on, but still the iterators are ultimately responsible to keep
> that information available.
>
(Continue reading)

Neil Groves | 29 Aug 13:05

Re: lifetime of ranges vs. iterators

>
> Aren't standard containers supposed to satisfy the Range concepts?  When
> the container is destroyed, its iterators are invalidated, right?
>

Yes, containers satisfy Range concepts, but this question is more related to
the opposite relationship. There is no need for a range destruction to
invalidate the iterators, and this leads to simpler code for example when
creating a temporary range to pass to a range algorithm.

Dave Abrahams
> BoostPro Computing
> http://www.boostpro.com

Neil Groves

>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

David Abrahams | 29 Aug 15:56
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Fri Aug 29 2008, "Neil Groves" <neil-AT-grovescomputing.com> wrote:

>>
>> Aren't standard containers supposed to satisfy the Range concepts?  When
>> the container is destroyed, its iterators are invalidated, right?
>>
>
> Yes, containers satisfy Range concepts, but this question is more related to
> the opposite relationship. There is no need for a range destruction to
> invalidate the iterators, and this leads to simpler code for example when
> creating a temporary range to pass to a range algorithm.

So?  No algorithm could possibly _rely_ on the fact that the iterators
will be invalidated, so what does it matter?  You can do whatever's most
efficient.

--

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Arno Schödl | 29 Aug 14:50
Favicon

Re: lifetime of ranges vs. iterators

For iterators, the make_ helpers are great:

vector<int> vecnA;
vector<int> vecnB;

DoSomethingWithRangeThatStoresIterators( 
	make_difference_iterator( vecnA.begin(), vecnA,end(), vecnB.begin(), vecnB.end() ),
	make_difference_iterator( vecnA.end(), vecnA,end(), vecnB.end(), vecnB.end() )
);

The equivalent with ranges, supposedly easier, would now look like this:

vector<int> vecnA;
vector<int> vecnB;

difference_range< int, int > diffrng( vecnA, vecnB ); // watch out with scope
DoSomethingWithRangeThatStoresIterators( diffrng ); 

as opposed to the much nicer:

vector<int> vecnA;
vector<int> vecnB;

DoSomethingWithRangeThatStoresIterators( make_difference_iterator( vecnA, vecnB ) );

If we drive this stacking to one or more levels, things pretty quickly become pretty ugly. Do we really want that?

Arno

-----Original Message-----
(Continue reading)

Neil Groves | 29 Aug 14:56

Re: lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 1:50 PM, Arno Schödl <aschoedl <at> think-cell.com>wrote:

> For iterators, the make_ helpers are great:
>
> vector<int> vecnA;
> vector<int> vecnB;
>
> DoSomethingWithRangeThatStoresIterators(
>        make_difference_iterator( vecnA.begin(), vecnA,end(), vecnB.begin(),
> vecnB.end() ),
>        make_difference_iterator( vecnA.end(), vecnA,end(), vecnB.end(),
> vecnB.end() )
> );
>
> The equivalent with ranges, supposedly easier, would now look like this:
>
> vector<int> vecnA;
> vector<int> vecnB;
>
> difference_range< int, int > diffrng( vecnA, vecnB ); // watch out with
> scope
> DoSomethingWithRangeThatStoresIterators( diffrng );
>
> as opposed to the much nicer:
>
> vector<int> vecnA;
> vector<int> vecnB;
>
> DoSomethingWithRangeThatStoresIterators( make_difference_iterator( vecnA,
> vecnB ) );
(Continue reading)

Arno Schödl | 29 Aug 15:13
Favicon

Re: lifetime of ranges vs. iterators

I have seen your | operator. It is o.k. for unary things, like rng | filter( predicate ), but for binary
things, it is a bit weird:

rngA | difference(rngB)

I find the alternatives clearer: rngA - rngB would be nice (but requires concept checking), or difference(
rngA, rngB ).

But regardless of notation, doesn't this suffer from the same problem that these objects are temporaries?

Arno

-----Original Message-----
From: boost-bounces <at> lists.boost.org [mailto:boost-bounces <at> lists.boost.org] On Behalf Of Neil Groves
Sent: Friday, August 29, 2008 2:56 PM
To: boost <at> lists.boost.org
Subject: Re: [boost] lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 1:50 PM, Arno Schödl <aschoedl <at> think-cell.com>wrote:

> For iterators, the make_ helpers are great:
>
> vector<int> vecnA;
> vector<int> vecnB;
>
> DoSomethingWithRangeThatStoresIterators(
>        make_difference_iterator( vecnA.begin(), vecnA,end(), vecnB.begin(),
> vecnB.end() ),
>        make_difference_iterator( vecnA.end(), vecnA,end(), vecnB.end(),
> vecnB.end() )
(Continue reading)

Neil Groves | 29 Aug 15:25

Re: lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 2:13 PM, Arno Schödl <aschoedl <at> think-cell.com>wrote:

> I have seen your | operator. It is o.k. for unary things, like rng |
> filter( predicate ), but for binary things, it is a bit weird:
>
> rngA | difference(rngB)
>
> I find the alternatives clearer: rngA - rngB would be nice (but requires
> concept checking), or difference( rngA, rngB ).
>

Indeed I can definately see your point about binary operations.

>
> But regardless of notation, doesn't this suffer from the same problem that
> these objects are temporaries?
>

Yes, it does not help at all with the 'difference' issue. Would you be able
to supply code for the difference solution you have proposed? I would be
happy to look at it. It would help me understand the problem more
concretely. If you have specific suggestions for changes to range / range_ex
then I would be happy to consider them. With my currently level of
non-understanding of the actual difference algorithm I wonder if the result
is not more appropriately a model of a Container rather than a Range?

Neil

>
> Arno
(Continue reading)

Re: lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 3:13 PM, Arno Schödl <aschoedl <at> think-cell.com> wrote:
> I have seen your | operator. It is o.k. for unary things, like rng | filter( predicate ), but for binary
things, it is a bit weird:
>
> rngA | difference(rngB)
>
> I find the alternatives clearer: rngA - rngB would be nice (but requires concept checking), or
difference( rngA, rngB ).

What about Infix a-la FC++?

  (rngA ^difference^ rngB)

I love the syntax, but maybe this is too much operator abuse.

>
> But regardless of notation, doesn't this suffer from the same problem that these
> objects are temporaries?

I'm still missing something: where is the problem in:

vector<int> vecnA;
vector<int> vecnB;

DoSomethingWithRangeThatStoresIterators( difference_range< int, int >
diffrng( vecnA, vecnB ) );

This works, as long as the range doesn't escape from DSWRTSI, or a copy is made.

[of course a make_difference_range would make the code much cleaner]
(Continue reading)

Arno Schödl | 29 Aug 16:12
Favicon

Re: lifetime of ranges vs. iterators

Indeed, as long as the range does not escape, everything is fine. But here we are in trouble:

some_stacked_adaptor_range<...> rng( make_difference_range( vecnA, vecnB ) );

Again, notation makes no difference, but Neil's idea, which I fully endorse, namely easy stacking, makes
the problem all the more apparent:

some_stacked_adaptor_range<...> rng( vecnA | difference( vecnB ) ); // wrong

What makes this really annoying is that there is an easy way out: Wouldn't it be more natural to represent a
forward range as a single iterator, with an extra .at_end() check? This would eliminate the problem,
because a difference_range would only need two such iterators, not four. Stacking them naively would
have 2^N storage growth, rather than 4^N, and we still don't need to worry about range lifetime.

For other classes of iterators, the situation is similar. bidirectional ranges have at_end() and
at_begin(), and random_access_ranges a size() and an operator[].

I know that overthrowing such an established concept as ranges is not terribly popular, but I don't see any
other way to combine easy stacking with no worry about lifetimes.

Arno

-----Original Message-----
From: boost-bounces <at> lists.boost.org [mailto:boost-bounces <at> lists.boost.org] On Behalf Of Giovanni
Piero Deretta
Sent: Friday, August 29, 2008 3:28 PM
To: boost <at> lists.boost.org
Subject: Re: [boost] lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 3:13 PM, Arno Schödl <aschoedl <at> think-cell.com> wrote:
(Continue reading)

Re: lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 4:12 PM, Arno Schödl <aschoedl <at> think-cell.com> wrote:
> Indeed, as long as the range does not escape, everything is fine. But here we are in
> trouble:
>
> some_stacked_adaptor_range<...> rng( make_difference_range( vecnA, vecnB ) );
>
> Again, notation makes no difference, but Neil's idea, which I fully endorse, namely
> easy stacking, makes the problem all the more apparent:
>
> some_stacked_adaptor_range<...> rng( vecnA | difference( vecnB ) ); // wrong
>
> What makes this really annoying is that there is an easy way out: Wouldn't it be more
> natural to represent a forward range as a single iterator, with an extra .at_end()
> check? This would eliminate the problem, because a difference_range would only need
> two such iterators, not four. Stacking them naively would have 2^N storage growth,
> rather than 4^N, and we still don't need to worry about range lifetime.
>

Ok, now I understand.

The lack of auto and decltype usually means that you use range
adaptors in a strictly LIFO way, i.e. you usually never have named
range adaptors, and iterator validity is never an issue as the scope
of the wrapping range is always a subset of the wrapped range.

Anyways, the solution is simple: if you want your adaptor to survive
the original wrapped range, you have to store a copy of the wrapped
range in the adaptor. As an added benefit, the adaptor size will be
the smallest possible for deep sequences of stacked adaptors.

(Continue reading)

David Abrahams | 29 Aug 16:48
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Fri Aug 29 2008, Arno Schödl <aschoedl-AT-think-cell.com> wrote:

> Wouldn't it be more natural to represent a forward range as a single
> iterator, with an extra .at_end() check? This would eliminate the
> problem, because a difference_range would only need two such
> iterators, not four. 

As part of
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1873.html one
of the ideas we discussed extensively was allowing the end iterator of a
range to be a different type from the begin iterator.  That would allow
a sort of "universal end iterator" to be used in situations such as
this:

  struct end
  {
      // These definitions are only useful for infinite ranges
      template <class T>
      bool operator==(T const&) { return false; }
      template <class T>
      bool operator=!(T const&) { return true; }
  };

Effectively the data representation of the begin iterator would be the
same as what you're proposing, plus comparison operators against the
universal end type above, but the end iterator type could be empty (and
optimized away).  That allows a generic description of algorithms that
are optimally efficient on pairs of pointers, but could simplify many
classes of iterator (ifstream_iterators, for example) where every
(Continue reading)

Re: lifetime of ranges vs. iterators

On Fri, Aug 29, 2008 at 4:48 PM, David Abrahams <dave <at> boostpro.com> wrote:
>
> on Fri Aug 29 2008, Arno Schödl <aschoedl-AT-think-cell.com> wrote:
>
>> Wouldn't it be more natural to represent a forward range as a single
>> iterator, with an extra .at_end() check? This would eliminate the
>> problem, because a difference_range would only need two such
>> iterators, not four.
>
> As part of
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1873.html one
> of the ideas we discussed extensively was allowing the end iterator of a
> range to be a different type from the begin iterator.
>

<snip>

> Effectively the data representation of the begin iterator would be the
> same as what you're proposing, plus comparison operators against the
> universal end type above, but the end iterator type could be empty (and
> optimized away).  That allows a generic description of algorithms that
> are optimally efficient on pairs of pointers, but could simplify many
> classes of iterator (ifstream_iterators, for example) where every
> iterator needs all the information required to detect whether it can
> advance anyway.
>

Nice!

>> Stacking them naively would have 2^N storage
(Continue reading)

David Abrahams | 30 Aug 01:51
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Fri Aug 29 2008, "Giovanni Piero Deretta" <gpderetta-AT-gmail.com> wrote:

> On Fri, Aug 29, 2008 at 4:48 PM, David Abrahams <dave <at> boostpro.com> wrote:
>>
>> I've been trying to say that generic algorithms *can't* worry about
>> the range lifetime; they have to assume that the iterators they're
>> passed will not be invalidated before they're destroyed.
>
> I think that the OP is talking about a different problem: let say you
> want to name the result of stacked applications of range adaptors:
>
> template<typename Range>
> void my_algo(Range& r) {
>   auto stacked_range = r | filtered(filter) | mapped(mapper);
>   // use stacked_range
> }
>
> This may lead iterator lifetime problems, not because r iterators may
> be invalid, but because the iterators of the range returned by
> filtered(...) may become invalid as that temporary has been
> destructed. There are many solutions:
>
> The easiest is splitting apart the expression, but is ugly.
>
> Then you could use expression templates to make the whole pipe
> expression return a range only at the end.
>
> Finally, both filtered and mapped could guarantee that the iterators
> are not invalidated when the range is destructed, but that may prevent
(Continue reading)

Re: lifetime of ranges vs. iterators

On Sat, Aug 30, 2008 at 1:51 AM, David Abrahams <dave <at> boostpro.com> wrote:
>
>> I'm not sure this is as revolutionary as you think.  Every other
>>> language I've looked at has a get_next()/at_end() iterator abstraction,
>>> which is a lot like what you're proposing.  My advice: make sure you
>>> keep in mind the whole problem domain.  You can't understand the reason
>>> that C++ iterators are unlike those in other languages without looking
>>> at the algorithms.
>>>
>>
>> BTW, ranges are equivalent to GoF iterators:
>
> Well, they're informationally equivalent...
>
>> you just spell 'at_end'
>> as 'empty(r)';
>> 'next()' as 'r = range(next(begin(r)), end(r))'   (equivalently: 'r =
>> rest(r))'; and '.get()' as
>> '*begin(r)';
>
> ...but that is so ugly as to make them non-equivalent abstractions in my
> book.  And that's a good thing for ranges ;-)

when I need a simple for loop (and can't reach for BOOST_FOREACH), the syntax
I sometimes use is:

for(range<container> r (c);  r; r = rest(r)) {
   ....
}

(Continue reading)

Re: lifetime of ranges vs. iterators

On Sat, Aug 30, 2008 at 1:51 AM, David Abrahams <dave <at> boostpro.com> wrote:
>
> on Fri Aug 29 2008, "Giovanni Piero Deretta" <gpderetta-AT-gmail.com> wrote:
>
>> On Fri, Aug 29, 2008 at 4:48 PM, David Abrahams <dave <at> boostpro.com> wrote:
>>>
>>> I've been trying to say that generic algorithms *can't* worry about
>>> the range lifetime; they have to assume that the iterators they're
>>> passed will not be invalidated before they're destroyed.
>>
>> I think that the OP is talking about a different problem: let say you
>> want to name the result of stacked applications of range adaptors:
>>
>> template<typename Range>
>> void my_algo(Range& r) {
>>   auto stacked_range = r | filtered(filter) | mapped(mapper);
>>   // use stacked_range
>> }
>>
>> This may lead iterator lifetime problems, not because r iterators may
>> be invalid, but because the iterators of the range returned by
>> filtered(...) may become invalid as that temporary has been
>> destructed. There are many solutions:
>>
>> The easiest is splitting apart the expression, but is ugly.
>>
>> Then you could use expression templates to make the whole pipe
>> expression return a range only at the end.
>>
>> Finally, both filtered and mapped could guarantee that the iterators
(Continue reading)

Arno Schödl | 30 Aug 11:26
Favicon

Re: lifetime of ranges vs. iterators

I am following up on Giovanni's idea of storing ranges by value. To minimize the impact on existing code, and
to make the frequent case of wrapping an adaptor range around another container like vector or set easy, we
could shift the burden to specify which ranges should be stored by reference and which by value onto the
ranges themselves.

A template container_reference would designate a reference to the underlying container that holds the
data. The data is assumed to remain valid, but any adaptors around it may go out of scope.

Storage by reference would be default, so everything works as it did so far, but any wrapped ranges may not go
out of scope:

template< class T >
struct container_reference {
	typedef T& type;
};

Adaptor ranges could decide to store themselves by value:

template< class RngA, class RngB >
struct container_reference< difference_range<RngA, RngB> > {
	typedef T type;
};

Adaptors would use it to store a reference to the underlying data:

template< class RngA, class RngB >
class difference_range {
	typename container_reference< RngA >::type m_rngA;
	typename container_reference< RngB >::type m_rngB;

(Continue reading)

David Abrahams | 30 Aug 20:17
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Sat Aug 30 2008, "Giovanni Piero Deretta" <gpderetta-AT-gmail.com> wrote:

> Hum, this would means that every range needs to know how to construct
> and deconstruct iterators of other ranges, at least if it wants to
> guarantee that the minimum space possible is used.

It would be done with associated types and functions of the iterators.
Few iterators need to store that kind of redundant information, but when
you build an iterator type that *does*, it would be deemed "polite" to
specialize an appropriate trait and/or define the corresponding
overloads to allow it to be decomposed/re-composed.

I've been itching to rework Boost.Iterator recently, and this could make
a very nice enhancement.

> It seems a bit complex, and there would be a need to come up with some
> non obvious protocol to do that (some sort of compile time
> introspection on ranges).

Not on ranges; just on iterators.

> Is it really worth it? Are you advocating it, or it was just another
> solution to consider?

I think I may be advocating it :-)

I think there may be some relationship between this and hierarchical
iterator representation, so I'd like to explore it further from that
angle as well.
(Continue reading)

Steven Watanabe | 30 Aug 23:18

Re: lifetime of ranges vs. iterators

AMDG

David Abrahams wrote:
> on Sat Aug 30 2008, "Giovanni Piero Deretta" <gpderetta-AT-gmail.com> wrote:
>
>   
>> Hum, this would means that every range needs to know how to construct
>> and deconstruct iterators of other ranges, at least if it wants to
>> guarantee that the minimum space possible is used.
>>     
>
> It would be done with associated types and functions of the iterators.
> Few iterators need to store that kind of redundant information, but when
> you build an iterator type that *does*, it would be deemed "polite" to
> specialize an appropriate trait and/or define the corresponding
> overloads to allow it to be decomposed/re-composed.
>
> I've been itching to rework Boost.Iterator recently, and this could make
> a very nice enhancement.
>   

How would this work:

concept IteratorWithEnd<typename Iterator> {
    Iterator get_end(Iterator);
    Iterator set_end(Iterator, Iterator);
}

Taking filter_iterator, for example, this would allow the space to be 
minimized,
(Continue reading)

David Abrahams | 31 Aug 02:24
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Sat Aug 30 2008, Steven Watanabe <watanabesj-AT-gmail.com> wrote:

> How would this work:
>
> 
> template<IteratorWithEnd Iterator, class F>
> class filter_iterator<Iterator, F>
> {
> public:
>    //...
>    filter_iterator(Iterator begin, Iterator end, F f)
>      : iter(set_end(begin,end)), f(f)
>    {}
>     
>    filter_iterator& operator++() {
>        ++iter;
>        while(iter != get_end(iter) && !f(*iter)) ++iter;
>        return *this;
>    }
>     
>    friend Iterator get_end(const filter_iterator& self) {
>        return
>            filter_iterator(
>                get_end(self.iter), get_end(self.iter), self.f);
>    }
>     
>    friend Iterator set_end(
>        const filter_iterator& self, const filter_iterator& other)
>    {
(Continue reading)

Steven Watanabe | 31 Aug 02:39

Re: lifetime of ranges vs. iterators

AMDG

David Abrahams wrote:
>>    friend Iterator get_end(const filter_iterator& self) {
>>        return
>>            filter_iterator(
>>                get_end(self.iter), get_end(self.iter), self.f);
>>    }
>>     
>>    friend Iterator set_end(
>>        const filter_iterator& self, const filter_iterator& other);
>>     
>
> Hi Steven,
>
> Say for example Iterator is strided_iterator<int>.  Then the return type
> of set_end above is strided_iterator<int>, right?  So how will its body
> typecheck?  Seems like this needs a little more thought.
>   

Oops.  I intended the return type to be filter_iterator.

In Christ,
Steven Watanabe

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

David Abrahams | 31 Aug 06:15
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Sat Aug 30 2008, Steven Watanabe <watanabesj-AT-gmail.com> wrote:

> AMDG
>
> David Abrahams wrote:
>>>    friend Iterator get_end(const filter_iterator& self) {
>>>        return
>>>            filter_iterator(
>>>                get_end(self.iter), get_end(self.iter), self.f);
>>>    }
>>>        friend Iterator set_end(
>>>        const filter_iterator& self, const filter_iterator& other);
>>>     
>>
>> Hi Steven,
>>
>> Say for example Iterator is strided_iterator<int>.  Then the return type
>> of set_end above is strided_iterator<int>, right?  So how will its body
>> typecheck?  Seems like this needs a little more thought.
>>   
>
> Oops.  I intended the return type to be filter_iterator.

Okay then, I take it you meant something like:

    template<IteratorWithEnd Iterator, class F>
    class filter_iterator<Iterator, F>
    {
    public:
(Continue reading)

Arno Schödl | 31 Aug 16:09
Favicon

Re: lifetime of ranges vs. iterators

>  // Gimme a better name, please!  The abstraction is a thing that
>  // is always used in pairs, where each instance contains some 
>  // redundant information in common.
>  auto concept Factorable<typename T>
>  {
>       typename common_data = void;
>       typename unique_data = T;
>
>       common_data common(T) {}
>       unique_data unique(T x) { return x; }
>       T reconstruct(common_data, unique_data x) { return x; }
>  };

How is the separation of common_data and unique_data different from a separation of ranges and iterators?
If iterators of ranges can rely on their range to exist, this is where common data like functors, end
iterators etc. can be stored.

Arno

--
Dr. Arno Schoedl · aschoedl <at> think-cell.com 
Technical Director 

think-cell Software GmbH · Invalidenstr. 34 · 10115 Berlin, Germany 
http://www.think-cell.com · phone +49-30-666473-10 · toll-free (US) +1-800-891-8091
Directors: Dr. Markus Hannebauer, Dr. Arno Schoedl · Amtsgericht Charlottenburg, HRB 85229

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

(Continue reading)

David Abrahams | 31 Aug 21:19
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Sun Aug 31 2008, Arno Schödl <aschoedl-AT-think-cell.com> wrote:

>>  // Gimme a better name, please!  The abstraction is a thing that
>>  // is always used in pairs, where each instance contains some 
>>  // redundant information in common.
>>  auto concept Factorable<typename T>
>>  {
>>       typename common_data = void;
>>       typename unique_data = T;
>>
>>       common_data common(T) {}
>>       unique_data unique(T x) { return x; }
>>       T reconstruct(common_data, unique_data x) { return x; }
>>  };
>
> How is the separation of common_data and unique_data different from a separation
> of ranges and iterators? If iterators of ranges can rely on their range to
> exist, this is where common data like functors, end iterators etc. can be
> stored.

Naturally the point is to store the common data once when you need to
store more than one iterator to the same sequence -- in a range, for
example.

If you're asking why I bother reconstituting the whole iterator out of
its parts, instead of simply referring to the common_data stored in the
range: an adapted iterator is a useful concept regardless of whether
anyone is using a range abstraction.

(Continue reading)

Arno Schödl | 31 Aug 23:59
Favicon

Re: lifetime of ranges vs. iterators

> > How is the separation of common_data and unique_data different from a separation
> > of ranges and iterators? If iterators of ranges can rely on their range to
> > exist, this is where common data like functors, end iterators etc. can be
> > stored.

> Naturally the point is to store the common data once when you need to
> store more than one iterator to the same sequence -- in a range, for
> example.

> If you're asking why I bother reconstituting the whole iterator out of
> its parts, instead of simply referring to the common_data stored in the
> range: an adapted iterator is a useful concept regardless of whether
> anyone is using a range abstraction.

You could provide an adapted_iterator and also an adapted_range. If we subscribe to the rule that ranges
must stay valid for their iterators to be valid, the adapted_range::iterator can use the common data
stored in the range, while the adapted_iterator stores the common data itself. Both could even be derived
from the same source code. Do you then still need a factored iterator?

Or do you want to avoid people having to use the range abstraction? If you pass iterator pairs to algorithms
instead of ranges, at least this parameter passing would have to pass the common data redundantly, even if
inside the algorithm, say when many iterators have to be stored, the common data is stripped and stored separately.

--
Dr. Arno Schoedl · aschoedl <at> think-cell.com 
Technical Director 

think-cell Software GmbH · Invalidenstr. 34 · 10115 Berlin, Germany 
http://www.think-cell.com · phone +49-30-666473-10 · toll-free (US) +1-800-891-8091
Directors: Dr. Markus Hannebauer, Dr. Arno Schoedl · Amtsgericht Charlottenburg, HRB 85229
(Continue reading)

David Abrahams | 1 Sep 05:24
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Sun Aug 31 2008, Arno Schödl <aschoedl-AT-think-cell.com> wrote:

>> > How is the separation of common_data and unique_data different from a
> separation
>> > of ranges and iterators? If iterators of ranges can rely on their range to
>> > exist, this is where common data like functors, end iterators etc. can be
>> > stored.
>
>> Naturally the point is to store the common data once when you need to
>> store more than one iterator to the same sequence -- in a range, for
>> example.
>
>> If you're asking why I bother reconstituting the whole iterator out of
>> its parts, instead of simply referring to the common_data stored in the
>> range: an adapted iterator is a useful concept regardless of whether
>> anyone is using a range abstraction.
>
> You could provide an adapted_iterator and also an adapted_range. 

My point is that the adapted_range would then have semantically
equivalent iterators with a different representation (and an extra
indirection), which seems like a waste.

> If we subscribe to the rule that ranges must stay valid for their
> iterators to be valid,

I don't.  I do subscribe to the rule that generic code cannot afford to
destroy an arbitrary range while its iterators are still in use.

(Continue reading)

Arno Schödl | 1 Sep 08:57
Favicon

Re: lifetime of ranges vs. iterators

> > If we subscribe to the rule that ranges must stay valid for their
> > iterators to be valid,

> I don't.  I do subscribe to the rule that generic code cannot afford to
> destroy an arbitrary range while its iterators are still in use.

Isn't that what I said? There may be iterators that work without their ranges, but in general they don't.

> > the adapted_range::iterator can use the common data stored in the
> > range, while the adapted_iterator stores the common data itself. Both
> > could even be derived from the same source code. 

> Yeah, that's still a lot of coding effort.

I think you could write it generically, a la iterator_facade/adaptor, so it is a one-time fixed cost.

> > Do you then still need a factored iterator?

> You need to be able to take two adapted iterators and turn them into a
> range.  Do you want that range to contain redundant data?  I don't.

> > Or do you want to avoid people having to use the range abstraction? 

> I certainly don't want to force it on them.

Ok, now I understand. The debate is about primacy of ranges or iterators. You propose that iterators stay
the primary vehicle and to convert them to/from ranges by stripping the common information. But that
would mean that there is no "lean" iterator, all iterators would contain the redundant information. 

When stacking N difference_ranges, the size difference between "fat" and "lean" iterators is 2^N. Thus in
(Continue reading)

David Abrahams | 1 Sep 18:51
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Mon Sep 01 2008, Arno Schödl <aschoedl-AT-think-cell.com> wrote:

>> > If we subscribe to the rule that ranges must stay valid for their
>> > iterators to be valid,
>
>> I don't.  I do subscribe to the rule that generic code cannot afford to
>> destroy an arbitrary range while its iterators are still in use.
>
> Isn't that what I said? 

No, but it might be what you meant.

> There may be iterators that work without their ranges,
> but in general they don't.
>
>> > the adapted_range::iterator can use the common data stored in the
>> > range, while the adapted_iterator stores the common data itself. Both
>> > could even be derived from the same source code. 
>
>> Yeah, that's still a lot of coding effort.
>
> I think you could write it generically, a la iterator_facade/adaptor,
> so it is a one-time fixed cost.

I'm not sure if it's worth the effort.

>> > Do you then still need a factored iterator?
>
>> You need to be able to take two adapted iterators and turn them into a
(Continue reading)

Re: lifetime of ranges vs. iterators

On Mon, Sep 1, 2008 at 5:24 AM, David Abrahams <dave <at> boostpro.com> wrote:
>
> on Sun Aug 31 2008, Arno Schödl <aschoedl-AT-think-cell.com> wrote:
>
>>> > How is the separation of common_data and unique_data different from a
>> separation
>>> > of ranges and iterators? If iterators of ranges can rely on their range to
>>> > exist, this is where common data like functors, end iterators etc. can be
>>> > stored.
>>
>>> Naturally the point is to store the common data once when you need to
>>> store more than one iterator to the same sequence -- in a range, for
>>> example.
>>
>>> If you're asking why I bother reconstituting the whole iterator out of
>>> its parts, instead of simply referring to the common_data stored in the
>>> range: an adapted iterator is a useful concept regardless of whether
>>> anyone is using a range abstraction.
>>
>> You could provide an adapted_iterator and also an adapted_range.
>
> My point is that the adapted_range would then have semantically
> equivalent iterators with a different representation (and an extra
> indirection), which seems like a waste.

hum, which indirection?

<...>
>> Do you then still need a factored iterator?
>
(Continue reading)

Arno Schödl | 1 Sep 13:56
Favicon

Re: lifetime of ranges vs. iterators

> >
> > You need to be able to take two adapted iterators and turn them into a
> > range.  Do you want that range to contain redundant data?  I don't.

> Hei, maybe this is all that we need! Let's have a metafunction that
> given an iterator type returns its preferred range type (the
> associated range). The default might be boost.range or even std::pair.
>  The developer of an iterator adaptor, can specialize the metafunction
> so that the associate range is the optimum representation for an
> iterator pair.

> Do you think this would be enough? It seems too simple to me, so
> probably I'm missing something...

I wouldn't know what to do when my iterator_adaptor needs three iterators to the same range. I could store
two efficiently as a range and retrieve them when needed by querying range::begin()/end(), but what do I
do with the third? Dave's factored iterators go further in the same direction, basically dropping the
requirement that the common data must be a range, it can be anything. 

Both proposals have the disadvantage that I need to wrap/unwrap the [common data (Dave)/range
(Giovanni)] whenever I change one of the wrapped iterators. I'd rather store the common data explicitly,
and have indirections inside the iterators that point to it.

Correct me if I misunderstand one of the two proposals.

Arno

--
Dr. Arno Schoedl · aschoedl <at> think-cell.com 
Technical Director 
(Continue reading)

Re: lifetime of ranges vs. iterators

On Mon, Sep 1, 2008 at 1:56 PM, Arno Schödl <aschoedl <at> think-cell.com> wrote:
>> >
>> > You need to be able to take two adapted iterators and turn them into a
>> > range.  Do you want that range to contain redundant data?  I don't.
>
>> Hei, maybe this is all that we need! Let's have a metafunction that
>> given an iterator type returns its preferred range type (the
>> associated range). The default might be boost.range or even std::pair.
>>  The developer of an iterator adaptor, can specialize the metafunction
>> so that the associate range is the optimum representation for an
>> iterator pair.
>
>> Do you think this would be enough? It seems too simple to me, so
>> probably I'm missing something...
>
> I wouldn't know what to do when my iterator_adaptor needs three iterators to the >same range. I could store
two efficiently as a range and retrieve them when needed >by querying range::begin()/end(), but what do I
do with the third? Dave's factored
>terators go further in the same direction, basically dropping the requirement that the >common data must
be a range, it can be anything.

You are right. It wouldn't work with three iterators.

But three iterators are a range of ranges. Maybe segmented iterators
(or, better, segmented ranges)  can come to the rescue?

Need to think about it. Could you give a practical example of an
iterator adapter needing three iterators?

>
(Continue reading)

Arno Schödl | 1 Sep 16:08
Favicon

Re: [SPAM (Bayesian)] - Re: lifetime of ranges vs. iterators - Bayesian Filter detected spam

> Need to think about it. Could you give a practical example of an
> iterator adapter needing three iterators?

I never needed them in practice. But I would forward this to Dave. He likes the complete generality of
factored iterators, I think. I think it is a cleaner concept than saying the common data must be
representable as a range, but I cannot really back this up with practical examples. The
wrapping/unwrapping (see below) troubles me in either case.

> You would still have to allow for storing the wrapped iterator in the
> wrapper if the wrapped iterator does not support refactoring. I do not
> think there is a way around it.

But in my proposal (actually, yours), all iterators would be "lean", and each iterator would have access to
its range, all the way up the iterator/range stack. So each iterator would itself only wrap other lean
iterators. Storage bloat for stack size N would only be O(N) for the indirections, rather than O(2^N).

> Also, I have this (completely unfounded) feeling that an iterator that
> relies on accessing common data in the owning range might be harder to
> optimize.

> I think that ranges should be used for long term storage, so if the
> iterator size itself is not optimal shouldn't be a big problem.

Without any kind of storage optimization like your preferred-range templates or Dave's
factored-iterators, the storage bloat would be _factor_ 2^N, so ever handling completely unwrapped
iterators just does not scale.

So it comes down to:

a) with storage optimization, at every iterator access, unwrap/wrap the iterator out of their storage:
(Continue reading)

Re: [SPAM (Bayesian)] - Re: lifetime of ranges vs. iterators - Bayesian Filter detected spam

On Mon, Sep 1, 2008 at 4:08 PM, Arno Schödl <aschoedl <at> think-cell.com> wrote:
>> Need to think about it. Could you give a practical example of an
>> iterator adapter needing three iterators?
>
> I never needed them in practice. But I would forward this to Dave. He likes the
> complete generality of factored iterators, I think. I think it is a cleaner concept than
> saying the common data must be representable as a range, but I cannot really back
> this up with practical examples. The wrapping/unwrapping (see below) troubles me in > either case.
>

Sure, full generality is always good, but if ranges cover 90% of the
cases, maybe it is good enough.

>> You would still have to allow for storing the wrapped iterator in the
>> wrapper if the wrapped iterator does not support refactoring. I do not
>> think there is a way around it.
>
> But in my proposal (actually, yours), all iterators would be "lean", and each iterator
> would have access to its range, all the way up the iterator/range stack. So each
> iterator would itself only wrap other lean iterators. Storage bloat for stack size N
> would only be O(N) for the indirections, rather than O(2^N).

You can't assume that all iterators are lean. In fact most won't.
Unless you drop compatibility with existing iterators, which is a
no-no.

My proposal was aimed at making ranges as lean as possible, not
necessarily iterators.
Anyways, as long as all parts of the stack are 'lean' ranges, you
should have only a 2x storage bloat when using iterators.
(Continue reading)

Arno Schödl | 1 Sep 20:28
Favicon

Re: [SPAM (Bayesian)] - Re: lifetime of ranges vs.iterators - Bayesian Filter detected spam

> Sure, full generality is always good, but if ranges cover 90% of the
> cases, maybe it is good enough.

I agree.

> > But in my proposal (actually, yours), all iterators would be "lean", and each iterator
> > would have access to its range, all the way up the iterator/range stack. So each
> > iterator would itself only wrap other lean iterators. Storage bloat for stack size N
> > would only be O(N) for the indirections, rather than O(2^N).

> You can't assume that all iterators are lean. In fact most won't.
> Unless you drop compatibility with existing iterators, which is a
> no-no.

Old iterators will have neither the indirection I propose, nor will they offer a non-trivial
factorization nor internally store preferred ranges. So any of the schemes discussed make no difference
to old iterators. But old iterators are often trivial, so the whole problem should not really apply to them.

> My proposal was aimed at making ranges as lean as possible, not
> necessarily iterators.
> Anyways, as long as all parts of the stack are 'lean' ranges, you
> should have only a 2x storage bloat when using iterators.

The bloat is potentially much worse with your implementation below:

>  iterator begin() const{
>       return iterator(begin(m_range), end(m_range), m_f);
>  }

This triggers recursive unwrapping all the way to the top, causing factor 2^N extra storage to be
(Continue reading)

David Abrahams | 1 Sep 19:01
Favicon
Gravatar

Re: lifetime of ranges vs. iterators


on Mon Sep 01 2008, "Giovanni Piero Deretta" <gpderetta-AT-gmail.com> wrote:

> Need to think about it. Could you give a practical example of an
> iterator adapter needing three iterators?

E.g., a bidirectional filtered_iterator needs to be prevented from
stepping off both ends of the underlying data to avoid undefined
behavior.

On the other hand, you shouldn't have to pay for those endpoint
comparisons all the time, which tells me that something may be wrong
with the design.

Iterators are supposed to be lightweight things.  If we're seriously
worried about the cost of building iterator adaptors, we might consider
the idea that there's something wrong with the bigger picture.  Maybe
these endpoint comparisons need to be dealt with differently, somehow.
The situation with deque iterators that led to segmented iterators is
very similar, and it resulted in a system of abstractions that allows
one to operate, once again, close to the machine model.

I wouldn't know how to address this issue without some real-life use
cases, though.  This has all become a bit too abstract a discussion for
me, and Generic Programming is supposed to be driven by concrete
examples.

--

-- 
Dave Abrahams
BoostPro Computing
(Continue reading)

Arno Schödl | 1 Sep 19:53
Favicon

Re: lifetime of ranges vs. iterators

> I wouldn't know how to address this issue without some real-life use
> cases, though.  This has all become a bit too abstract a discussion for
> me, and Generic Programming is supposed to be driven by concrete
> examples.

Isn't my problem concrete enough? I need a stackable difference_range without exponential storage
bloat. How should I implement it?

I am not sure that you appreciate the problem: 

Let's consider two implementations of a forward difference_iterator.

A) Implemented with iterators alone, it contains 4 iterators:

- the iterator to the range being traversed (mutable)
- the iterator to the range being removed (mutable)
- the end iterator to the range being traversed (constant, common)
- the end iterator to the range being removed (constant, common)

Total size: 4*sizeof(ptr)

B) Implemented with iterators + indirection to common data:

- the iterator to the range being traversed (mutable)
- the iterator to the range being removed (mutable)
- a reference to the range

Total size: 3*sizeof(ptr)

Now we stack these things, creating difference_range( difference_range, difference_range ) and so on.
(Continue reading)

Steven Watanabe | 1 Sep 20:32

Re: lifetime of ranges vs. iterators

AMDG

Arno Schödl wrote:
> So any implementation that even temporarily expands iterators to their original size, like Giovanni's
implementation, will need vastly more storage. IMO, relying on the compiler to fix this is hazardous.
Non-optimized code should be a constant factor slower than optimized code, and need a constant factor
more memory, not exponentially slower and exponentially more memory.
>   

If I understand Giovanni's implementation correctly, it only needs to 
expand one layer at a time.

In Christ,
Steven Watanabe

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Arno Schödl | 1 Sep 20:55
Favicon

Re: lifetime of ranges vs. iterators

> If I understand Giovanni's implementation correctly, it only needs to 
> expand one layer at a time.

Let's expand it:

  iterator begin() const{
       return iterator(begin(m_range), end(m_range), m_f);
  }

-->

  return iterator( iterator( begin(subrange), end(subrange), m_f ), iterator( end(subrange),
end(subrange), m_f ), m_f);

-->

  ...

The expansion will terminate when begin/end(sub...subrange) is the base iterator. Then the call is made
to the filter_iterator ctor, which calls the associated_range<filter_iterator> ctor, which
condenses everything back down.

Arno

--
Dr. Arno Schoedl · aschoedl <at> think-cell.com 
Technical Director 

think-cell Software GmbH · Invalidenstr. 34 · 10115 Berlin, Germany 
http://www.think-cell.com · phone +49-30-666473-10 · toll-free (US) +1-800-891-8091
(Continue reading)

Re: lifetime of ranges vs. iterators

On Mon, Sep 1, 2008 at 8:55 PM, Arno Schödl <aschoedl <at> think-cell.com> wrote:
>> If I understand Giovanni's implementation correctly, it only needs to
>> expand one layer at a time.
>
> Let's expand it:
>
>  iterator begin() const{
>       return iterator(begin(m_range), end(m_range), m_f);
>  }
>
> -->
>
>  return iterator( iterator( begin(subrange), end(subrange), m_f ), iterator( end(subrange),
end(subrange), m_f ), m_f);
>
> -->
>
>  ...
>
>
> The expansion will terminate when begin/end(sub...subrange) is the base iterator.
>Then the call is made to the filter_iterator ctor, which calls the
> associated_range<filter_iterator> ctor, which condenses everything back down.
>

I see the problem.

I think that for non complex iterators, the optimizer can still manage
to fix the problem.

(Continue reading)

Arno Schödl | 1 Sep 23:35
Favicon

Re: lifetime of ranges vs. iterators

I fixed filter_range::begin() and filter_range::end() as you said. iterator::equal was still
exponential, but can be fixed by only comparing begin(). I think the exponential cases are now gone.

Most things are still quadratic in the stack size, though... end() for example constructs a parameter of
size N-1, which to be constructed, must construct a parameter of size N-2 and so on. NRVO does not help, as
far as I understand it. You would need "upward" NRVO, constructing parameters right into the respective
members. Instead of passing a pointer to the function where it wants the return value to be constructed
(NRVO), the caller would need to get a pointer from the callee where the callee wants its parameters
constructed. I don't think compilers do that, at least not reliably enough to build a whole paradigm on it:-(

> [btw, for long chains of filter and map iterators it is not really a
> problem because you can always transform them exactly to one each of
> map and filter iterator]

That's why the problem occurred to me looking at difference_iterator.

------

struct both_end {};

template<class Iter, class F>
struct filter_iterator : iterator_facade<blah,blah...> {  
  filter_iterator(associated_range<Iter>::type const& rng, F f ) :
   m_range(rng), m_f(f) {}

  filter_iterator(iter e, F f, both_end ) :
   m_range(e,e), m_f(f) {} // if m_range takes e by value, e is expanded twice here, but no more, so no problem
private:
  void increment() {
      while(!m_f(front(m_range))
(Continue reading)

Re: lifetime of ranges vs. iterators

On Mon, Sep 1, 2008 at 11:35 PM, Arno Schödl <aschoedl <at> think-cell.com> wrote:
> I fixed filter_range::begin() and filter_range::end() as you said. iterator::equal was still
exponential, but can be fixed by only comparing begin(). I think the exponential cases are now gone.
>

it should be ok to compare only begin. If the two iterators have
different ends, probably things would be a "little" messed up anyway.

> Most things are still quadratic in the stack size, though... end() for example constructs a parameter of
size N-1, which to be constructed, must construct a parameter of size N-2 and so
> on. NRVO does not help, as far as I understand it. You would need "upward" NRVO, constructing parameters
right into the respective members. Instead of passing a pointer to the >
> function where it wants the return value to be constructed (NRVO), the caller would need to get a pointer
from the callee where the callee wants its parameters constructed. I don't think > compilers do that, at
least not reliably enough to build a whole paradigm on it:-(

Hum, I've played a little with some mockup iterators, and  I can
definitely see the N^2 stack growth, and with some expression template
heavy program, I wouldn't be surprised if the  growth would actually
become noticeable (even if it weren't enough to blow the stack, it
could still produce cache misses and such).   I can't seem to come-up
with any clever scheme to reduce the growth (short of using pointers
to the original range of course).

Enough for today. Maybe tomorrow I'll get some idea.

--

-- 
gpd
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
(Continue reading)

Steven Watanabe | 2 Sep 05:34

Re: lifetime of ranges vs. iterators

AMDG

Giovanni Piero Deretta wrote:
> Hum, I've played a little with some mockup iterators, and  I can
> definitely see the N^2 stack growth, and with some expression template
> heavy program, I wouldn't be surprised if the  growth would actually
> become noticeable (even if it weren't enough to blow the stack, it
> could still produce cache misses and such).   I can't seem to come-up
> with any clever scheme to reduce the growth (short of using pointers
> to the original range of course).
>
> Enough for today. Maybe tomorrow I'll get some idea.
>   

I've been playing around with this a bit.
See attached.

In Christ,
Steven Watanabe

Attachment (scratch.cpp): text/x-c++src, 7401 bytes
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Arno Schödl | 2 Sep 12:25
Favicon

Re: [SPAM (Bayesian)] - Re: lifetime of ranges vs. iterators - Bayesian Filter detected spam

> I've been playing around with this a bit.
> See attached.

> In Christ,
> Steven Watanabe

    iterator begin() {
        return iterator(boost::begin(m_range), boost::end(m_range), m_f);
    }
    iterator end() {
        return iterator(boost::end(m_range), boost::end(m_range), m_f);
    }

This would blow up, I believe. It gets expanded on both begin and end, causing exponential storage growth.

Arno

--
Dr. Arno Schoedl · aschoedl <at> think-cell.com 
Technical Director 

think-cell Software GmbH · Invalidenstr. 34 · 10115 Berlin, Germany 
http://www.think-cell.com · phone +49-30-666473-10 · toll-free (US) +1-800-891-8091
Directors: Dr. Markus Hannebauer, Dr. Arno Schoedl · Amtsgericht Charlottenburg, HRB 85229

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Steven Watanabe | 2 Sep 16:55

Re: lifetime of ranges vs. iterators

AMDG

Arno Schödl wrote:
> This would blow up, I believe. It gets expanded on both begin and end, causing exponential storage growth.
>   

Ok.  Right.  Incidentally, the storage required is not exponential 
because it is not
all needed at once, but I see your point.

I like the idea of having the filter_iterator contain a copy of the range.

In Christ,
Steven Watanabe

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Arno Schödl | 2 Sep 17:02
Favicon

Re: [SPAM (Bayesian)] - Re: lifetime of ranges vs. iterators - Bayesian Filter detected spam

> > This would blow up, I believe. It gets expanded on both begin and end, causing exponential storage growth.
> >   

> Ok.  Right.  Incidentally, the storage required is not exponential 
> because it is not
> all needed at once, but I see your point.

You are right, running time is exponential, but storage is not.

--
Dr. Arno Schoedl · aschoedl <at> think-cell.com 
Technical Director 

think-cell Software GmbH · Invalidenstr. 34 · 10115 Berlin, Germany 
http://www.think-cell.com · phone +49-30-666473-10 · toll-free (US) +1-800-891-8091
Directors: Dr. Markus Hannebauer, Dr. Arno Schoedl · Amtsgericht Charlottenburg, HRB 85229

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Re: lifetime of ranges vs. iterators