www.digitalmars.com         C & C++   DMDScript  

D - New (?) Interface semantics

reply Mac Reiter <Mac_member pathlink.com> writes:
From the interface semantics description:
" Interfaces can be inherited, but functions are not overridden:

interface D
{
int foo();
}

class A : D
{
int foo() { return 1; }
}

class B : A
{
int foo() { return 2; }
}

..

B b = new B();
b.foo();		// returns 2
D d = (D) b;		// ok since B inherits A's D implementation
d.foo();		// returns 1;
"

I am especially confused by the next to last line:

D d = (D) b;

The reference d has no reason to prefer any particular implementation of "int
D::foo()".  The comment says that it will use A's implementation due to
inheritance, but this line never mentions A.  Why would this code do what
appears to be an implicit "cast to base class" with no reason?  Since B has a
perfectly good foo(), I would expect virtual function behavior and that I would
get B::foo().  The only thing I can think of is that this is a doc typo, and was
supposed to read:

D d = (A) b;

In which case I'm OK for the example doing what it says it does.  That does,
however, leave me with the question of what the example -- as it currently
exists, with the cast to D -- would print.  Would you get the virtual function
call to B::foo(), like I would expect, since D has no implementation of its own?

Mac
Oct 02 2002
next sibling parent "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:anf057$1429$1 digitaldaemon.com...
 From the interface semantics description:
 " Interfaces can be inherited, but functions are not overridden:

 interface D
 {
 int foo();
 }

 class A : D
 {
 int foo() { return 1; }
 }

 class B : A
 {
 int foo() { return 2; }
 }

 ..

 B b = new B();
 b.foo(); // returns 2
 D d = (D) b; // ok since B inherits A's D implementation
 d.foo(); // returns 1;
 "

 I am especially confused by the next to last line:

 D d = (D) b;

 The reference d has no reason to prefer any particular implementation of

 D::foo()".  The comment says that it will use A's implementation due to
 inheritance, but this line never mentions A.  Why would this code do what
 appears to be an implicit "cast to base class" with no reason?

Because it will use A's D.
 Since B has a
 perfectly good foo(), I would expect virtual function behavior and that I

 get B::foo().  The only thing I can think of is that this is a doc typo,

 supposed to read:

 D d = (A) b;

No, it's not a typo.
 In which case I'm OK for the example doing what it says it does.  That

 however, leave me with the question of what the example -- as it currently
 exists, with the cast to D -- would print.  Would you get the virtual

 call to B::foo(), like I would expect, since D has no implementation of

No, you'd get A.foo(). Joe & I went around on this for a few cycles, and this seemed the best solution.
Oct 02 2002
prev sibling parent reply Patrick Down <pat codemoon.com> writes:
If the D docs are correct, I don't like
the way interfaces are now implemented.

I have some general issues with them but
the thing that I think is a big issue
is this...

  B b = new B();
  b.foo();    // returns 2
  D d = (D) b;
  d.foo();    // returns 2
  A a = (A) b;
  D d2 = (D) a;
  // REALLY BIG PROBLEM FOR ME HERE!!!
  d2.foo();   // returns 1, because it uses A's D, not B's D

There are places in my code that I keep arrays of base classes.  
I act on interfaces of these base classes with the expectation
that they use the most derived interface for that class.

The only way I can see around this problem now is to do this:


interface D
{
    int foo();
}

class A : D
{
    int foo() { return 1; }
    
    D getDInterface() { return (D)this; }
}

class B : A, D
{
    int foo() { return 2; }

    D getDInterface() { return (D)this; }
}

Now use getDInterface() on the base class to get
the correct interface.

 
Oct 02 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
I hear you. You might be right. Let's let things percolate a bit and
see. -Walter

"Patrick Down" <pat codemoon.com> wrote in message
news:Xns929B69618134Dpatcodemooncom 63.105.9.61...
 If the D docs are correct, I don't like
 the way interfaces are now implemented.

 I have some general issues with them but
 the thing that I think is a big issue
 is this...

   B b = new B();
   b.foo();    // returns 2
   D d = (D) b;
   d.foo();    // returns 2
   A a = (A) b;
   D d2 = (D) a;
   // REALLY BIG PROBLEM FOR ME HERE!!!
   d2.foo();   // returns 1, because it uses A's D, not B's D

 There are places in my code that I keep arrays of base classes.
 I act on interfaces of these base classes with the expectation
 that they use the most derived interface for that class.

 The only way I can see around this problem now is to do this:


 interface D
 {
     int foo();
 }

 class A : D
 {
     int foo() { return 1; }

     D getDInterface() { return (D)this; }
 }

 class B : A, D
 {
     int foo() { return 2; }

     D getDInterface() { return (D)this; }
 }

 Now use getDInterface() on the base class to get
 the correct interface.

