digitalmars.D - opEquals the template?
- Steven Schveighoffer (33/33) Sep 30 2011 There is a debate in a sub-thread about logical const on whether
- kenji hara (19/52) Sep 30 2011 a
- Steven Schveighoffer (6/67) Sep 30 2011 Yes, you did almost exactly what I did, except I made it valid to call
- kenji hara (57/61) Sep 30 2011 Your suggestion makes object.opEquals more simple. It is good.
- kenji hara (3/3) Sep 30 2011 Additionally, I think we should change the "HiddenFuncError" from
- Andrej Mitrovic (19/19) Oct 01 2011 Is opEquals not allowed to be a template in a struct? Because I was
- Andrej Mitrovic (2/2) Oct 01 2011 *correction*, the error code is return by the application itself. I'm
- Andrej Mitrovic (3/7) Oct 01 2011 This right here gets an award of some kind. I'll show myself the exit
- Jonathan M Davis (6/46) Sep 30 2011 Sounds good to me. Even better, it would make it possible to have opEqua...
- Jonathan M Davis (7/30) Oct 01 2011 Struct's are a completely different issue. opEquals for structs is not h...
- Andrej Mitrovic (3/3) Oct 01 2011 Ok so it seems there was a restriction before pull 373/70. I'll hold
- Jonathan M Davis (5/15) Oct 01 2011 I don't know. If you're _that_ confused, you'll probably end up in the
- Andrej Mitrovic (9/9) Oct 01 2011 Guilty of copy-pasting and editing on the fly. Well originally it was
- kenji hara (24/57) Oct 15 2011 I have updated my pull requests.
There is a debate in a sub-thread about logical const on whether Object.opEquals should be const or mutable. Right now, it's mutable. This disallows comparison between const objects (except it is technically allowed because the compiler ignores const correctness for this). I think it should be const. However, making it const disallows opEquals functions which mutate the object (for lazy loading or caching purposes). I question that this is a major concern, but I have discovered an easy fix that appeases all. If we make opEquals a template (not Object.opEquals, but the free function opEquals(Object lhs, Object rhs) ), then the template has access to the full type information of the derived object. This means that Object.opEquals can be exclusively const, and you can overload this with a mutable version, and everything just works. I tried it out by making my copy of druntime use a template (it's actually very few lines change). Although I needed to use casts to compile some parts of phobos (some opEquals calls depend on the compiler ignoring const guarantees as mentioned above), when compiling a simple test program, I can prove it works without compiler changes. Along with the ability to have both const and non-const (and even technically immutable) opEquals, we have the following drawbacks and benefits: 1. template bloat. Every combination of two object types being compared will generate a new template. 2. inlining of opEquals. Both the free function template, and possibly a final-declared opEquals on a dervied object could be inlined. 3. possible specialization of opEquals(MyDerivedType obj). I don't remember exactly the overloading rules, so this may be impossible. 4. Fixes the issue with interface comparison (see bug http://d.puremagic.com/issues/show_bug.cgi?id=4088), not in the best way, but it would be better than the current situation. What do people think? -Steve
Sep 30 2011
2011/10/1 Steven Schveighoffer <schveiguy yahoo.com>:There is a debate in a sub-thread about logical const on whether Object.opEquals should be const or mutable. Right now, it's mutable. =A0This disallows comparison between const objec=ts(except it is technically allowed because the compiler ignores const correctness for this). I think it should be const. However, making it const disallows opEquals functions which mutate the object (for lazy loading or caching purposes). =A0I question that this is=amajor concern, but I have discovered an easy fix that appeases all. If we make opEquals a template (not Object.opEquals, but the free functio=nopEquals(Object lhs, Object rhs) ), then the template has access to the f=ulltype information of the derived object. =A0This means that Object.opEqual=s canbe exclusively const, and you can overload this with a mutable version, a=ndeverything just works. I tried it out by making my copy of druntime use a template (it's actuall=yvery few lines change). =A0Although I needed to use casts to compile some parts of phobos (some opEquals calls depend on the compiler ignoring cons=tguarantees as mentioned above), when compiling a simple test program, I c=anprove it works without compiler changes. Along with the ability to have both const and non-const (and even technically immutable) opEquals, we have the following drawbacks and benefits: 1. template bloat. =A0Every combination of two object types being compare=dwill generate a new template. 2. inlining of opEquals. =A0Both the free function template, and possibly=afinal-declared opEquals on a dervied object could be inlined. 3. possible specialization of opEquals(MyDerivedType obj). =A0I don't rem=emberexactly the overloading rules, so this may be impossible. 4. Fixes the issue with interface comparison (see bug http://d.puremagic.com/issues/show_bug.cgi?id=3D4088), not in the best wa=y,but it would be better than the current situation. What do people think? -SteveI think your suggestion is almost same as mine. Discussions: https://github.com/D-Programming-Language/phobos/pull/262 Implementations: https://github.com/D-Programming-Language/druntime/pull/72 Kenji Hara
Sep 30 2011
On Fri, 30 Sep 2011 11:48:02 -0400, kenji hara <k.hara.pg gmail.com> wrote:2011/10/1 Steven Schveighoffer <schveiguy yahoo.com>:Yes, you did almost exactly what I did, except I made it valid to call mutable opEquals for two unlike objects. That is, I think there is no point for the static if in the middle, just do lhs.opEquals(rhs) && rhs.opEquals(lhs) for all cases. -SteveThere is a debate in a sub-thread about logical const on whether Object.opEquals should be const or mutable. Right now, it's mutable. This disallows comparison between const objects (except it is technically allowed because the compiler ignores const correctness for this). I think it should be const. However, making it const disallows opEquals functions which mutate the object (for lazy loading or caching purposes). I question that this is a major concern, but I have discovered an easy fix that appeases all. If we make opEquals a template (not Object.opEquals, but the free function opEquals(Object lhs, Object rhs) ), then the template has access to the full type information of the derived object. This means that Object.opEquals can be exclusively const, and you can overload this with a mutable version, and everything just works. I tried it out by making my copy of druntime use a template (it's actually very few lines change). Although I needed to use casts to compile some parts of phobos (some opEquals calls depend on the compiler ignoring const guarantees as mentioned above), when compiling a simple test program, I can prove it works without compiler changes. Along with the ability to have both const and non-const (and even technically immutable) opEquals, we have the following drawbacks and benefits: 1. template bloat. Every combination of two object types being compared will generate a new template. 2. inlining of opEquals. Both the free function template, and possibly a final-declared opEquals on a dervied object could be inlined. 3. possible specialization of opEquals(MyDerivedType obj). I don't remember exactly the overloading rules, so this may be impossible. 4. Fixes the issue with interface comparison (see bug http://d.puremagic.com/issues/show_bug.cgi?id=4088), not in the best way, but it would be better than the current situation. What do people think? -SteveI think your suggestion is almost same as mine. Discussions: https://github.com/D-Programming-Language/phobos/pull/262 Implementations: https://github.com/D-Programming-Language/druntime/pull/72
Sep 30 2011
2011/10/1 Steven Schveighoffer <schveiguy yahoo.com>:Yes, you did almost exactly what I did, except I made it valid to call mutable opEquals for two unlike objects. That is, I think there is no point for the static if in the middle, just do lhs.opEquals(rhs) && rhs.opEquals(lhs) for all cases.Your suggestion makes object.opEquals more simple. It is good. I worried that direct comparison partially breaks "Liskov substitution principle", but D can detect "HiddenFuncError" in compile time, then it is not so bug prone. ---- // comparing with any other types returns true class X { bool opEquals(const Object o) const { return true; } } // provide special opEquals against class X class C { bool opEquals(const Object o) const { return true; } bool opEquals(const X o) const { return true; } } // provide special opEquals against class X too, and rewrite its behavior class D : C { override bool opEquals(const Object o) const { if (auto x = cast(const X) o) return opEquals(x); return true; } override bool opEquals(const X o) const pure nothrow { return false; } // In class D, we should define two opEquals, otherwise raises HiddenFuncError in compile time. } void main() { auto x = new X(); { auto c = new C(); assert(c == x); auto oc = c, ox = x; assert(oc == ox); } { auto d = new D(); assert(d != x); // direct comparison C cd = d; assert(cd is d); assert(cd != x); // instance based behavior, it is consistent Object od = d, ox = x; assert(od != x); // opEquals(const Object) const is used, it is required the right definition of opEquals in class D } } ---- We still define the two opEquals properly (e.g. in class D), but I think it is right cost to specialization. Kenji Hara
Sep 30 2011
Additionally, I think we should change the "HiddenFuncError" from deprecated feature to removed feature. Kenji Hara
Sep 30 2011
Is opEquals not allowed to be a template in a struct? Because I was using it in my library, and it sort-of worked, up until it started causing some really weird bugs where a ctor was called instead of opCast. This results in an error code returned by the compiler: struct Foo { bool opEquals(T)(ref const T rhs) const { return this == this; } } void main() { auto foo = Foo(); assert(foo == foo); } Exit code: -1073741819 And yet it worked fine in a larger module until I've hit the random buggy behavior.. I don't understand..
Oct 01 2011
*correction*, the error code is return by the application itself. I'm filing this anyway.
Oct 01 2011
On 10/2/11, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:bool opEquals(T)(ref const T rhs) const { return this == this; }This right here gets an award of some kind. I'll show myself the exit doors. LOL.
Oct 01 2011
On Friday, September 30, 2011 11:35:51 Steven Schveighoffer wrote:There is a debate in a sub-thread about logical const on whether Object.opEquals should be const or mutable. Right now, it's mutable. This disallows comparison between const objects (except it is technically allowed because the compiler ignores const correctness for this). I think it should be const. However, making it const disallows opEquals functions which mutate the object (for lazy loading or caching purposes). I question that this is a major concern, but I have discovered an easy fix that appeases all. If we make opEquals a template (not Object.opEquals, but the free function opEquals(Object lhs, Object rhs) ), then the template has access to the full type information of the derived object. This means that Object.opEquals can be exclusively const, and you can overload this with a mutable version, and everything just works. I tried it out by making my copy of druntime use a template (it's actually very few lines change). Although I needed to use casts to compile some parts of phobos (some opEquals calls depend on the compiler ignoring const guarantees as mentioned above), when compiling a simple test program, I can prove it works without compiler changes. Along with the ability to have both const and non-const (and even technically immutable) opEquals, we have the following drawbacks and benefits: 1. template bloat. Every combination of two object types being compared will generate a new template. 2. inlining of opEquals. Both the free function template, and possibly a final-declared opEquals on a dervied object could be inlined. 3. possible specialization of opEquals(MyDerivedType obj). I don't remember exactly the overloading rules, so this may be impossible. 4. Fixes the issue with interface comparison (see bug http://d.puremagic.com/issues/show_bug.cgi?id=4088), not in the best way, but it would be better than the current situation. What do people think?Sounds good to me. Even better, it would make it possible to have opEquals be pure too, since a derived class could make it pure and then the opEquals template could be pure, allowing for the use of == in pure functions when dealing with classes. - Jonathan M Davis
Sep 30 2011
On Sunday, October 02, 2011 03:39:29 Andrej Mitrovic wrote:Is opEquals not allowed to be a template in a struct? Because I was using it in my library, and it sort-of worked, up until it started causing some really weird bugs where a ctor was called instead of opCast. This results in an error code returned by the compiler: struct Foo { bool opEquals(T)(ref const T rhs) const { return this == this; } } void main() { auto foo = Foo(); assert(foo == foo); } Exit code: -1073741819 And yet it worked fine in a larger module until I've hit the random buggy behavior.. I don't understand..Struct's are a completely different issue. opEquals for structs is not handled the same way as it is for classes. Their biggest issue is http://d.puremagic.com/issues/show_bug.cgi?id=3659 and that was finally fixed a couple of days ago. There may be further bugs with it, but the issue that Steven is bringing up is about classes, not structs. - Jonathan M Davis
Oct 01 2011
Ok so it seems there was a restriction before pull 373/70. I'll hold on to any bug reports until I've checked these. Sorry for intruding, didn't want to create a whole new topic since it's relevant (kinda).
Oct 01 2011
On Sunday, October 02, 2011 04:43:34 Andrej Mitrovic wrote:On 10/2/11, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:I don't know. If you're _that_ confused, you'll probably end up in the bathroom instead. ;) LOL. Yeah well, we all make dumb mistakes from time to time. - Jonathan M Davisbool opEquals(T)(ref const T rhs) const { return this == this; }This right here gets an award of some kind. I'll show myself the exit doors. LOL.
Oct 01 2011
Guilty of copy-pasting and editing on the fly. Well originally it was a templated overload of opEquals that used opCast behind-the-scenes to convert `rhs` to a native type and then use another opEquals to do the actual comparison: return this == cast(typeof(this))rhs; It might not be the most efficient code, but it saves me time from typing. Due to some funky bug, that cast right there actually calls a ctor that can take `rhs` as a parameter. I'll just have to wait for 2.056 so templated opEquals can work properly.
Oct 01 2011
I have updated my pull requests. https://github.com/D-Programming-Language/dmd/pull/387 https://github.com/D-Programming-Language/druntime/pull/72 https://github.com/D-Programming-Language/phobos/pull/262 New improvements: - Enable direct comparison with mutable opEquals. - Enable interface comparison for fixing issue 4088. - Add changes for fixing issue 3789. It requires const equality comparison for member classes. Kenji Hara 2011/10/1 Steven Schveighoffer <schveiguy yahoo.com>:There is a debate in a sub-thread about logical const on whether Object.opEquals should be const or mutable. Right now, it's mutable. =A0This disallows comparison between const objec=ts(except it is technically allowed because the compiler ignores const correctness for this). I think it should be const. However, making it const disallows opEquals functions which mutate the object (for lazy loading or caching purposes). =A0I question that this is=amajor concern, but I have discovered an easy fix that appeases all. If we make opEquals a template (not Object.opEquals, but the free functio=nopEquals(Object lhs, Object rhs) ), then the template has access to the f=ulltype information of the derived object. =A0This means that Object.opEqual=s canbe exclusively const, and you can overload this with a mutable version, a=ndeverything just works. I tried it out by making my copy of druntime use a template (it's actuall=yvery few lines change). =A0Although I needed to use casts to compile some parts of phobos (some opEquals calls depend on the compiler ignoring cons=tguarantees as mentioned above), when compiling a simple test program, I c=anprove it works without compiler changes. Along with the ability to have both const and non-const (and even technically immutable) opEquals, we have the following drawbacks and benefits: 1. template bloat. =A0Every combination of two object types being compare=dwill generate a new template. 2. inlining of opEquals. =A0Both the free function template, and possibly=afinal-declared opEquals on a dervied object could be inlined. 3. possible specialization of opEquals(MyDerivedType obj). =A0I don't rem=emberexactly the overloading rules, so this may be impossible. 4. Fixes the issue with interface comparison (see bug http://d.puremagic.com/issues/show_bug.cgi?id=3D4088), not in the best wa=y,but it would be better than the current situation. What do people think? -Steve
Oct 15 2011