digitalmars.D - putting more smarts into a == b
- Andrei Alexandrescu (20/20) Sep 26 2009 Consider two objects a and b with a of class type. Currently, the
- Jarrett Billingsley (9/28) Sep 26 2009 ion
- Christopher Wright (6/33) Sep 27 2009 C# operator overloads are of the form:
- bearophile (4/6) Sep 26 2009 I like it, is also saves some boring code.
- Ary Borenszweig (2/6) Sep 26 2009 Definitely!
- Robert Jacques (8/28) Sep 26 2009 I like this. I think optimizing away opEquals for identical objects woul...
- Andrei Alexandrescu (17/52) Sep 27 2009 This code has an inefficiency, it seems, because it makes a bit more
- Robert Jacques (6/56) Sep 27 2009 Are the extra branch and return statement faster? Besides, I thought the...
- Andrei Alexandrescu (4/68) Sep 27 2009 I'm trying to not rely on such...
- Frank Benoit (2/26) Sep 27 2009 What about interfaces?
- Frank Benoit (8/9) Sep 27 2009 I mean, this is a point that annoyes me a lot in D, that interfaces
- Andrei Alexandrescu (15/42) Sep 27 2009 Good question! What do they do now? I ran this:
- Frank Benoit (6/55) Sep 27 2009 Hm, i would have expected it not to compile, because A does not have
- Andrei Alexandrescu (3/58) Sep 27 2009 I think in D the cast is inserted automatically. Walter?
- Frank Benoit (3/11) Sep 27 2009 there is a related bug report
- Steven Schveighoffer (6/62) Sep 27 2009 From the assembly, it appears that the compiler is comparing the refere...
- Ary Borenszweig (39/99) Sep 27 2009 Using the compile-time view of Descent, if I have this code:
Consider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? Andrei
Sep 26 2009
On Sat, Sep 26, 2009 at 9:32 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Consider two objects a and b with a of class type. Currently, the express=iona =3D=3D b is blindly rewritten as a.opEquals(b). I argue it should be re=writteninto a call to an (imaginary/inlined) function equalObjects(a, b), with t=hefollowing definition: bool equalObjects(T, U)(T a, U b) if (is(T =3D=3D class)) { =A0 =A0static if (is(U =3D=3D class)) =A0 =A0{ =A0 =A0 =A0 =A0if (b is null) return a is null; =A0 =A0 =A0 =A0if (a is null) return b is null; =A0 =A0} =A0 =A0else =A0 =A0{ =A0 =A0 =A0 =A0enforce(a !is null); =A0 =A0} =A0 =A0return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals wi=thnull references. What do you think?Of course, with nonnull types, the check for null wouldn't even need to exiiiiiiiist.... ;)
Sep 26 2009
Jarrett Billingsley wrote:On Sat, Sep 26, 2009 at 9:32 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:public static ReturnType operator+(Arg1 arg1, Arg2 arg2) {} Object.operator== is defined to call arg1.Equals(arg2) if arg1 isn't null. But this isn't a feature of operator overloads.Consider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think?Of course, with nonnull types, the check for null wouldn't even need to exiiiiiiiist.... ;)How clever and insightful of you!
Sep 27 2009
Andrei Alexandrescu:This hoists the identity test outside the opEquals call and also deals with null references. What do you think?I like it, is also saves some boring code. Bye, bearophile
Sep 26 2009
Andrei Alexandrescu wrote:Consider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b)Definitely!
Sep 26 2009
On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Consider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiI like this. I think optimizing away opEquals for identical objects would also be a good idea: static if (is(U == class)) if(a is b || a is null || b is null) return a is b; else enforce(a !is null);
Sep 26 2009
Robert Jacques wrote:On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:This code has an inefficiency, it seems, because it makes a bit more checks than necessary (e.g. checks a is b twice). Let's simplify: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (a is b) return true; if (b is null || a is null) return false; } else { enforce(a !is null); } return a.opEquals(b); } AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiI like this. I think optimizing away opEquals for identical objects would also be a good idea: static if (is(U == class)) if(a is b || a is null || b is null) return a is b; else enforce(a !is null);
Sep 27 2009
On Sun, 27 Sep 2009 09:11:38 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Robert Jacques wrote:Are the extra branch and return statement faster? Besides, I thought the optimizer would cache a is b: auto a_is_b = a is b; if (a_is_b || b is null || a is null) return a_is_b;On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:This code has an inefficiency, it seems, because it makes a bit more checks than necessary (e.g. checks a is b twice). Let's simplify: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (a is b) return true; if (b is null || a is null) return false; } else { enforce(a !is null); } return a.opEquals(b); } AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiI like this. I think optimizing away opEquals for identical objects would also be a good idea: static if (is(U == class)) if(a is b || a is null || b is null) return a is b; else enforce(a !is null);
Sep 27 2009
Robert Jacques wrote:On Sun, 27 Sep 2009 09:11:38 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Just as fast. Short-circuit evaluation also generates code with branches.Robert Jacques wrote:Are the extra branch and return statement faster?On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:This code has an inefficiency, it seems, because it makes a bit more checks than necessary (e.g. checks a is b twice). Let's simplify: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (a is b) return true; if (b is null || a is null) return false; } else { enforce(a !is null); } return a.opEquals(b); } AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiI like this. I think optimizing away opEquals for identical objects would also be a good idea: static if (is(U == class)) if(a is b || a is null || b is null) return a is b; else enforce(a !is null);Besides, I thought the optimizer would cache a is b: auto a_is_b = a is b; if (a_is_b || b is null || a is null) return a_is_b;I'm trying to not rely on such... Andrei
Sep 27 2009
Andrei Alexandrescu schrieb:Consider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiWhat about interfaces?
Sep 27 2009
Frank Benoit schrieb:What about interfaces?I mean, this is a point that annoyes me a lot in D, that interfaces (instances) cannot be treated like objects. I cannot do if( someiface == someobj ){ ... } With that technique, the compiler could do a dynamic cast to Object in place, do the null checks and then call opEquals. Certainly, this should also work for the other methods of Objects like toHash, toString, ...
Sep 27 2009
Frank Benoit wrote:Andrei Alexandrescu schrieb:Good question! What do they do now? I ran this: interface A {} class Widget : A {} void main() { auto a = cast(A) new Widget; A b = null; writeln(a == b); writeln(b == a); } To my surprise, the program printed false twice. If I replace A with Widget inside main, the program prints false then crashes with the mythical segfault :o). So how are interfaces compared? AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiWhat about interfaces?
Sep 27 2009
Andrei Alexandrescu schrieb:Frank Benoit wrote:Hm, i would have expected it not to compile, because A does not have opEquals. In DWT, I cast always first to Object. Java> if( intf1.equals(intf2) ){ D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){Andrei Alexandrescu schrieb:Good question! What do they do now? I ran this: interface A {} class Widget : A {} void main() { auto a = cast(A) new Widget; A b = null; writeln(a == b); writeln(b == a); } To my surprise, the program printed false twice. If I replace A with Widget inside main, the program prints false then crashes with the mythical segfault :o). So how are interfaces compared? AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiWhat about interfaces?
Sep 27 2009
Frank Benoit wrote:Andrei Alexandrescu schrieb:I think in D the cast is inserted automatically. Walter? AndreiFrank Benoit wrote:Hm, i would have expected it not to compile, because A does not have opEquals. In DWT, I cast always first to Object. Java> if( intf1.equals(intf2) ){ D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){Andrei Alexandrescu schrieb:Good question! What do they do now? I ran this: interface A {} class Widget : A {} void main() { auto a = cast(A) new Widget; A b = null; writeln(a == b); writeln(b == a); } To my surprise, the program printed false twice. If I replace A with Widget inside main, the program prints false then crashes with the mythical segfault :o). So how are interfaces compared? AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiWhat about interfaces?
Sep 27 2009
Andrei Alexandrescu schrieb:Frank Benoit wrote:there is a related bug report http://d.puremagic.com/issues/show_bug.cgi?id=2794In DWT, I cast always first to Object. Java> if( intf1.equals(intf2) ){ D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){I think in D the cast is inserted automatically. Walter? Andrei
Sep 27 2009
On Sun, 27 Sep 2009 10:32:29 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Frank Benoit wrote:From the assembly, it appears that the compiler is comparing the reference values directly. Which is not what you want, you want the opEquals from Object. -SteveAndrei Alexandrescu schrieb:I think in D the cast is inserted automatically. Walter?Frank Benoit wrote:Hm, i would have expected it not to compile, because A does not have opEquals. In DWT, I cast always first to Object. Java> if( intf1.equals(intf2) ){ D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){Andrei Alexandrescu schrieb:Good question! What do they do now? I ran this: interface A {} class Widget : A {} void main() { auto a = cast(A) new Widget; A b = null; writeln(a == b); writeln(b == a); } To my surprise, the program printed false twice. If I replace A with Widget inside main, the program prints false then crashes with the mythical segfault :o). So how are interfaces compared? AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiWhat about interfaces?
Sep 27 2009
Andrei Alexandrescu wrote:Frank Benoit wrote:Using the compile-time view of Descent, if I have this code: --- interface I { } class C { } int main(char[][] args) { C c = new C(); I i = null; auto x = i == c; auto y = c == i; return 0; } --- the compiler turns it into: --- interface I { } class C: Object { } int main(char[][] args) { C c = new C; I i = null; int x = c.opEquals(cast(Object) i); int y = c.opEquals(cast(Object) i); return 0; } --- That's why it doesn't segfault. Debugging the code it turns out the logic is very simple, it's just applying operator overloading: (for the first comparison) "opEquals" is searched in "I". Since "opEquals" is not found in it, the compiler checks if "opEquals" is commutative. It is, so it searches "opEquals" in "C", finds it and does the rewrite. (more or less)Andrei Alexandrescu schrieb:I think in D the cast is inserted automatically. Walter? AndreiFrank Benoit wrote:Hm, i would have expected it not to compile, because A does not have opEquals. In DWT, I cast always first to Object. Java> if( intf1.equals(intf2) ){ D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){Andrei Alexandrescu schrieb:Good question! What do they do now? I ran this: interface A {} class Widget : A {} void main() { auto a = cast(A) new Widget; A b = null; writeln(a == b); writeln(b == a); } To my surprise, the program printed false twice. If I replace A with Widget inside main, the program prints false then crashes with the mythical segfault :o). So how are interfaces compared? AndreiConsider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: bool equalObjects(T, U)(T a, U b) if (is(T == class)) { static if (is(U == class)) { if (b is null) return a is null; if (a is null) return b is null; } else { enforce(a !is null); } return a.opEquals(b); } This hoists the identity test outside the opEquals call and also deals with null references. What do you think? AndreiWhat about interfaces?
Sep 27 2009