Oct 02 2002
next sibling parent reply Joe Battelle <Joe_member pathlink.com> writes:
Um..I don't think what you implemented is what we came up with <g>.

"Interfaces can be inherited, but functions are not overridden:"
This leads to trouble.  If you keep this stance, I advocated not allowing
signatures in derived classes that would "cover" the interface ones.

"But interfaces can be reimplemented in derived classes:"
Here the real question is does a.foo()==d2.foo()?  If not, again people will be
upset.

I don't have time for a full reply right now, I'll go back through my posts and
then resummarize if necessary.
Oct 02 2002
parent reply "Walter" <walter digitalmars.com> writes:
This is the relevant message, I infer there's a bug in it <g>:

3) Overriding Interfaces -- if a subclass of a class that implements an
interface, overrides a member of that interface then it is a compile-time
error,
unless the class specifies that it is redefining the interface, whereby a
new
interface is created from that point in the hierarchy.
interface D { void foo(); }
class A : D { void foo() {} }
class B : A { void foo() {} } //ERROR, redefining D without specifying it
class C : B, D { void foo() {} } //OK, we get a new interface here
class C : B, D { char[] foo() {} } //ERROR, we didn't override anything in D


"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:anffo9$1kfl$1 digitaldaemon.com...
 Um..I don't think what you implemented is what we came up with <g>.

 "Interfaces can be inherited, but functions are not overridden:"
 This leads to trouble.  If you keep this stance, I advocated not allowing
 signatures in derived classes that would "cover" the interface ones.

 "But interfaces can be reimplemented in derived classes:"
 Here the real question is does a.foo()==d2.foo()?  If not, again people

 upset.

 I don't have time for a full reply right now, I'll go back through my

 then resummarize if necessary.

Oct 03 2002
parent Joe Battelle <Joe_member pathlink.com> writes:
This is the relevant message, I infer there's a bug in it <g>:

as (D)b.foo(), when both A and B implement D for the moment. I'll come back to this in another post. What I was addressing with the post you quoted was if (A)b.foo()==(D)b.foo() when only A implements D, B doesn't specify D (silently inheriting it), but overrides foo() thereby covering D's foo(). This leads to the bizarre situation where (A)b.foo()!=(D)b.foo() where D is only specified at A. Quoting the relevant line:
class B : A { void foo() {} } //ERROR, redefining D without specifying it

think you were against it (I think). But that only makes sense if inherited interfaces can also be overriden (which I though you were implementing), so that B's foo automatically alters B's inherited D interface,which it does not in your implementation.
Oct 03 2002
prev sibling next sibling parent "chris jones" <flak clara.co.uk> writes:
Why not have an 'overide' keyword so you can explicitly state when you want
the derived class function to overide the base class function?

chris

"Walter" <walter digitalmars.com> wrote in message
news:anf6fl$1aj0$1 digitaldaemon.com...
 I hear you. You might be right. Let's let things percolate a bit and
 see. -Walter

 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns929B69618134Dpatcodemooncom 63.105.9.61...
 If the D docs are correct, I don't like
 the way interfaces are now implemented.

 I have some general issues with them but
 the thing that I think is a big issue
 is this...

   B b = new B();
   b.foo();    // returns 2
   D d = (D) b;
   d.foo();    // returns 2
   A a = (A) b;
   D d2 = (D) a;
   // REALLY BIG PROBLEM FOR ME HERE!!!
   d2.foo();   // returns 1, because it uses A's D, not B's D

 There are places in my code that I keep arrays of base classes.
 I act on interfaces of these base classes with the expectation
 that they use the most derived interface for that class.

 The only way I can see around this problem now is to do this:


 interface D
 {
     int foo();
 }

 class A : D
 {
     int foo() { return 1; }

     D getDInterface() { return (D)this; }
 }

 class B : A, D
 {
     int foo() { return 2; }

     D getDInterface() { return (D)this; }
 }

 Now use getDInterface() on the base class to get
 the correct interface.


