Jump to page: 1 2
Thread overview
Recommended ways to handle final classes
Aug 16, 2013
JS
Aug 16, 2013
Dicebot
Aug 16, 2013
Dicebot
Aug 16, 2013
H. S. Teoh
Aug 16, 2013
Dicebot
Aug 16, 2013
JS
August 16, 2013
Hi all,

I'm writing some class-based code and so my concerns have turned to the issue of final classes vs. inheritance.

Suppose that you want a particular class to be able to act as a base class, but also to perform at top speed.  Is the following kind of pattern worth considering:

    class _A
    {
        // ... implementation, with virtual functions
    }

    final class A : _A
    {
        // ... don't add anything!
    }

...?  Or are there other recommended ways to handle how to get speed while still allowing the option of inheritance?

Thanks & best wishes,

    -- Joe
August 16, 2013
On Friday, 16 August 2013 at 15:12:03 UTC, Joseph Rushton Wakeling wrote:
> Hi all,
>
> I'm writing some class-based code and so my concerns have turned to the issue of
> final classes vs. inheritance.
>
> Suppose that you want a particular class to be able to act as a base class, but
> also to perform at top speed.  Is the following kind of pattern worth considering:
>
>     class _A
>     {
>         // ... implementation, with virtual functions
>     }
>
>     final class A : _A
>     {
>         // ... don't add anything!
>     }
>
> ...?  Or are there other recommended ways to handle how to get speed while still
> allowing the option of inheritance?
>
> Thanks & best wishes,
>
>     -- Joe

Can you describe how this helps?

Sure, if you use A the compiler should be able to optimize method calls but you can't use A in inheritance because it is a final class. e.g.,

A a = new _A; // wrong

_A a = new A; // right, but a will use virtual functions.

The final class only prevents class B : A AFAIK but the vtable is still intact.
August 16, 2013
On 08/16/2013 05:40 PM, JS wrote:
> Can you describe how this helps?
> 
> Sure, if you use A the compiler should be able to optimize method calls but you can't use A in inheritance because it is a final class. e.g.,

The point is, you should be able to use _A for inheritance, A for performance, and they both have the same functionality.

Or am I mistaken?
August 16, 2013
It very is likely that you in fact want A to be a struct and _A - template mixin.

In other words, you are trying to express a relationship that does not really model anything in OOP terms. You proposal will work but it will essentially be just hacky implementation of mixins with some extra informational overhead.

Maybe we can come with a better proposal if you explain what object model are you trying to achieve?
August 16, 2013
On 08/16/2013 06:12 PM, Dicebot wrote:
> Maybe we can come with a better proposal if you explain what object model are you trying to achieve?

The idea is maybe more to be able to have,

    _A  -- base class
    A : _A -- final class that doesn't change _A's functionality
    B : _A -- final class that _does_ override _A's functions

August 16, 2013
On Friday, 16 August 2013 at 16:27:37 UTC, Joseph Rushton Wakeling wrote:
> On 08/16/2013 06:12 PM, Dicebot wrote:
>> Maybe we can come with a better proposal if you explain what object model are
>> you trying to achieve?
>
> The idea is maybe more to be able to have,
>
>     _A  -- base class
>     A : _A -- final class that doesn't change _A's functionality
>     B : _A -- final class that _does_ override _A's functions

That makes some sense indeed, all you need is to stop naming base class "_A" and give it a proper meaningful name that reflects its role in object model :)

From the conceptual purity point of view I still think that having bunch of template mixins instead of _A and just combining them into A or B is the proper way (inheritance should not be used as code reusage tool) - but D may not have powerful enough tools to makes this approach convenient, so resorting to old-school inheritance sounds pragmatical.

You need to be aware though that I am personally an OOP hater and my advices should be reconsidered in with that in mind ;)
August 16, 2013
On Friday, 16 August 2013 at 17:08:36 UTC, Dicebot wrote:
> That makes some sense indeed, all you need is to stop naming base class "_A" and give it a proper meaningful name that reflects its role in object model :)

Hey, it's shorthand :-)

> From the conceptual purity point of view I still think that having bunch of template mixins instead of _A and just combining them into A or B is the proper way (inheritance should not be used as code reusage tool) - but D may not have powerful enough tools to makes this approach convenient, so resorting to old-school inheritance sounds pragmatical.

I'm happy to consider alternative ideas. I find that mixins are very useful like this but also find that stuff tends to gain complexity quite rapidly with their use.

> You need to be aware though that I am personally an OOP hater and my advices should be reconsidered in with that in mind ;)

;-)

Thanks for the thoughts and advice!
August 16, 2013
On Fri, Aug 16, 2013 at 07:51:22PM +0200, Joseph Rushton Wakeling wrote:
> On Friday, 16 August 2013 at 17:08:36 UTC, Dicebot wrote:
[...]
> >From the conceptual purity point of view I still think that having bunch of template mixins instead of _A and just combining them into A or B is the proper way (inheritance should not be used as code reusage tool) - but D may not have powerful enough tools to makes this approach convenient, so resorting to old-school inheritance sounds pragmatical.
> 
> I'm happy to consider alternative ideas. I find that mixins are very useful like this but also find that stuff tends to gain complexity quite rapidly with their use.

