ref tuples

Artur Skawina art.08.09 at gmail.com
Wed Jul 3 13:04:15 PDT 2013


On 07/03/13 19:10, Brad Anderson wrote:
> On Wednesday, 3 July 2013 at 16:35:18 UTC, Artur Skawina wrote:
>> On 07/03/13 18:29, Brad Anderson wrote:
>>> On Wednesday, 3 July 2013 at 11:54:39 UTC, Artur Skawina wrote:
>>>> On 07/03/13 02:22, Brad Anderson wrote:
>>>>> C++11's std::tuple includes a function std::tie that takes references to the arguments and returns a tuple that maintains the references to the arguments.
>>>>>
>>>>> Along with the usual cases where you'd want reference semantics it also enables this interesting construct for unpacking tuples.
>>>>>
>>>>> int a, b;
>>>>> tie(a, b) = make_tuple(1, 2);
>>>>>
>>>>> assert(a == 1 && b == 2);
>>>>>
>>>>> Is there any way to do something similar with std.typecons.Tuple?
>>>>
>>>> Well, aliases can be used to get a similar effect.
>>>>
>>>>    template tie(A...) { alias tie = A; }
>>>>    tie!(a, b) = tuple(1, 2);
>>>
>>> That won't work.  a and b aren't held as references (also you passed them as type parameters :P).
>>
>> Try it...
>>
>> And, yes, the fact that 'A...' template parms accept symbols
>> is not exactly obvious. But it's much more useful that way.
>>
>> artur
> 
> Huh, I had no idea you could do something like that. I stand corrected. Thanks.
> 
> That does get tie = working but doesn't work if you want to pass it around which is actually more at the heart of what I'm interested in.
> 
> To get straight to the point, I was playing around with implementing bearophile's enumerate() feature request (something I've wanted myself).  Both his posted solution [1] and my own quick testing hack (auto enumerate(Range)(Range r) { return zip(sequence!"n"(), r); }) lose the ability to do ref elements in foreach that can modify the source range:
> 
> ---
> auto a = ["a", "b", "c"];
> foreach(i, ref item; a.enumerate())
>   item = to!string(i);
> 
> assert(a == ["0", "1", "2"]); // fails, a is still ["a", "b", "c"]
> ---
> 
> They don't work because both the tuples he returns from front and the tuples zip creates aren't references to the originals so you are just changing the copy stored in the tuple.
> 
> Something like "alias RefIntTuple = Tuple!(ref int, ref int);" gives a compiler error (Error: expression expected, not 'ref').
> 
> 1. http://d.puremagic.com/issues/show_bug.cgi?id=5550#c2

D does not yet have proper ref types, which means a lot of things
are not possible, at least not directly. And a lot of hacks are
required to achieve certain effects. Anyway, the following seems
to work - it's bearophiles code from the mentioned bugzilla entry
with some tweaks.

It's dirty enough, so I shouldn't be posting this on a 'learn'
ML... It's meant more as an illustration of the language deficiencies.
Please do not use anything like this. :)


   import std.stdio, std.algorithm, std.range, std.typecons, std.traits, std.array;

   struct RefHack(T) {
      T* ptr;
      ref get() @property { return *ptr; }
      alias get this;
   }
   auto refHack(T)(ref T a) { return RefHack!T(&a); }

   struct Enumerate(R) {
       R r;
       int i;

       @property bool empty() { return r.empty; }

       @property Tuple!(typeof(this.i), typeof(refHack(r.front))) front() {
           return typeof(return)(i, refHack(r.front));
       }

       void popFront() {
           this.r.popFront();
           this.i++;
       }
   }

   Enumerate!R enumerate(R)(R range, int start=0) if (isInputRange!R) {
       return Enumerate!R(range, start);
   }

   void main() {
       auto flags = [0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1];

       flags.enumerate(2).filter!q{!a[1]}().map!q{a[0]}().writeln();

       {
          import std.conv;
          auto a = ["a", "b", "c"];

          foreach(i, ref item; a.enumerate())
             item = to!string(i);

          assert(a == ["0", "1", "2"]);
       }
   }


artur


More information about the Digitalmars-d-learn mailing list