Oct 03 2002
prev sibling parent reply Patrick Down <pat codemoon.com> writes:
"Walter" <walter digitalmars.com> wrote in news:anf6fl$1aj0$1
 digitaldaemon.com:

 I hear you. You might be right. Let's let things percolate a bit and
 see. -Walter
 

So was any decision reached about how interfaces should work?
Oct 07 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
So was any decision reached about how interfaces
should work?

If Walter implements the "no cover" rule, then to override an interface necessarily requires the derived class to specify the interface, thereby creating a new instance of the interface at that level in the hierarchy. So the question becomes: does (D)(A)b.foo()==(D)b.foo() when both A and subclass B specify interface D. In the current implementation, the answer is "no." You can only count on "most-derived" interface semantics if you confine yourself to interface references (converted directly from a most derived object reference). If you want to keep a list of A's (containing both A's and B's), and then you convert to D's on demand, you will only get A's version of D. I frankly don't find this so bothersome. What I do care about, is that you get a new interface at each level of the hierarchy in which the interface is specified, and that there must be a way to refer to individual interfaces so that they can be passed to external programs. The main advantage to the "interface per level" approach is that one can use interfaces to communicate with external applications (COM) that access different versions of the component (interface) at the same time, while using inheritence for reuse among versions. This is roughly how interfaces are implemented now. I would hate to lose this in an attempt to switch over to general "most-derived" semantics. So to some up, either Walter makes interfaces work more intuitively (in a D sense) and supports interface inheritence _allowing_ overloading without re-specifying the interface, guaranteeing (A)b.foo()==(D)b.foo(), or Walter makes covering interfaces _illegal_ when not explicitly specifying the interface, and keeps everything else as it is--which better supports external program interfacing. [Wow, that's quite a sentence =] I think it really depends on what people want to use interfaces for. I want to use them to interface to external programs, or as OS calls/callbacks (a la OSKIT) in an embedded system. This requires multiple, predictable interface definitions at the same time--exactly what we have. I know! "Let's have a pragma or compiler switch decide between the two". [kidding...]
Oct 07 2002
next sibling parent Joe Battelle <Joe_member pathlink.com> writes:
So to some up...

=]
Oct 07 2002
prev sibling next sibling parent reply Patrick Down <pat codemoon.com> writes:
Joe Battelle <Joe_member pathlink.com> wrote in
news:antj7j$que$1 digitaldaemon.com: 

So was any decision reached about how interfaces
should work?

In the current implementation, the answer is "no." You can only count on "most-derived" interface semantics if you confine yourself to interface references (converted directly from a most derived object reference). If you want to keep a list of A's (containing both A's and B's), and then you convert to D's on demand, you will only get A's version of D. I frankly don't find this so bothersome.

I think I would find it bothersome. <g> For me, and the majority of purposes that I would use interfaces for, I would certainly desire the most derived object interface. In my view a B is always a B even if I cast it to an A and thus should always present the interfaces implemented on a B class.
 to external programs. The main advantage to the "interface per level"
 approach is that one can use interfaces to communicate with external
 applications (COM) that access different versions of the component
 (interface) at the same time, while using inheritance for reuse among
 versions.  

It seems like this problem could be solved by mapping different internal interfaces to the same external one depending on component version.
 I think it really depends on what people want to use interfaces for. 

I guess when I saw that D had interfaces I expected that they would be like Java's interfaces. The following would be my preferred functionality. interface D { int foo(); } class A : D { int foo() { return 1; } } class B : A // Note don't need to redeclare D { int foo() { return 2; } } A a = new A(); A b = new B(); D da = (D)a; D db = (D)b; da.foo(); // return 1 db.foo(); // return 2 However I expect that Walter would tell me that he doesn't want to implicitly create a D interface vtable for every class derived from A.
Oct 07 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
First, having one hierarchy declare multiple, yet similar interfaces is a mess.
If the public interface hasn't changed, then it's much better if we can continue
to declare a single interface implemented differently at different levels in the
inheritance [got the spelling right finally =] hierarchy.

However I expect that Walter would tell me that he doesn't want to
implicitly create a D interface vtable for every class derived from 
A.

