digitalmars.D.learn - interface opEquals
- Antonio (49/49) Nov 23 2023 ```d
- Jonathan M Davis (18/21) Nov 23 2023 I'd have to take the time to study your code in detail to see whether wh...
- Antonio (25/37) Nov 24 2023 My apologies... I should have proposed a more specific example.
- Alexandru Ermicioi (7/8) Nov 24 2023 Dunno if this might help, but I noticed that `==` sometimes calls
- Antonio (5/14) Nov 27 2023 Thangs Alexandru,
- Alexandru Ermicioi (13/14) Nov 27 2023 All classes (and interfaces I think), at root of inheritance have
```d interface IOpt(T) { T value(); bool empty(); bool opEquals(IOpt!T other); } class None(T) : IOpt!T { bool empty() => true; T value(){ throw new Exception("None has not a value"); } bool opEquals(IOpt!T other)=>other.empty; } class Some(T) : IOpt!T { this(T value) { this._value = value; } bool empty() => false; T value()=> _value; bool opEquals(IOpt!T other)=>!other.empty && other.value==_value; private T _value; } IOpt!T some(T)(T v)=>new Some!T(v); IOpt!T none(T)()=>new None!T; void main() { assert(new Some!int(1) == new Some!int(1)); assert(new None!int == new None!int); assert(none!int.opEquals(none!int)); assert(none!int == none!int); } ``` It compiles, but last assertion ```assert(none!int == none!int);``` fails ``` core.exception.AssertError testiface.d(33): Assertion failure ``` To avoid "extrange effects" I test an alternative equality that fails too: ```d assert( (cast (IOpt!int) new None!int) == (cast (IOpt!int) new None!int)); ``` What seems strange to me is that ```none!int.opEquals(none!int)``` works properly. **Questions** * Why, when applied to interface, ```opEquals``` called directly behavior is not the same that when calling ```==``` ? * Is it the expected behaviour?
Nov 23 2023
On Thursday, November 23, 2023 2:20:25 PM MST Antonio via Digitalmars-d-learn wrote:* Why, when applied to interface, ```opEquals``` called directly behavior is not the same that when calling ```==``` ? * Is it the expected behaviour?I'd have to take the time to study your code in detail to see whether what exactly you're seeing makes sense or not, but it's not expected that normal D code will call opEquals directly, and for classes, == does more than call lhs.opEquals(rhs). It does additional stuff to try to have the correct behavior for equality without you having to code it all up yourself in opEquals. == on classes results in the free function, opEquals, in object.d being called. That function does a variety of checks such as checking whether either reference is null (to avoid dereferencing null) and using is to compare the address of the class references first (to avoid calling the class' opEquals if both references are to the same object). It also makes sure that both rhs.opEquals(lhs) and lhs.opEquals(rhs) are true for == to be true to avoid subtle bugs that can come into play when comparing a base class against a derived class. https://github.com/dlang/dmd/blob/master/druntime/src/object.d#L269 - Jonathan M Davis
Nov 23 2023
On Thursday, 23 November 2023 at 21:52:56 UTC, Jonathan M Davis wrote:I'd have to take the time to study your code in detail to see whether what exactly you're seeing makes sense or not ...My apologies... I should have proposed a more specific example. ```d interface IOpt { bool opEquals(IOpt other) const safe pure; } class None : IOpt { bool opEquals(IOpt other) const safe pure => true; } void main() { None a = new None(), b = new None(); IOpt a2 = a, b2 = b; assert(a == b); assert(a2 == a); assert(b2 == b); assert(a2 == b2); // fails!!! } ```== on classes results in the free function, opEquals, in object.d being called. That function does a variety of checks such as checking whether either reference is null (to avoid dereferencing null) and using is to compare the address of the class references first (to avoid calling the class' opEquals if both references are to the same object). It also makes sure that both rhs.opEquals(lhs) and lhs.opEquals(rhs) are true for == to be true to avoid subtle bugs that can come into play when comparing a base class against a derived class. https://github.com/dlang/dmd/blob/master/druntime/src/object.d#L269Replacing ```interface``` by ```abstract class``` removes the problem. But I have not enough D knowledge to discover why interface version is not working Thanks Antonio
Nov 24 2023
On Friday, 24 November 2023 at 17:39:10 UTC, Antonio wrote:...Dunno if this might help, but I noticed that `==` sometimes calls `opEquals(const Object) const` instead of overload defined on class/interface, you might try and override it as well, and delegate to your overload that deals directly with `IOpt`. Best regards, Alexandru.
Nov 24 2023
On Saturday, 25 November 2023 at 01:15:34 UTC, Alexandru Ermicioi wrote:On Friday, 24 November 2023 at 17:39:10 UTC, Antonio wrote:Thangs Alexandru, It works. ...but why?...Dunno if this might help, but I noticed that `==` sometimes calls `opEquals(const Object) const` instead of overload defined on class/interface, you might try and override it as well, and delegate to your overload that deals directly with `IOpt`. Best regards, Alexandru.
Nov 27 2023
On Monday, 27 November 2023 at 09:53:48 UTC, Antonio wrote:...but why?All classes (and interfaces I think), at root of inheritance have `Object` class, this class defines couple of generic methods, you can find this class in object.d btw. One of those methods is `bool opEquals(const Object rhs) const`, therefore if you try to call opEquals, it could be possible that in some cricumstances instead of your overload, one defined on Object is picked up, which checks only if rhs is same object as the one invoked upon. Btw, dunno how the rules for overload set resolution on opEquals work, so someone else should check whther this is a bug or expected behavior. Best regards, Alexandru.
Nov 27 2023