Self-reference in interface definition

Jason House jason.james.house at gmail.com
Sun May 20 13:58:45 PDT 2007


Bill Baxter wrote:
> Jason House wrote:
>> Chris Nicholson-Sauls wrote:
>>> Jason House wrote:
>>>> While the code below does not compile, I believe that it should be 
>>>> allowed.  Requiring something that implements an interface to accept 
>>>> other objects conforming to the interface should always be allowed.  
>>>> The specific problem below makes perfect sense to disallowed for 
>>>> classes, but does not for interfaces.
>>>>
>>>> == test.d ==
>>>> interface X{
>>>>   X opAssign(X);
>>>> }
>>>>
>>>> == gdc output ==
>>>> test.d:2: function test.X.opAssign identity assignment operator 
>>>> overload is illegal
>>>
>>> The problem is specifically with the X in opAssign's parameters.  One 
>>> of the restrictions on assignment operator overloading is that any 
>>> type X cannot have an overloaded assignment for X or a derivative of 
>>> X.  From the specs:
>>>
>>> """The assignment operator cannot be overloaded for rvalues that can 
>>> be implicitly cast to the lvalue type."""
>>>
>>> Nor may an opAssign have more than one parameter.
>>>
>>> I assume your intention was to have code like:
>>> X x1 = new A;
>>> X x2 = new B;
>>> x1 = x2;
>>>
>>> Where the assignment at the end copies data from x2 to x1 rather than 
>>> making them the same reference.  For that you will need an explicit 
>>> copy(X) method instead.  Or maybe you wanted something else, but that 
>>> was what first came to mind.
>>>
>>> -- Chris Nicholson-Sauls
>>
>> The code below is more what I'm trying to do.  Obviously, the sample 
>> code is a gross simplification.  A and B are optimized for two 
>> different types of usage (and may add extra accessors and 
>> manipulators), but fundamentally represent the same type of data.  
>> Even though algorithms will get written to use A or B explicitly, I 
>> want to be able to share data between them.  Converting A to B and B 
>> to A makes a lot of sense. I wanted the interface to require that 
>> functionality of anything that extends the interface...  And requiring 
>> explicit knowledge of all classes that extend the interface isn't 
>> generic enough for me.
>>
>> == test.d ==
>> interface X{}
>>
>> class A:X{}
>> class B:X{}
>>
>> int main(char[][] args){
>>   A a = new A();
>>   B b = a;
>> }
> 
> Object 'a' is not a B, so that's not going to work generally.

That's why I want to be able to assign any instance of X to b.  I'll 
leave it up to B to use the access methods of the X instance to build 
its internal representation.

> It sounds what you want may be better handled by object composition. Let 
> X be the thing that represents the core data and just make it a member 
> of A and B.
> 
> class X {}
> class A { X x; }
> class B { X x; }

The simplest way to implement interface X is as a pair of integers. 
While this would work, certain applications can be implemented much more 
quickly by using only one... But how to map it down to one is 
fundamentally different.  When lots of manipulation is done, the 
streamlined implementations provide a significant performance gain for 
specific classes of implementations.  I want to allow optimized 
implementations.

Embedding a basic implementation of X inside (in addition to an 
optimized implementation) only increases the work of maintaining it and 
defeats the purpose.


> Then you can add a method to change A or B's X member.
> If you also need to be able to treat A and B polymorphically, then you 
> can make a simple hasX interface that exposes X's basic attributes.  But 
> I didn't see anywhere that you said you needed to do so.
> 
> Or you could make X the base class for both A and B.

if A and B extend a base class X, the data associated with X will go 
completely unused and both A and B will need to override X's member 
functions to do alternate behavior.  Rather than have X contain data and 
function implementations that go unused by those that extend it, I'm 
using an interface.

Besides, if X is a base class or an interface, generically forcing 
everything that extends X (A and B in the example) to be constructed 
from an X is impossible... You can't define a (copy) constructor of X 
that accepts X as an input.

It's possible to do workarounds such as defining extra classes and 
interfaces to trick the compiler, but I really hate having to trick a 
compiler.  I did enough of that in C++!



More information about the Digitalmars-d mailing list