change the interface, redeclaring it makes perfect sense. This is analogous to the "inserting a call to super" thread. I don't think the compiler should be creating new interfaces without the programmer knowing about it. A seasoned programmer would know in any event with some investigating. But isn't it just as easy to say to the newbie: "you want to change the interface? redeclare it for the derived class. nothing going on behind the scenes. You want to reference a particular version of the interface you do this: (interface)(class) reference. You want the most derived interface? Manipulate interface references directly; don't cast through a base class first." Having said this. I would like to know from Walter how much work it is to generate "implicit" interfaces every time you overload a base classes declared interface. And, how we would refer to these (so as to pass to external apis) if they are implicit.
Oct 07 2002
next sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
In article <antvhr$17e7$1 digitaldaemon.com>, Joe Battelle says...
First, having one hierarchy declare multiple, yet similar interfaces is a mess.
If the public interface hasn't changed, then it's much better if we can continue
to declare a single interface implemented differently at different levels in the
inheritance [got the spelling right finally =] hierarchy.

However I expect that Walter would tell me that he doesn't want to
implicitly create a D interface vtable for every class derived from 
A.

change the interface, redeclaring it makes perfect sense. This is analogous to the "inserting a call to super" thread. I don't think the compiler should be creating new interfaces without the programmer knowing about it. A seasoned programmer would know in any event with some investigating. But isn't it just as easy to say to the newbie: "you want to change the interface? redeclare it for the derived class. nothing going on behind the scenes. You want to reference a particular version of the interface you do this: (interface)(class) reference. You want the most derived interface? Manipulate interface references directly; don't cast through a base class first." Having said this. I would like to know from Walter how much work it is to generate "implicit" interfaces every time you overload a base classes declared interface. And, how we would refer to these (so as to pass to external apis) if they are implicit.

I'm cool with all of that, except that it appears that I have no way to access most-derived interfaces in the current implementation. I asked point blank about the example in the documentation that states that: (D)b.foo; // calls A's implementation That is wrong to me on so many counts that I can't even begin to talk coherently about it. My major complaint is that A was not mentioned anywhere at all in that code, and B has a valid override of the D interface. If the above statement is true, it is impossible for me to ever make a generic container via interfaces -- the standard polymorphic example of drawable objects leaps to mind. Walter stated clearly that the documentation was correct. I cannot envision how I should understand this system. Sorry, gotta go to a teleconference... Mac
Oct 08 2002
next sibling parent Joe Battelle <Joe_member pathlink.com> writes:
I'm cool with all of that, except that it appears that I have no way to access
most-derived interfaces in the current implementation.

I asked point blank about the example in the documentation that states that:

(D)b.foo; // calls A's implementation

That is wrong to me on so many counts that I can't even begin to talk coherently
about it.  My major complaint is that A was not mentioned anywhere at all in
that code, and B has a valid override of the D interface.

It does not have a valid override. That is the whole point! Walter let overrides cover interface methods without requiring redeclaring interfaces and that causes loads of trouble. If he had not allowed the override, then B would _necessarily_ have had to redeclare D to overload foo and (D)b.foo would indeed call B's D. So as long as you manipulate D's without casting through bases you always get most-derived semantics.
Oct 08 2002
prev sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
I asked point blank about the example in the documentation that states that:

(D)b.foo; // calls A's implementation

That is wrong to me on so many counts that I can't even begin to talk coherently
about it.  My major complaint is that A was not mentioned anywhere at all in
that code, and B has a valid override of the D interface.  If the above
statement is true, it is impossible for me to ever make a generic container via
interfaces -- the standard polymorphic example of drawable objects leaps to
mind.

Walter stated clearly that the documentation was correct.  I cannot envision how
I should understand this system.

Sorry, gotta go to a teleconference...
Mac

