July 13, 2010
On 2010-07-12 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> T* getNext(R, T)(ref R range, ref T item);
> 
> Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item.
> 
> In all cases, when there are no more elements in the range, getNext returns null.

This can't be @safe. getNext would need to take a pointer out of the item reference, which isn't allowed in SafeD.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

July 13, 2010
On 2010-07-13 00:38:55 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> Yah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it:
> 
> T meh;
> T * neh;
> while ((neh = getNext(r, meh))) {
>     ... process *neh ...
> }

At this point what you want is to use a foreach loop. In fact, if foreach could be made to work with getNext (and it should), you'd rarely need to write that boilerplate code yourself.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

July 13, 2010
On Tue, 13 Jul 2010 08:54:07 -0400, Michel Fortin <michel.fortin@michelf.com> wrote:

> On 2010-07-13 00:38:55 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
>
>> Yah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it:
>>  T meh;
>> T * neh;
>> while ((neh = getNext(r, meh))) {
>>     ... process *neh ...
>> }
>
> At this point what you want is to use a foreach loop. In fact, if foreach could be made to work with getNext (and it should), you'd rarely need to write that boilerplate code yourself.

struct InputForeach(R) if(isInputRangeThatUsesGetNext!R)
{
   private R* range;
   this(ref R range) {this.range = &range;}
   int opApply(scope int delegate(ref ElementType!R) dg)
   {
      ElementType!R buf;
      ElementType!R *e;
      int result = 0;
      while(!result && (e = getNext(*range, buf)))
      {
         result = dg(*e);
      }
      return result;
   }
}

InputForeach!R inputForeach(R)(ref R r) if(isInputRangeThatUsesGetNext!R) { return InputForeach!R(r); }


foreach(e; inputForeach(r))
{
   ...
}

opApply to the rescue :)

Of course, native support would be better, unless the compiler decides to start inlining opApply.

-Steve
July 13, 2010
On 07/13/2010 05:09 AM, Shin Fujishiro wrote:
> Andrei Alexandrescu<SeeWebsiteForEmail@erdani.org>  wrote:
>> I've just had an idea that is so dark and devious, I was almost afraid
>> to try it. But it works like a charm. Consider:
>>
>> T * getNext(R, E)(ref R range,
>>                     ref E store = *(cast(E*) alloca(E.sizeof))
>> {
>>       ...
>> }
>>
>> With this, allocating a dummy buffer on caller's stack is automated, so
>> client code can just write:
>>
>> for (T * p; (p = getNext(r)); )  {
>>      ... process *p ...
>> }
>>
>> I feel dirty.
>
> How about a TLS variable?
>
> template temporary(T)
> {
>      static T temporary;
> }
> E* getNext(R, E)(ref R range, ref E store = temporary!E);

There's the classic problem of reusing the same temporary. Consider:

Range r1, r2;
ElementType!Range * p1, p2;
while ((p1 = getNext(r1)) && (p2 = getNext(r2)))
{
   ... oops ...
}

You need one temporary for each static occurrence of getNext.

Andrei
July 13, 2010
On 07/13/2010 07:39 AM, Steven Schveighoffer wrote:
> On Mon, 12 Jul 2010 23:48:05 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> I think I figured out a comfortable and all-encompassing means to
>> define a simplified interface for an input range.
>>
>> Currently input ranges need to define empty, front, and popFront. That
>> works but it's a bit heavy for simple input ranges. We've been
>> discussing simplified interfaces in this group but couldn't find one
>> that satisfied all use cases.
>>
>> Consider this:
>>
>> T* getNext(R, T)(ref R range, ref T item);
>>
>> Semantics: if the range wants to expose addresses of its elements, it
>> returns a pointer to the current element and also advances to the next
>> element. Otherwise (i.e. the range does not have or does not want to
>> expose addresses of its elements), the range fills "item" with the
>> current value, again moves on to the next value, and returns &item.
>>
>> In all cases, when there are no more elements in the range, getNext
>> returns null.
>>
>> getNext is easy to define for e.g. arrays and files. How does it
>> sound? Does it bring significant simplification?
>
> Yes, yes, yes!
>
> A question though -- whenever a pointer occurs, we always cringe,
> especially in safeD. will getNext be unsafe?

I need to discuss this with Walter, he mentioned that it wouldn't be difficult to allow certain uses of pointers in SafeD.

> BTW, I like the alloca thingy, that's really cool.
>
> One thing I just thought of, getNext should be split into two functions,
> the one you have, and:
>
> ElementType!R *getNext(R)(ref R range)
>
> To avoid having to supply the item or use alloca when the range is going
> to give you back a pointer to its internals anyways (tested with a
> template constraint).

Yup, good point.


Andrei
July 13, 2010
On 13.07.2010 06:38, Andrei Alexandrescu wrote:
> Yah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it:
> 
> T meh;
> T * neh;
> while ((neh = getNext(r, meh))) {
>    ... process *neh ...
> }
> 

One way to make this slightly easier would be allowing auto in loop bodies, same as it works in if - ie.

T meh;
while (auto neh = getNext(r, meh)) { }
July 13, 2010
Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> There's the classic problem of reusing the same temporary. Consider:
> 
> Range r1, r2;
> ElementType!Range * p1, p2;
> while ((p1 = getNext(r1)) && (p2 = getNext(r2)))
> {
>     ... oops ...
> }
> 
> You need one temporary for each static occurrence of getNext.

To my shame, I missed it!

Anyways, I like getNext().  LockingTextReader definitely needs it.


Shin
July 14, 2010
Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range.
> 
> Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases.
> 
> Consider this:
> 
> T* getNext(R, T)(ref R range, ref T item);
> 
> Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item.
> 
> In all cases, when there are no more elements in the range, getNext returns null.
> 
> getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification?

I gave it a try, and it fairly simplified range implementation.
    http://gist.github.com/474562
I think it's good.

(1) But how does it represent an infinite range?

(2) Should user code use getNext for input ranges (like put for output
ranges)?  For example:

void doSomething(R)(R input)
{
    // read first character if any
    dchar* p = getNext!dchar(input);
    if (p == null)
        return;
    dchar a = *p;

    // read subsequent characters...
    dchar b = *enforce( getNext!dchar(input) );
    ...
}


Shin
July 23, 2010
Dnia 13-07-2010 o 06:38:55 Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> napisał(a):

> I've just had an idea that is so dark and devious, I was almost afraid to try it. But it works like a charm. Consider:
>  T * getNext(R, E)(ref R range,
>                   ref E store = *(cast(E*) alloca(E.sizeof))
> {
>     ...
> }
>  With this, allocating a dummy buffer on caller's stack is automated, so client code can just write:
>  for (T * p; (p = getNext(r)); )  {
>    ... process *p ...
> }
>  I feel dirty.

What's wrong with foreach(e; r) that you want to play dirty? The compiler is here to help.

Tomek
July 09, 2012
> I've just had an idea that is so dark and devious, I was almost afraid to try it. But it works like a charm. Consider:
>
> T * getNext(R, E)(ref R range,
>                   ref E store = *(cast(E*) alloca(E.sizeof))
> {
>     ...
> }

I don't know if this proposal went anywhere since 2010, but it occurs to me that there is a hidden danger here. alloca will allocate a sequence of separate temporaries. If the collection is large, the stack will overflow, and the client might not have a clue what happened.