covariant final interface functions
Steven Schveighoffer
schveiguy at yahoo.com
Fri Mar 19 07:36:32 PDT 2010
On Fri, 19 Mar 2010 09:28:21 -0400, Justin Johansson <no at spam.com> wrote:
> Maybe I don't understand your problem exactly, but in answer to
> what you said here ...
>
>> Because of covariance, this works
>> awesome because you always get back the type you are using.
>
> .. If you know in the first place (presumably by static analysis) the
> type you
> are using then you know the type you expect to be returned, so why do
> you need to even use an interface and expect some magic covariance to
> be of use to you.
Because people may want to use the interface vs the actual class. There
is no point of an interface if people don't use it to abstract the
implementation. I want the interface to call the same function as the
class, but the return type should be covariant. That is, someone who is
using the derived type and wants to chain calls on such functions should
not be forced to use only the interface supported calls after they make
the first one.
> Like I say, perhaps I misunderstand your point but feel from my own
> endeavours that covariant return types are rarely useful and only serve
> to push more entries into a virtual function table further down an
> inheritance
> tree for no purposeful gain in the end.
covariant functions occupy one slot of the vtable. You may be
misunderstanding how covariance works. See below.
>
> Perhaps you could elaborate on what the real problem is that you want to
> solve.
>
> Please accept my apologies if your question/proposal is obvious to all
> and sundry;
> just that personally I cannot reconcile this in my own mind that there
> is an end
> to be met here by your means.
No apologies needed :) I'll explain what I mean:
Covariance works like this:
class C
{
C doSomething() {...; return this;}
}
class D : C
{
D doSomething() {...; return this;}
void doSomethingElse() {...}
}
If I have a D, I can call d.doSomething().doSomethingElse(), whereas if I
have a C reference to a D, I cannot call the doSomethingElse part, but
both c.doSomething() and d.doSomething() invoke the exact same function
(D's version).
If you look at D's virtual table, it only has 2 entries, because the
covariant version overrides the base version. In C++ this is not
possible, because it does not consider that you can return a pointer to
something that is both a C and a D at the same time. The benefit is one
implementation for both interfaces (C or D).
However, if you have simple "wrapper" functions, like the ones I've
outlined, in order to achieve covariance on those wrappers, you must
override them.
e.g.:
class C
{
C doSomething(ref int x) {...; return this;}
C doSomething() {int dummy; return doSomething(dummy);}
}
class D : C
{
D doSomething(ref int x) {...; return this;}
D doSomething() {int dummy; return doSomething(dummy);} // need this
reimplementation to achieve covariance
}
That second function of D is a complete duplicate of code, and poses a
maintenance nightmare. If this is a large hierarchy, any changes I make
to the base wrapper must be duplicated in each derived class.
Not only that, but all versions of the function are exact copies! There
is no difference in the generated code whatsoever.
With the advent of final functions in interfaces, I can achieve wrapper
functions in an interface (I'm using that now for some things in
dcollections). However, I can't make them covariant. So I'm forced to
write the wrapper functions in the implementation instead of the
interface, where it belongs.
It would be nice in both the interface and the class hierarchy cases to
simply identify that a function is the same, but permanently covariant
because it returns a pointer to this. Covariance always is possible if
you return this.
That's all I'm saying.
-Steve
More information about the Digitalmars-d
mailing list