OK, I'm back from the teleconference. The only mental model I can come up with is that (D)b tries to "cast" b to a D reference, which it can't do (no implementation for D, since it is just an interface). So, it casts it to the nearest thing it can find along that chain, which is A. I can (sort of) buy that from an implementation standpoint. Unfortunately, it completely breaks polymorphism on interfaces, and that raises a consistency issue, because polymorphism works for classes but not for interfaces: class BASE {} interface INTERFACE {} class ONE:BASE, INTERFACE { int foo() {return 1;} } class TWO:ONE, INTERFACE { int foo() {return 2;} } BASE b1, b2; INTERFACE i1, i2; ONE one; TWO two; one.foo(); // returns 1 two.foo(); // returns 1 b1 = (BASE)one; b2 = (BASE)two; i1 = (INTERFACE)one; i2 = (INTERFACE)two; b1.foo(); // returns 1 b2.foo(); // returns 2, as I would expect i1.foo(); // returns 1 i2.foo(); // returns 1, because it uses ONEs implementation rather than TWOs. I'm OK with all of the above except for the last line, which seems completely wrong to me. But I have been assured that that is what would happen. It is equivalent to the code from the documentation that does: (D)b.foo(); // returns 1 from A's implementation D has been replaced by INTERFACE, and b has been replaced by 'two', to avoid confusion with the BASE class. If you make those substitutions, the doc code becomes: (INTERFACE)two.foo(); My code merely uses a storage reference variable, so that what I exactly say is: INTERFACE i2; i2 = (INTERFACE)two; i2.foo(); We can, of course, remove the b1/b2/i1/i2 references, and just do the casts inline, but I expect the problem to arise in real world code when an object reference has been implicitly cast due to a function call and is being stored in a variable reference of a base type (standard collection design). While I can sort of buy the argument that an explicit cast like: (ONE)two.foo(); // returns 1, because it has been case to a one will call the lower interface because of the explicitness of the cast, I don't like that behavior when the cast is implicit. Nor do I like different behavior for implicit vs. explicit casts. And I don't really buy it even for explicit casts, because that wasn't my intent. I wanted to call a function that existed in the ONE class, but I wanted the behavior that was appropriate to the object I actually called it on -- two, of class TWO. That is what polymorphism is for, and it works for classes. Having it _not_ work for interfaces seems like a very inconsistent and error prone design. Realistically, there is no reason for the explicit cast as shown, unless it is meant to be something like C++'s dynamic_cast. I seem to be wandering from my point, sorry... I don't understand why you would _want_ to call a base class's implementation of an interface member. The base class is virtually guaranteed not to properly understand the derived class. If it did, it seems like you wouldn't have overridden that member. However, there is a lot of programming out there that I am unfamiliar with, so I won't state that you never need to do this. I would claim that doing so is an exceptional case, where the programmer is very deliberately choosing a base implementation. As an exceptional case, I think it deserves the special syntax, and common syntax should support polymorphism properly, like it does for classes. Two thoughts for how to access base class implementations: b.A::foo(); // from C++, more or less b.(A)foo(); // from my fevered imagination, as far as I can tell b.(A.foo)foo(); // seems redundant... b.(A.foo)(); // kinda fugly I have no clue how hard the second option would be to parse, either for compilers or for humans. I prefer it over (A)b.foo simply because the mechanism that supports (A)b.foo is the standard reference cast mechanism, and I don't want it to do this for all of the previous reasons. The second option casts the member, not the reference. Of course, syntactically, you are telling it to cast a function into a class reference, which breaks the syntax==semantics rule, which led me to the third option. However, the third option is quite verbose, and suggests that it would be legal to do: b.(A.bar)foo(); which would call A.bar, which raises the question of why foo() is even present -- hence option 4. Really, the only option of those that I like is the first one, but it does bring back the evil C++ scope resolution operator. Oooh! oooh! oooh! (Sorry, just had a potential brainstorm) How would: b.A.foo(); be? Treat each interface as an implicit member. If you are trying to get to a particular interface on an object, you go to that member. If you want standard "most derived" semantics, you just call it normally: b.foo(); // calls B's foo() b.B.foo(); // also calls B's foo(), should you wish to be verbose b.A.foo(); // calls A's foo() b.D.foo(); // illegal - D has no implementation, and thus is not included in // the "pseudo-member" set of base classes. In COM terms, that would mean you got things like: MySurface.DX7_SURFACE.blit(,,,); And because it goes through the member system, you can even use 'with' to gather up a collection of calls that all go through a specific base interface: with MySurface.DX7 { blit(,,,); } vtables/interfaces would not have to exist as specific entities at every level of the hierarchy, and classes that do not provide an overridden implementation would not be considered pseudo-members: // forgive the shorthand, but this hopefully will make sense in the context // of what we have been discussing: interface I class A:I class B:A,I // provides special overrides class C:B // nothing added/changed here class D:C,I // provides special overrides D d; d.foo; // OK, uses D d.D.foo; // OK, uses D d.C.foo; // illegal -- C does not provide specialized implementation d.B.foo; // OK, uses B d.A.foo; // OK, uses A Would such a system meet all of the requirements for everybody? 1. Polymorphic, "most derived" semantics for all common syntax, whether classes or interfaces. 2. Access to specific interface implementations when necessary, in a clear, understandable, and convenient way 3. (Reasonably) easy to implement in a compiler 4. (Reasonably) easy to explain to new programmers It seems like it would to me, but I am biased. Mac
Oct 08 2002
next sibling parent Mac Reiter <Mac_member pathlink.com> writes:
Sorry - while I was typing up my monster length followup, Joe Batelle clarified
an issue for me (that the only reason (D)b.foo fell back to A.foo was because B
did not state that it was overriding the D interface, so b.foo is apparently
part of B, but not part of the D interface to B)

