Revised RFC on range design for D2

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Tue Sep 16 18:46:38 PDT 2008


Sergey Gromov wrote:
> Andrei Alexandrescu <SeeWebsiteForEmail at erdani.org> wrote:
>> Sergey Gromov wrote:
>>> Andrei Alexandrescu <SeeWebsiteForEmail at erdani.org> wrote:
>>>> Sergey Gromov wrote:
>>>>> Andrei Alexandrescu <SeeWebsiteForEmail at erdani.org> wrote:
>>>>>> Given that in D const is transitive, we can't operate with const the 
>>>>>> same way C++ does. Consider something as trivial as a copy:
>>>>>>
>>>>>> Tgt copy(Src, Tgt)(Src src, Tgt tgt)
>>>>>> {
>>>>>>      for (; !src.done; src.next) tgt.put(src.tip);
>>>>>> }
>>>>>>
>>>>>> Problem is, you won't be able to copy e.g. an int[][] to another because 
>>>>>> the types aren't compatible.
>>>>> If Src is an input range you must make a deep copy.  This holds true for 
>>>>> your current design also.  So this algorithm is flawed and it's good if 
>>>>> it won't compile.
>>>> Great point. I need to sleep on this some more.
>>> Then I'll continue to think aloud.
>> [snip]
>>
>> Thanks for the code samples, they're cool. I hit a problem related to 
>> the return type of head. Consider writing a range that iterates two 
>> other ranges in lockstep. A very useful generic range. I started coding 
>> it like this:
>>
>> struct Lockstep(R0, R1)
>> {
>>      private R0 _r0;
>>      private R1 _r1;
>>      this(R0 r0, R1 r1)
>>      {
>>         _r0 = r0;
>>         _r1 = r1;
>>      }
>>
>>      bool empty()
>>      {
>>          return _r0.empty || _r1.empty;
>>      }
>>
>>      Tuple!(ElementType!(R0), ElementType!(R1)) head()
>>      {
>>          return tuple(_r0.head, _r1.head);
>>      }
>>
>>      void next()
>>      {
>>          _r0.next;
>>          _r1.next;
>>      }
>> }
> 
> Thinking aloud is fun.  :P
> 
> What you basically want to achieve is that
> 
> 	Lockstep l1, l2;
> 	l1.head = l2.head;
> 
> translates into
> 
> 	l1._r0.head = l2._r0.head;
> 	l1._r1.head = l2._r1.head;
> 
> all by itself.  Umm...  The simplest for a programmer would be compiler-
> supported multiple return values and multiple assignment:
> 
> 	ref typeof(R0.head), ref typeof(R1.head) head()
> 	{
> 	  return _r0.head, _r1.head;
> 	}
> 
> then
> 
> 	l1.head = l2.head
> 
> is actually
> 
> 	l1._r0.head, l1._r1.head = l2._r0.head, l2._r1.head;
> 
> I'm not expecting this to be implemented though.  Other methods, 
> including returning a Tulpe!(), all return a struct.  There's no use in 
> returning references there, even if we could, as long as structs are 
> bit-copied.  All the tricks with reference fields rely substantially on 
> the compiler performing specific actions on a per-field basis.

I figured a deceptively simple(r) solution. With apologies to Abba, I 
let the code speak:

/**
Defines a range that moves two given ranges in lockstep.
*/
struct Lockstep(R0, R1)
{
     struct ElementType
     {
         private R0 _r0;
         private R1 _r1;
         auto _0() { return _r0.head; }
         auto _1() { return _r1.head; }
     }
     private ElementType _e;

     this(R0 r0, R1 r1)
     {
        _e._r0 = r0;
        _e._r1 = r1;
     }

     bool empty()
     {
         return _e._r0.empty || _e._r1.empty;
     }

     ref ElementType head()
     {
         return _e;
     }

     void next()
     {
         _e._r0.next;
         _e._r1.next;
     }
}

Now when I write:

Lockstep.(RA, RB) r;
auto x = r.head._0;

then I simply access whatever RA.head returns, be it by reference or 
value. Problem solved.


Andrei


More information about the Digitalmars-d-announce mailing list