www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - interface opEquals

reply Antonio <antoniocabreraperez gmail.com> writes:
```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
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
parent reply Antonio <antoniocabreraperez gmail.com> writes:
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#L269
Replacing ```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
parent reply Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
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
parent reply Antonio <antoniocabreraperez gmail.com> writes:
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:
 ...
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.
Thangs Alexandru, It works. ...but why?
Nov 27 2023
parent Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
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