Given that, I pitch my vote in on the "you have to state inheritance from the
interface to override any of its methods".

Although I still like the suggestion I ended up with at the end of the
mega-post... (using "pseudo-members" for base interfaces, so that you would do
b.A.foo() if you actually wanted access to A's foo())

Isn't asynchronous communication wonderful?
Mac
Oct 08 2002
prev sibling parent Joe Battelle <Joe_member pathlink.com> writes:
The only mental model I can come up with is that (D)b tries to "cast" b to a D
reference, which it can't do (no implementation for D, since it is just an
interface).  So, it casts it to the nearest thing it can find along that chain,
which is A.

covering. When B is forced to redeclare the interface, then this all goes away.
Oct 08 2002
prev sibling parent reply Patrick Down <pat codemoon.com> writes:
Joe Battelle <Joe_member pathlink.com> wrote in
news:antvhr$17e7$1 digitaldaemon.com: 

 First, having one hierarchy declare multiple, yet similar interfaces
 is a mess. If the public interface hasn't changed, then it's much
 better if we can continue to declare a single interface implemented
 differently at different levels in the inheritance [got the spelling
 right finally =] hierarchy. 

I understand.
 
However I expect that Walter would tell me that he doesn't want to
implicitly create a D interface vtable for every class derived from 
A.

want to change the interface, redeclaring it makes perfect sense. This is analogous to the "inserting a call to super" thread. I don't think the compiler should be creating new interfaces without the programmer knowing about it. A seasoned programmer would know in any event with some investigating.

I don't think this behaviour is as unexpected as the "inserting a call to super" behaviour. I look at an interface as an inheritable attribute of a class. If I say class Foo is ISerializable then I expect that Foo and all of it's child classes are ISerializable also. To change the behavior of the ISerializable in a child class I override the serialize function of the interface. I don't think this is unexpected or bad because I already expect to change the behavoir of a child class by overriding functions, does it matter if they are part of an interface or not? However, as I already stated, I don't thnk this is a battle I'm going to win and I can live with redefining the interface for base classes. I just think it redundant.
 But isn't it just as easy to say to
 the newbie: "you want to change the interface?  redeclare it for the
 derived class.  nothing going on behind the scenes.  You want to 
 reference a particular version of the interface you do this:
 (interface)(class) reference.  You want the most derived interface? 
 Manipulate interface references directly; don't cast through a base
 class first." 

Easier said than done. Suppose I have multiple hierarchies of classes. For the purpose of the program I store and manipulate them in a containers that holds the base class type. Now I want to store all of them. I would like to create an I/O object and pass it to the containers ISerialize interface. The container in turn will call the ISerialize on all is objects. I think this is a fairly common design pattern but it won't work with interfaces the way they are now.
 
 Having said this.  I would like to know from Walter how much work it
 is to generate "implicit" interfaces every time you overload a base
 classes declared interface.  And, how we would refer to these (so as
 to pass to external apis) if they are implicit.
 
 
 

Oct 08 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
However, as I already stated, I don't thnk this is a battle I'm going
to win and I can live with redefining the interface for base classes.
I just think it redundant.

classes.  For the purpose of the program I store and manipulate 
them in a containers that holds the base class type.  Now I want
to store all of them.  I would like to create an I/O object
and pass it to the containers ISerialize interface.  The container
in turn will call the ISerialize on all is objects

I actually think a better way of doing it is to have a DataModel object aggregate all objects you want to serialize by holding interface references to all those differently-inherited objects. To serialize you iterate over this list and you automatically get most-derived semantics. You get your polymorphism without hassle, albeit (possibly) at the cost of another container. In my own real-world experience, I usually have an object represent a file, and this object naturally aggregates all objects that I want to serialize. These interface references are most-derived because they are registered by the constructor of the serializable object.
I think this is a fairly common design pattern but it won't work
with interfaces the way they are now.

