Final by default?

Dmitry Olshansky dmitry.olsh at gmail.com
Thu Mar 13 14:29:32 PDT 2014


14-Mar-2014 00:00, Andrei Alexandrescu пишет:
> On 3/13/14, 12:49 PM, Dmitry Olshansky wrote:
>> 13-Mar-2014 23:40, Andrei Alexandrescu пишет:
>>> On 3/13/14, 12:38 PM, Dmitry Olshansky wrote:
>>>> Not the same as `final:` inside - the above just means you can't
>>>> inherit
>>>> from A. Funnily enough any new methods in A would still be virtual even
>>>> if nothing can inherit from it!
>>>
>>> Looks like we could improve on this. Is there an enhancement request
>>> available?
>>>
>>
>> None that I know of.
>
> Just played with some code a bit, turns out at least gdc inlines calls
> for final classes appropriately: http://goo.gl/oV8EYu. What would be a
> reliable way to detect that a vtable entry is generated?

Mistake on my part.
This:

final class A {
     int i;
     void f() { ++i; }
     void g() { ++i; }

}
pragma(msg, __traits(isFinalFunction, A.g));
pragma(msg, __traits(isFinalFunction, A.f));

Prints:

true
true

during compilation, which I take as no vtable entries should be generated.

>
>> P.S. I have a strong distaste to the way OOP is currently designed in D
>> anyway. No, I don't propose to change it.
>
> I take it that overridable by default is part of it. What other things
> are not to your liking?

In no particular order.

1. Qualifiers apply to both reference and instance of a class. Recall 
the ref Object problem and Rebindable!T in Phobos.

(Would be far more fair to simply admit that classes and OOP are about 
MUTABLE state and close the whole const problem (of instance itself) 
with them)

2. Related. Apparently OOP was not ready nor prepared to TLS by default 
scheme. Monitor field per class is an indication of the said fact.

3. Memory management policy coupled with polymorphism. I have plenty of 
use-cases where providing polymorphic interface is great, but there is 
no use in GCing these objects. No emplace and dirty work is not a good 
way out.

4. There is a TypeInfo per class but no type-switch? Just ahwww.

5. Casts look just like plain casts but in fact do dynamic casts. And 
there is no static one in the sight, nor _documented_ way to do it.
*cast(void**)&object is not nice

6. Related to the 1st one - since class instance and reference are 
conflated there is no way to do 'composition':
class A{ ... }
class B{
   A a; // this is delegation, a pointer (!)
   void foo(){ a.foo(); .. }
}
Memory layout goes under the bus. There should have been a way to put 
the contents of class A in the same memory area as B instance.

7. Related to 6. Empty base class optimization, I mean in general using 
0 _extra_ bytes per empty class inside of another one.
Mostly can't be done because of :
a) lack of (any form of) multiple inheritance
b) delegation instead of composition, see also 6

8. No this-call function pointers. Delegates are nice but what's the 
problem with:
class A{ void foo(); }
&A.foo where A is a class to return the equivalent of :
extern(this_call) void foo(A _this);

Having to conjure fake delegates and twiddle with context pointer is 
both inefficient and silly.

...

~~~ Personal matter of taste ~~~

1. This is not taking the fact that I simply do not see sense in going 
with anything beyond a fixed-function duo of:

Traits (Extended interfaces, see e.g. Scala) and final/concrete classes.

Any attempts to reuse code otherwise are welcome with plain composition.

2. The root Object in should be an opaque reference. Hashing, toString, 
opEquals, opCmp belong to standard traits/interfaces.

3. Multiple inheritance with linearizion of inheritance tree.

-- 
Dmitry Olshansky


More information about the Digitalmars-d mailing list