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