rather than kind (base class). I think this leads to better encapsulated design but YMMV.
Oct 08 2002
parent reply Patrick Down <pat codemoon.com> writes:
Joe Battelle <Joe_member pathlink.com> wrote in
news:anv2ir$2fd1$1 digitaldaemon.com: 
 In my own real-world experience, I usually have an object represent a
 file, and this object naturally aggregates all objects that I want to
 serialize.  These interface references are most-derived because they
 are registered by the constructor of the serializable object.
 
I think this is a fairly common design pattern but it won't work
with interfaces the way they are now.

(interface) rather than kind (base class). I think this leads to better encapsulated design but YMMV.

This is the problem with giving specific examples. We could ask 14 other programmers and get 14 other answers about how this particular example could be done. I can see that keeping collection of interfaces by utility will work but I don't think that it is always an appropriate solution. So it boils down this, which is my own personal opinion about how things should work. Without any other contextual information I should be able to ask an object if it provides interface X and there should be one and only one implementation of X for that object instance.
Oct 08 2002
parent "Walter" <walter digitalmars.com> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns92A18FB0822A7patcodemooncom 63.105.9.61...
 Without any other contextual information I should be able to ask an
 object if it provides interface X and there should be one and only
 one implementation of X for that object instance.

I tend to agree, and that is the current way it works. I don't have a lot of experience using interfaces, so I'm willing to listen <g>. (Note that although it behaves as if there is only one implementation of an interface no matter how many times it is inherited from, the actual implementation provides multiple implementations, each with its own vtbl[] with the right adjustor thunks. This is necessary to make COM work right.)
Oct 09 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:antj7j$que$1 digitaldaemon.com...
 I think it really depends on what people want to use interfaces for.  I

 use them to interface to external programs, or as OS calls/callbacks (a la
 OSKIT) in an embedded system.  This requires multiple, predictable

 definitions at the same time--exactly what we have.

The way it is now is the most derived implementation of a member foo overrides the foo of every interface in the inheritance heirarchy.
Oct 09 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
The way it is now is the most derived implementation of a member foo
overrides the foo of every interface in the inheritance heirarchy.

I might add. But I can get over it.
Oct 09 2002
next sibling parent Mac Reiter <Mac_member pathlink.com> writes:
In article <ao2c3a$2pvc$1 digitaldaemon.com>, Joe Battelle says...
The way it is now is the most derived implementation of a member foo
overrides the foo of every interface in the inheritance heirarchy.

I might add. But I can get over it.

Joe, Is it the loss of access to specific base interface implementations that is a problem? I made the following suggesting in a previous post, but I did a fair amount of rambling prior to it, and it may have been missed. How would it be to have syntax that let you access a specific base interface implementation as a member of the object: interface D{int foo()} class A:D {int foo(){return 1}} class B:A,D{int foo(){return 2}} B b = new B; printf("%d\n", b.foo()); // prints 2 printf("%d\n", b.B.foo()); // also prints 2, but probably never used // since you can just do b.foo() printf("%d\n", b.A.foo()); // prints 1, from the A implementation printf("%d\n", b.D.foo()); // compile error, no implementation named D // member syntax/semantics allows grouping use of a specific implementation // using 'with' with b.A { printf("%d\n", .foo()); // prints 1, from the A implementation } This is somewhat like how I understand the COM system to work, but with (at least in my opinion) a prettier syntax. As I understand it, COM would do it something like this: b.GetInterface(UUID_FOR_A)->foo(); And, since GetInterface is a runtime issue, you have to worry about getting back NULL or InvalidInterface at runtime, so you can't just write the code like that. If D built it into the language, it could perform compile time checks to validate the interface use. I would only allow use of base interface implementations. I would not allow someone to use this syntax for using a "reference to D" to get to an "interface implemented in B": D d = <reference to a B object that came from somewhere else> d.foo(); // uses B.foo, because of "most derived" semantics, which I like d.B.foo(); // compiler error -- cannot be checked at compile time d.A.foo(); // compiler error -- same reason If it is necessary to get to a "more derived specific implementation" of an interface (like in d.A.foo()), then some form of dynamic cast or dynamic interface retrieval will be needed. Said mechanism must have some way to signal failure cleanly. Oh, and if the inheritance hierarchy included another class that did _not_ implement the interface, it would be a compiler error to try to use that specific implementation: interface D{int foo()} class A:D {int foo(){return 1}} class A_and_some:A{} // does not do anything to A's implementation of D class B:A,D{int foo(){return 2}} B b = new B; printf("%d\n", b.A_and_some.foo()); // compile error - no such implementation Sorry if this just isn't desired or isn't workable. I didn't see any responses at all to the previous post, so I didn't know if it just got lost in the shuffle or if it was so bad as to not deserve comment. Hopefully it wasn't the latter... Mac
Oct 10 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:ao2c3a$2pvc$1 digitaldaemon.com...
The way it is now is the most derived implementation of a member foo
overrides the foo of every interface in the inheritance heirarchy.


