Thread overview
interface and opEquals
Nov 04, 2007
Oliver
Nov 06, 2007
Oliver
Nov 07, 2007
Oliver
November 04, 2007
Hi everyone,

the following code works and does what it is supposed to do. I am, however, unsure if this is the "right" way to do it. I read somewhere that opEquals should take an Object as argument. I can not get the code to work with Object as argument. Can anyone explain this to be, what am i missing? Thanks to everyone,

Oliver

------------------------

import std.stdio;

interface Vi {
    void f();
    int opEquals( Vi );
    //int opEquals( Object );
    //int opEquals();
}

class C1 : Vi {
    void f() { writefln( "C1: ", itsInt ); }
    this( int i ) { this.itsInt = i; }
    int opEquals( Vi o ) {
        C1 test = cast(C1)o;
        return test && this.itsInt == test.itsInt;
    }
    //int opEquals( C1 test ) { return this.itsInt == test.itsInt; }
private:
    int itsInt;
}

int main() {
    assert( 1 == 1.0 );
    Vi myInt1 = new C1(1);
    Vi myInt2 = new C1(1);
    myInt1.f();
    assert( cast(C1)myInt1 == cast(C1)myInt2 );
    assert( myInt1 == myInt2 );
    return 0;
}

November 05, 2007
"Oliver" wrote
> Hi everyone,
>
> the following code works and does what it is supposed to do. I am, however, unsure if this is the "right" way to do it. I read somewhere that opEquals should take an Object as argument. I can not get the code to work with Object as argument. Can anyone explain this to be, what am i missing? Thanks to everyone,
>
> Oliver
>
> ------------------------
>
> import std.stdio;
>
> interface Vi {
>    void f();
>    int opEquals( Vi );
>    //int opEquals( Object );
>    //int opEquals();
> }
>
> class C1 : Vi {
>    void f() { writefln( "C1: ", itsInt ); }
>    this( int i ) { this.itsInt = i; }
>    int opEquals( Vi o ) {
>        C1 test = cast(C1)o;
>        return test && this.itsInt == test.itsInt;
>    }
>    //int opEquals( C1 test ) { return this.itsInt == test.itsInt; }
> private:
>    int itsInt;
> }
>
> int main() {
>    assert( 1 == 1.0 );
>    Vi myInt1 = new C1(1);
>    Vi myInt2 = new C1(1);
>    myInt1.f();
>    assert( cast(C1)myInt1 == cast(C1)myInt2 );
>    assert( myInt1 == myInt2 );
>    return 0;
> }
>

You should use Object as the argument.  The reason is simple.  If you do not use Object, then the base opEquals function is not overridden.  When you use Vi as your argument, you start a new method that is overridden only by derivatives of Vi.  Your example isn't a good way to demonstrate why this is bad, but imagine if Vi had a base interface...

Having said that, you do not need opEquals to be defined as a method in your interface, because Object already defines it.  All classes derived from Object will have a valid opEquals.  Perhaps this is why it was not working for you.

I think this code would work:

import std.stdio;

interface Vi {
    void f();
    //int opEquals( Vi );  // ** commented out
    //int opEquals( Object );
    //int opEquals();
}

class C1 : Vi {
    void f() { writefln( "C1: ", itsInt ); }
    this( int i ) { this.itsInt = i; }
    int opEquals( Object o ) { // ** changed to Object
        C1 test = cast(C1)o;
        return test && this.itsInt == test.itsInt;
    }
    //int opEquals( C1 test ) { return this.itsInt == test.itsInt; }
private:
    int itsInt;
}

int main() {
    assert( 1 == 1.0 );
    Vi myInt1 = new C1(1);
    Vi myInt2 = new C1(1);
    myInt1.f();
    assert( cast(C1)myInt1 == cast(C1)myInt2 );
    assert( myInt1 == myInt2 );
    return 0;
}

-Steve


November 06, 2007
Steven Schveighoffer Wrote:


Steve,

thank you for looking into this.

> You should use Object as the argument.  The reason is simple.  If you do not use Object, then the base opEquals function is not overridden.  When you use Vi as your argument, you start a new method that is overridden only by derivatives of Vi.

That puts it very clearly.

> Your example isn't a good way to demonstrate why this is bad, but imagine if Vi had a base interface...

