A opIndexUnary quiz

Peter Summerland p.summerland at gmail.com
Wed Jan 2 16:08:11 PST 2013


On Wednesday, 2 January 2013 at 03:52:21 UTC, bearophile wrote:
> Era Scarecrow:
>
>> Well I see that you have opIndexUnary twice; According to the 
>> manual you wouldn't need as it would rewrite the code so you 
>> only need it once;
>
> And as you have seen if you remove the useles opIndexRight the 
> program keeps compiling with no errors and keeps asserting at 
> run-time:
>
>
>
> struct Foo {
>     int x;
>     alias this = x;
> }
>
> class Bar {
>     Foo[] data;
>
>     this() {
>         data.length = 10;
>     }
>
>     Foo opIndex(uint i) {
>         return data[i];
>     }
>
>     void opIndexUnary(string op)(uint i) if (op == "++") {
>         data[i]++;
>     }
> }
>
> void main() {
>     auto barfoo = new Bar;
>     ++barfoo[3];
>     assert(barfoo.data[3] == 1);
>     barfoo[3]++;
>     assert(barfoo.data[3] == 2);
> }
>
>
> Bye,
> bearophile

I am really just guessing, but it appears that whatever the post 
increment op gets rewritten as takes either a reference to or a 
copy of what is being incremented and it involves opIndex, not 
opIndexUnary as suggested by TDPL p. 378 and p. 369.

If opIndex is changed to return ref Foo, everything works. (I had 
change alias this = x to alias x this - 2.60 64 bit). Here is 
code that shows when opIndexUnary and opIndex is called and when 
the assertion fails for version(B) - with ref and version(A) 
without ref.

Note that TDPL recommends *not* having opIndex return a ref (p. 
378). Can we have out cake and eat it too?


import std.stdio, std.conv;

// -----------------
struct Foo {
   int x;
   alias x this;
   string toString() {
     return to!string(x);
   }
}

class Bar {
   Foo[] data;

   this() {
     data.length = 10;
   }

   version( A ) {
     Foo opIndex(uint i) {
       writefln("opIndex data[i]: %s",data[i]);
       return data[i];
     }
   }

   version( B ) {
     ref Foo opIndex(uint i) {
       writefln("opIndex data[i]: %s",data[i]);
       return data[i];
     }
   }

   Foo opIndexUnary(string op)(uint i) if (op == "++") {
     ++data[i];
     writefln("opIndexUnary data[i]: %s",data[i]);
     return data[i];
   }

}

void main() {
   auto barfoo = new Bar;
   assert(++barfoo[3] == 1);
   writeln("after ++barfoo[3]");
   assert(barfoo[3]++ == 1);
   writeln("after barfoo[3]++");
   assert(barfoo[3] == 2);             // Line 47 - fails for 
version(A)
   writeln("after barfoo[3]");
}
// -----------------


/*

$> dmd opndx.d -version=B
$> ./opndx
opIndexUnary data[i]: 1
after ++barfoo[3]
opIndex data[i]: 1
after barfoo[3]++
opIndex data[i]: 2
after barfoo[3]


$> dmd opndx.d -version=A
$> ./opndx
opIndexUnary data[i]: 1
after ++barfoo[3]
opIndex data[i]: 1
after barfoo[3]++
opIndex data[i]: 1
core.exception.AssertError at opndx(47): Assertion failure

*/



More information about the Digitalmars-d-learn mailing list