Yes.
  Without notice
 I might add.  But I can get over it.

It's in the changelog that the semantics were changed. Sorry I wasn't more specific, but the current semantics seems to be the most defensible.
Oct 10 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
It's in the changelog that the semantics were changed. Sorry I wasn't more
specific, but the current semantics seems to be the most defensible.

going quiet, implementing what you think is best and then throwing it out to the group. Some may find this a good thing. It's certainly reasonable in the sense that D is your thing and you're putting in the effort. What bothered me about this last round is that I spent some of my overcommitted time debating/discussing interfaces at the same time that you were implementing them in a way completely opposite of what I was proposing. You can choose any implementation you like, but it's not cool to give the impression that the debate is still open and then drop a bombshell indicating it's not because you implemeted something else. I have used up the free time I have allocated to interfaces for now, so I will leave it to the rest of the group to explore.
Oct 11 2002
parent "Walter" <walter digitalmars.com> writes:
"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:ao8ddr$2vlu$1 digitaldaemon.com...
 I know, I was just being cranky.  I was serious though.  You have a

 going quiet, implementing what you think is best and then throwing it out

 group.  Some may find this a good thing.  It's certainly reasonable in the

 that D is your thing and you're putting in the effort.  What bothered me

 this last round is that I spent some of my overcommitted time
 debating/discussing interfaces at the same time that you were implementing

 in a way completely opposite of what I was proposing.  You can choose any
 implementation you like, but it's not cool to give the impression that the
 debate is still open and then drop a bombshell indicating it's not because

 implemeted something else.  I have used up the free time I have allocated

 interfaces for now, so I will leave it to the rest of the group to

Your ideas and input here have been extremely valuable and I appreciate immensely the effort you've put into your posts. Picking the correct semantics for interfaces is certainly a confusing exercise. You are the one who originally discovered that what I had implemented was completely broken. You were quite right. I plead guilty to going quiet for a while, sometimes that is because I am working on another problem and I prefer to do only think about one thing at a time. Although it may appear that the debate is closed because I've implemented something else, since I've reimplemented the interface semantics several times now due to these discussions, I don't think it's true. I put out the new implementation as a strawman model for further discussion. I apologize if I gave the wrong impression here.
Oct 12 2002
prev sibling parent "Sandor Hojtsy" <hojtsy index.hu> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns929B69618134Dpatcodemooncom 63.105.9.61...
 If the D docs are correct, I don't like
 the way interfaces are now implemented.

 I have some general issues with them but
 the thing that I think is a big issue
 is this...

   B b = new B();
   b.foo();    // returns 2
   D d = (D) b;
   d.foo();    // returns 2
   A a = (A) b;
   D d2 = (D) a;
   // REALLY BIG PROBLEM FOR ME HERE!!!
   d2.foo();   // returns 1, because it uses A's D, not B's D

 There are places in my code that I keep arrays of base classes.
 I act on interfaces of these base classes with the expectation
 that they use the most derived interface for that class.

 The only way I can see around this problem now is to do this:


 interface D
 {
     int foo();
 }

 class A : D
 {
     int foo() { return 1; }

     D getDInterface() { return (D)this; }
 }

 class B : A, D
 {
     int foo() { return 2; }

     D getDInterface() { return (D)this; }
 }

 Now use getDInterface() on the base class to get
 the correct interface.

Wow. So you are emulating the Right behaviour. Anybody can please put in an example here where this non-virtual interface is usefull?
Oct 03 2002