Unfortunately, I do not understand this. Could you explain a little more, or tell me were i could read up on this?

> Having said that, you do not need opEquals to be defined as a method in your interface, because Object already defines it.  All classes derived from Object will have a valid opEquals.  Perhaps this is why it was not working for you.


> I think this code would work:
> 

The second assert fails. And that is the one i would like to work. I have the impression that it does not work because opEquals does not know what to do with Vi objects.

Then i tried to have opEquals( Object ) in the interface and wanted opEquals ( C1 test ) in the C1 class. But that did not work either. Which kind of makes sense.

hm, i do not really understand this. Any ideas? Thanks,

Oliver

> import std.stdio;
> 
> interface Vi {
>     void f();
>     //int opEquals( Vi );  // ** commented out
>     //int opEquals( Object );
>     //int opEquals();
> }
> 
> class C1 : Vi {
>     void f() { writefln( "C1: ", itsInt ); }
>     this( int i ) { this.itsInt = i; }
>     int opEquals( Object o ) { // ** changed to Object
>         C1 test = cast(C1)o;
>         return test && this.itsInt == test.itsInt;
>     }
>     //int opEquals( C1 test ) { return this.itsInt == test.itsInt; }
> private:
>     int itsInt;
> }
> 
> int main() {
>     assert( 1 == 1.0 );
>     Vi myInt1 = new C1(1);
>     Vi myInt2 = new C1(1);
>     myInt1.f();
>     assert( cast(C1)myInt1 == cast(C1)myInt2 );
>     assert( myInt1 == myInt2 );
>     return 0;
> }
> 
> -Steve
> 
> 

November 06, 2007
"Oliver" wrote
> Steven Schveighoffer Wrote:
>
>
> Steve,
>
> thank you for looking into this.

No problem :)

>
>> Your example isn't a good way to demonstrate why this is bad, but imagine if Vi had a base interface...
>
> Unfortunately, I do not understand this. Could you explain a little more, or tell me were i could read up on this?

Here is a better example:

import std.stdio;

class A
{
   int opEquals(A o)
  {
      writefln("A");
      return 0;
   }
}

class B : A
{
   int opEquals(B o)
   {
       writefln("B");
       return 0;
   }
}

void main()
{
   A x = new B;
   A y = new B;
   x == y;
}

This prints "A".

The reason you want to use Object is because the method will override the base class' interface.  If you don't do this, you are starting a new method, and comparing two base class objects will result in the comparison NOT calling your method.

>
>> Having said that, you do not need opEquals to be defined as a method in
>> your
>> interface, because Object already defines it.  All classes derived from
>> Object will have a valid opEquals.  Perhaps this is why it was not
>> working
>> for you.
>
>
>> I think this code would work:
>>
>
> The second assert fails. And that is the one i would like to work. I have the impression that it does not work because opEquals does not know what to do with Vi objects.
>
> Then i tried to have opEquals( Object ) in the interface and wanted opEquals ( C1 test ) in the C1 class. But that did not work either. Which kind of makes sense.
>
> hm, i do not really understand this. Any ideas?

Hm... I was not aware that interfaces did not have a base interface, or could not be implicitly cast to Object.  This makes sense because if you had a base interface that called out opCmp or opEquals, it would not allow Object to fulfill that requirement, and therefore you would be required to always implement those two functions in the class that implements the interface.

I can't figure out how you could have the identified two lines below call the same method.  Perhaps this is by design?

interface I
{
  ...
}

class C : I
{
   ...
}

...
I c1 = new C;
I c2 = new C;

//  how can you implement C and I such that the following 2 lines
//  call the same method?
c1 == c2;
cast(Object)c1 == cast(Object)c2;

For now, my recommendation would be to cast the interfaces to Object before comparing (i.e. second line above).  Casting to Object should always work because an interface should always be able to transform into an Object, since the implementation is always a class.  It's ugly, but it should work the way you want.

Any D gurus have the answer to this?

-Steve


November 07, 2007
Steven Schveighoffer Wrote:


> 
> Any D gurus have the answer to this?
> 
> -Steve
> 
> 

Thanks, Steve.

I found the following thread: http://www.digitalmars.com/d/archives/digitalmars/D/Object_and_interface_compatibility_48235.html

but of course, I'd be very interested to learn if there is another, cleaner, way that casting to Object.

Oliver