## digitalmars.D - "==" not symmetric

• Lionello Lunesu (20/20) May 23 2006 I was checking some sources in the 'internal' folder and saw this:
• Unknown W. Brackets (29/46) May 23 2006 Forgive my rusty logic skills... how do I read your table? As far as I
• Lionello Lunesu (14/39) May 23 2006 'bad'? I don't think you can tell whether a pointer is bad or valid. I t...
• Unknown W. Brackets (28/59) May 23 2006 Bad means a pointer to an invalid object. A bad pointer. I was trying
• Lionello Lunesu (7/83) May 24 2006 Going thirty and I'm still mixing left and right :S
"Lionello Lunesu" <lionello lunesu.remove.com> writes:
```I was checking some sources in the 'internal' folder and saw this:

#int _d_obj_eq(Object o1, Object o2)
#{
#    return o1 is o2 || (o1 && o1.opEquals(o2));
#}

truth table:
o1 o2   result
0   0     1
0   x     0
x   0     opEquals, crash?
x   x     1
y   x     opEquals

It's interesting that (o1==o2) is not necessarily the same as (o2==o1)!
Since o1=x,o2=0 returns 0, the other way around should return 0 as well.

I suppose the function should be also check o2, resulting in:

#int _d_obj_eq(Object o1, Object o2)
#{
#    return o1 is o2 || (o1 && o2 && o1.opEquals(o2));
#}

L.
```
May 23 2006
"Unknown W. Brackets" <unknown simplemachines.org> writes:
```Forgive my rusty logic skills... how do I read your table?  As far as I
see it:

o1     o2     result
null   null   true
null   valid  false
valid  null   result of opEquals(null) - probably false
valid  valid  result of opEquals(valid) - unknown result
valid  bad    result of opEquals(bad) - probably false

I don't know what "y" is on your table.

Of course, you could throw that off with an opEquals like this:

bool opEquals(Object o)
{
return true;
}

Obviously, we have to assume that o1 and o2 are of the same type, or
else different opEquals might be called.  So, okay... the only
differences we have:

If o1 is null, reversing the comparison will depend on opEquals
returning false when it is passed a null pointer (which should be the case.)

Comparing a valid pointer to a bad pointer cannot be properly reversed,
or it might cause a segfault.  This is always wrong anyway imho.

That said, opEquals (imho) will either always check for null or cannot
be reversed (null == evaluatesToNullObject is always false.)  So it
would seem logical to do something like you suggested.

-[Unknown]

truth table:
o1 o2   result
0   0     1
0   x     0
x   0     opEquals, crash?
x   x     1
y   x     opEquals

It's interesting that (o1==o2) is not necessarily the same as (o2==o1)!
Since o1=x,o2=0 returns 0, the other way around should return 0 as well.

I suppose the function should be also check o2, resulting in:

#int _d_obj_eq(Object o1, Object o2)
#{
#    return o1 is o2 || (o1 && o2 && o1.opEquals(o2));
#}

```
May 23 2006
"Lionello Lunesu" <lionello lunesu.remove.com> writes:
```"Unknown W. Brackets" <unknown simplemachines.org> wrote in message
news:e50lr3\$rqo\$1 digitaldaemon.com...
o1     o2     result
null   null   true
null   valid  false
valid  null   result of opEquals(null) - probably false
valid  valid  result of opEquals(valid) - unknown result
valid  bad    result of opEquals(bad) - probably false

'bad'? I don't think you can tell whether a pointer is bad or valid. I think
calling opEquals(null) is bad, since it's likely to crash. And I've noticed
before that many implementations start with a if(lhs is null) check. It just
makes sense to put that check 1 function up in the code, namely in the ==.

I don't know what "y" is on your table.

'y' is just different from 'x' :)

bool opEquals(Object o)
{
return true;
}

Obviously, we have to assume that o1 and o2 are of the same type, or else
different opEquals might be called.

Might not be a bad idea to also check the classinfo of both objects in the
== ?

So, okay... the only differences we have:

If o1 is null, reversing the comparison will depend on opEquals returning
false when it is passed a null pointer (which should be the case.)

Right. At the moment opEquals _must_ begin with "if (lhs is null) return
false;", but this can easily be enforced by putting the same check in ==.

Comparing a valid pointer to a bad pointer cannot be properly reversed, or
it might cause a segfault.  This is always wrong anyway imho.

Right. Nothing you can do about that I'm afraid.

That said, opEquals (imho) will either always check for null or cannot be
reversed (null == evaluatesToNullObject is always false.)  So it would
seem logical to do something like you suggested.

It would seem so :)

L.
```
May 23 2006
"Unknown W. Brackets" <unknown simplemachines.org> writes:
```Bad means a pointer to an invalid object.  A bad pointer.  I was trying
to cover all the bases.

Your typical opEquals looks like this:

bool opEquals(Object o)
{
Foo f = cast(Foo) o;

// If o was null, or if f is now null (doesn't match class) fail.
if (f is null)
return false;

return f.prop == this.prop;
}

Anything else either doesn't work properly with subclassing, isn't
typesafe, or will segfault.  This doesn't seem wrong to me.

Okay, missed that x and y were supposed to be objects.  You seem to be
missing a "x y" case, then.  This would be the same as "y x", though.

It would be a bad idea to check the class info imho.  Why can't a XYZ
equal an ABC even if they share no parents aside from Object?  But that
does mean the order of the comparison does matter (consider Array ==
SpecializedArray vs. SpecializedArray == Array.)

No, opEquals should never begin with any check on lhs... lhs will be
this.  It should however check rhs, first casting it more than likely,
and then checking for null on the cast.... since, if the cast fails, it
will be null.

This same check cannot be necesarily made in ==, since it can't know
what other classes the opEquals supports.

Still, checking for null could be a good thing for sanity; I'd guess it
doesn't because opEquals would have to anyway.

-[Unknown]

'bad'? I don't think you can tell whether a pointer is bad or valid. I think
calling opEquals(null) is bad, since it's likely to crash. And I've noticed
before that many implementations start with a if(lhs is null) check. It just
makes sense to put that check 1 function up in the code, namely in the ==.

I don't know what "y" is on your table.

'y' is just different from 'x' :)

Might not be a bad idea to also check the classinfo of both objects in the
== ?

So, okay... the only differences we have:

If o1 is null, reversing the comparison will depend on opEquals returning
false when it is passed a null pointer (which should be the case.)

Right. At the moment opEquals _must_ begin with "if (lhs is null) return
false;", but this can easily be enforced by putting the same check in ==.

Comparing a valid pointer to a bad pointer cannot be properly reversed, or
it might cause a segfault.  This is always wrong anyway imho.

Right. Nothing you can do about that I'm afraid.

That said, opEquals (imho) will either always check for null or cannot be
reversed (null == evaluatesToNullObject is always false.)  So it would
seem logical to do something like you suggested.

It would seem so :)

```
May 23 2006
Lionello Lunesu <lio lunesu.remove.com> writes:
```Going thirty and I'm still mixing left and right :S

You've made some good points. Indeed, since opEquals has to check the
type of the passed Object, it'll automatically check for null too.

Still odd though, that you can get a different result when you do o1==o2
or o2==o1, but I guess the library programmer should take care of that :)

L.

Unknown W. Brackets wrote:
Bad means a pointer to an invalid object.  A bad pointer.  I was trying
to cover all the bases.

Your typical opEquals looks like this:

bool opEquals(Object o)
{
Foo f = cast(Foo) o;

// If o was null, or if f is now null (doesn't match class) fail.
if (f is null)
return false;

return f.prop == this.prop;
}

Anything else either doesn't work properly with subclassing, isn't
typesafe, or will segfault.  This doesn't seem wrong to me.

Okay, missed that x and y were supposed to be objects.  You seem to be
missing a "x y" case, then.  This would be the same as "y x", though.

It would be a bad idea to check the class info imho.  Why can't a XYZ
equal an ABC even if they share no parents aside from Object?  But that
does mean the order of the comparison does matter (consider Array ==
SpecializedArray vs. SpecializedArray == Array.)

No, opEquals should never begin with any check on lhs... lhs will be
this.  It should however check rhs, first casting it more than likely,
and then checking for null on the cast.... since, if the cast fails, it
will be null.

This same check cannot be necesarily made in ==, since it can't know
what other classes the opEquals supports.

Still, checking for null could be a good thing for sanity; I'd guess it
doesn't because opEquals would have to anyway.

-[Unknown]

'bad'? I don't think you can tell whether a pointer is bad or valid. I
think calling opEquals(null) is bad, since it's likely to crash. And
I've noticed before that many implementations start with a if(lhs is
null) check. It just makes sense to put that check 1 function up in
the code, namely in the ==.

I don't know what "y" is on your table.

'y' is just different from 'x' :)

Might not be a bad idea to also check the classinfo of both objects in
the == ?

So, okay... the only differences we have:

If o1 is null, reversing the comparison will depend on opEquals
returning false when it is passed a null pointer (which should be the
case.)

Right. At the moment opEquals _must_ begin with "if (lhs is null)
return false;", but this can easily be enforced by putting the same
check in ==.

Comparing a valid pointer to a bad pointer cannot be properly
reversed, or it might cause a segfault.  This is always wrong anyway
imho.

Right. Nothing you can do about that I'm afraid.

That said, opEquals (imho) will either always check for null or
cannot be reversed (null == evaluatesToNullObject is always false.)
So it would seem logical to do something like you suggested.

It would seem so :)

```
May 24 2006