This probably doesn't relate directly to your problem at hand, but armed with alias this, D structs can be made to behave as if they had inheritance (though not fully):

	struct Base {
		int x;
		void baseMethod() {}
	}

	struct Derived {
		Base __base;
		alias __base this;

		int y;
		void derivedMethod() {}
	}

	Derived d;
	d.x = 1; // Can access "base struct" members directly
	d.baseMethod();
	d.y = 2; // Obviously can also access "derived" members
	d.derivedMethod();

You can actually implement your own version of OO by careful use of this trick (though it would require manual maintenance and wouldn't have language-level support).

Of course, this isn't necessarily restricted to structs alone. You can also do this with structs that you 'alias this' into classes, to make it appear as though the struct methods are among the class methods. (Unfortunately multiple alias this isn't implemented yet, so this is somewhat limited. But you should be able to pull off some pretty clever tricks with it.)


> >You need to be aware though that I am personally an OOP hater and my advices should be reconsidered in with that in mind ;)
[...]

I'm not an OOP *hater* per se, but I *am* a skeptic about applying OOP anywhere and everywhere, even when it doesn't really belong (*cough*Java*cough*). Different problems need different solutions; insisting on turning a screw with a hammer only leads to poor hacks, boilerplate, and other code smells.

*ahem* http://www.ioccc.org/2005/chia/chia.c *cough* ;-)


T

-- 
"I speak better English than this villain Bush" -- Mohammed Saeed al-Sahaf, Iraqi Minister of Information
August 16, 2013
On Friday, 16 August 2013 at 18:20:46 UTC, H. S. Teoh wrote:
> I'm not an OOP *hater* per se, but I *am* a skeptic about applying OOP
> anywhere and everywhere, even when it doesn't really belong
> (*cough*Java*cough*). Different problems need different solutions;
> insisting on turning a screw with a hammer only leads to poor hacks,
> boilerplate, and other code smells.

I was not really speaking about OOP concept as-is but about traditional implementations in languages like C++ or Java that suggest using class hierarchies for ~everything.

Really loved this article found on reddit few months ago, it pretty much sums up my feelings on topic: https://lwn.net/Articles/548560/ . It speaks about Go and Rust but key point is generic - classical class-based OOP only makes sense if you need a run-time polymorphic behavior (and even than there are exceptions).

I believe that with the development of meta-programming tools completely new approach will emerge eventually.
August 16, 2013
On Friday, 16 August 2013 at 15:47:21 UTC, Joseph Rushton Wakeling wrote:
> On 08/16/2013 05:40 PM, JS wrote:
>> Can you describe how this helps?
>> 
>> Sure, if you use A the compiler should be able to optimize method calls but you
>> can't use A in inheritance because it is a final class. e.g.,
>
> The point is, you should be able to use _A for inheritance, A for performance,
> and they both have the same functionality.
>
> Or am I mistaken?

You can't get them both at the same time though, which you might just make _A final.


The whole point of inheritance is to treat derived classes as if they were base classes.

e.g., code was designed to use class _A, you come along, extend _A to A, but "trick" to the code to use your A(even though it thinks it's an _A).

Now, when you use your final class A where _A's are suppose to be, there can be no optimization made by the compiler(unless it can determine the object really is an A).

e.g.,

class _A { void foo() { writeln(); } }
final class A : _A { }

...

_A a = new A;
...
a.foo(); // can't be inlined because a's type is not CT final.

A aa = new A;
...
aa.foo(); // can be inlined because aa's type is final(no need for vtable)

The only difference is that a is _A and aa is A.


So when you use your final class with inheritance, it becomes useless because it gets treated as the base class, which is not final.

When one does

_A a = new A;

a is of type _A regardless of what we actually stick in a as far as what the compiler see's(unless it's real smart but then, final is not needed).

A final class is simply to stop someone from deriving from it. The compiler can make optimizations based on this info IF it knows that the object is from a final type.


Here is a more real world example


class Shape { }
final class fShape : Shape { }
class Rectangle : Shape { }
final class fRectangle : Rectangle { }

Shape s = new fRectangle; // useless, just use Rectangle, the compiler will probably never know s is an fRectangle. (it might in some cases but probably not worth it)

That is the oop way, this is not:

fShape s = new fRectangle; // invalid, fRectangle is not derived from fShape.
// compiler knows s is a final class and can make optimizations on it.


Because we can't assign concrete shapes to fShape(because they have to derive from them, but can't because the class is final), it becomes useless to use(no way to do oop).


To do oop you have to have derivation from base classes, but final stops that. Hence your two goals(being able to use oop and being able not to(using final) are mutually exclusive).


The only way it can be useful is if the compiler can determine if an object is final at compile time... but if it can do this then it doesn't need to be final in the first place(it will still be able to inline methods).


« First   ‹ Prev
1 2