On Sun, 14 Mar 2010 10:55:41 -0400, bearophile <bearophileh...@lycos.com> wrote:

In the following little programs can you help me tell apart my errors from bugs in dmd?

I'll preface my comments with a general comment.

operator functions themselves are not, and should not be, treated specially by the compiler. You can write opBinary(int x) for all the compiler cares, and it's a valid function. opX is not a keyword or specially treated symbol.

The way D works (and has always worked, even in D1) is that the operators themselves (e.g. '+' '-', etc.) are transformed into a function call.

The benefit to all this is that keyword pollution is kept to a minimum, and the compiler is extremely simplified. Imagine if opBinary is treated as a keyword, you need a special cases littered throughout the code, special grammar, etc. If opBinary is just another symbol, it's already covered by the compiler. The only place it's special int the compiler is when it's *generating* opBinary, not when it's *parsing* opBinary.


1) Error in the documentation in this page:
http://www.digitalmars.com/d/2.0/operatoroverloading.html

This:

struct S {
    int opEquals(ref const S s) { ... }
}

Probably has to become like:

struct S {
    const bool opEquals(ref const(S) s) { ... }
}

I think there is no restriction on what opEquals in structs. However, where opEquals is a virtual function, a const-correct signature must be required in order to override the base function. I filed a recent bug on it, where in fact you can change an object through a const reference because the compiler forces it.


-------------------------------

2) This code runs:


import std.c.stdio: printf;
struct Foo {
    int data;
    int opEquals(T:Foo)(T other) {
        printf("A");
        return this.data == other.data;
    }
}
void main() {
    int r = Foo(5) == Foo(5);
}


But I think dmd has to raise a c error, and require something like:
const bool opEquals(T:Foo)(ref const(Foo) other) {
Or:
const bool opEquals(T:Foo)(const(Foo) other) {
etc.

Again, opEquals is not a special function. Comparing two const Foos will correctly result in a compiler error.


-------------------------------

3) This code runs:


struct Foo {
    int data;
bool opBinary(string Op:"==")(ref const Foo other) { // wrong: opEquals
        return this.data == other.data;
    }
bool opBinary(string Op)(ref const Foo other) if (Op == "0") { // wrong
        return this.data == other.data;
    }
}
void main() {}


But that can cause bugs: it's easy for programmers to forget to use opEquals instead of opBinary("==").

Again, opBinary and opEquals are simply functions. There is nothing special about their existence, just about how the compiler rewrites the operators into calling those functions. The fact that opBinary does not get called is the same as defining a function that does not get called. These kinds of errors are appropriate for a lint tool.

And generally I'd like dmd to raise an error when silly ops are defined, like that "0". It's important to avoid many bugs in the operator overloading usage.

Note that opBinary can be directly called via:

myfoo.opBinary!("0")(other);


-------------------------------

4) This program doesn't compile:


struct Foo {
    int x;
    Foo opUnary(string op:"++")() {
        this.x++;
        return this;
    }
}
void main() {
    Foo f = Foo(5);
    f++; // line 10
}


Prints:
temp.d(10): Error: var has no effect in expression (__tmp1)

This is a bug, I think introduced by a recent change that makes f++ an lvalue. Please file a bugzilla report.

-------------------------------

5) Here an implicit cast to uint or int can be handy, to avoid the cast (to be able to create a generic "fake int" struct), is this possible?


struct Foo {
    int x;
    int opCast(T:int)() {
        return this.x;
    }
}
void main() {
    Foo f = Foo(5);
    auto a1 = new int[cast(int)f]; // OK
    auto a2 = new int[f];          // ERR
}


Philippe Sigaud in the digitalmars.D.learn newsgroup suggests an opImplicitCast.

opImplicitCast was abandoned for the more versatile alias this. Something like this should work:

struct Foo {
  int x;
  int opCast(T:int)() { return this.x; };
  alias opCast this;
}

-Steve

Reply via email to