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