"The last feature": overridable methods in interfaces

Steven Schveighoffer schveiguy at yahoo.com
Mon Feb 8 12:08:37 PST 2010


On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> Don wrote:
>> Andrei Alexandrescu wrote:
>>> Walter has now implemented final methods in interfaces and also  
>>> contracts in interfaces, both of which I think are just awesome.
>>>
>>> We figured that essentially he artificially disallows interfaces from  
>>> providing bodies for methods. I think that's a gratuitous limitation;  
>>> the only distinguishing quality of an interface is that it has no  
>>> state.  Other than that, interfaces can always offer overridable  
>>> functions that by default offer functionality in terms of existing  
>>> interface functions. For example:
>>>
>>> interface Stack(T)
>>> {
>>>     void push(T);
>>>     void pop();
>>>     @property ref T top();
>>>     @property bool empty();
>>>     T belowTop()
>>>     {
>>>         auto t = top;
>>>         pop();
>>>         auto result = top;
>>>         push(t);
>>>     }
>>> }
>>>
>>> The default implementation of belowTop does a fair amount of work. A  
>>> particular implementation might just use that or override it with a  
>>> more efficient implementation.
>>>
>>> Many more examples can be imagined, but I'm looking for a killer one,  
>>> or perhaps a killer counterexample (e.g. when would an  
>>> interface-defined method be really bad?)
>>>
>>> Your thoughts welcome.
>>>
>>>
>>> Andrei
>>  I don't understand this. How does belowTop() know how to call top()?
>
> It calls top() through the normal interface mechanism. Inside  
> belowTop(), this has Stack!T type.

Actually, I think Don has a point here.  A virtual function (even on an  
interface) is always called with the 'this' pointer, not the interface  
pointer.  A real example:

interface A
{
    int bar();
    int foo() { return bar(); }
}

class C : A
{
    override int bar() { return 1;}
}

class D : C
{
    override int foo() { return 2;}
}

if you have a reference to A, when calling foo, what do you pass as the  
this pointer?  I think the way it works is an interface call does an  
interface lookup to get the 'true' this pointer (this is a quick lookup  
since an interface pointer has the offset info at a predetermined  
location), so the object pointer is passed into foo.  When the actual type  
is a D object, a D reference is expected, but when the actual type is a C  
object, what is expected?

The compiler cannot tell what the underlying type is, so when the actual  
type is a C object, a C reference will be passed in.  So in a "default  
implementation", there has to be an implicit thunk to convert the type  
back into an interface.  Basically, the function foo as implemented in A  
looks like this:

int foo() {
A __this = cast(A)this; // do a thunk, 'this' is of type Object
return __this.bar(this); // no lookup of this required, so this is the  
same as a standard virtual call
}

This severely lowers my taste for this idea.  I think a thunk uses a  
linear lookup of the interface list at runtime to find the correct  
interface.

A way you might be able to get rid of this problem is to compile the  
default implementation as if it were a function of the class that  
implements the interface.  I could probably live with that, but this  
feature seems more complicated than it is worth.

-Steve



More information about the Digitalmars-d mailing list