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@opndx(47): Assertion failure

*/

Reply via email to