www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Self-reference in interface definition

reply Jason House <jason.james.house gmail.com> writes:
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
May 15 2007
parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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
May 15 2007
parent reply Jason House <jason.james.house gmail.com> writes:
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; } == gdc output == test.d:7: Error: cannot implicitly convert expression (a) of type test.A to test.B
May 20 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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. 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; } 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. --bb
May 20 2007
parent Jason House <jason.james.house gmail.com> writes:
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++!
May 20 2007