www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - virtual-by-default rant

reply Manu <turkeyman gmail.com> writes:
The virtual model broken. I've complained about it lots, and people always
say "stfu, use 'final:' at the top of your class".

That sounds tolerable in theory, except there's no 'virtual' keyword to
keep the virtual-ness of those 1-2 virtual functions I have... so it's no
good (unless I rearrange my class, breaking the logical grouping of stuff
in it).
So I try that, and when I do, it complains: "Error: variable
demu.memmap.MemMap.machine final cannot be applied to variable", allegedly
a D1 remnant.
So what do I do? Another workaround? Tag everything as final individually?

My minimum recommendation: D needs an explicit 'virtual' keyword, and to
fix that D1 bug, so putting final: at the top of your class works, and
everything from there works as it should.
Mar 17 2012
next sibling parent "Bernard Helyer" <b.helyer gmail.com> writes:
On Sunday, 18 March 2012 at 01:23:42 UTC, Manu wrote:
 My minimum recommendation: D needs an explicit 'virtual' 
 keyword, and to
 fix that D1 bug, so putting final: at the top of your class 
 works, and
 everything from there works as it should.
Agreed. Final by default is a proposition long gone, but that seems reasonable.
Mar 17 2012
prev sibling next sibling parent reply =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
On Sun, 18 Mar 2012 02:23:31 +0100, Manu <turkeyman gmail.com> wrote:

 The virtual model broken. I've complained about it lots, and people  
 always
 say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's no
 good (unless I rearrange my class, breaking the logical grouping of stuff
 in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",  
 allegedly
 a D1 remnant.
 So what do I do? Another workaround? Tag everything as final  
 individually?
class Foo { final { // Final functions here. } // Virtual functions here. } Good?
 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
I agree that a virtual keyword would sometimes be a boon. With the solution outlined above, I find it a minor nit, though.
Mar 17 2012
parent reply Manu <turkeyman gmail.com> writes:
On 18 March 2012 03:49, Simen Kj=C3=A6r=C3=A5s <simen.kjaras gmail.com> wro=
te:

 On Sun, 18 Mar 2012 02:23:31 +0100, Manu <turkeyman gmail.com> wrote:

  The virtual model broken. I've complained about it lots, and people alwa=
ys
 say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's n=
o
 good (unless I rearrange my class, breaking the logical grouping of stuf=
f
 in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable", alleged=
ly
 a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individuall=
y?

 class Foo {
    final {
        // Final functions here.
    }
    // Virtual functions here.
 }

 Good?
Ah, didn't think of braces... got stuck on the ':' approach. That's no less ugly though, in fact, it's considerably more ugly. more brace spam + indentation levels for nothing. My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
I agree that a virtual keyword would sometimes be a boon. With the soluti=
on
 outlined above, I find it a minor nit, though.
I'm still mortified that people won't do it or just forget, and every method ever will be virtual.
Mar 17 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Manu wrote:
 That's no less ugly though, in fact, it's considerably more 
 ugly. more
 brace spam + indentation levels for nothing.
I actually like the bracket+indentation as a section separator indicator. Usually I'll "group" fields/methods by common attribute: class Window { private { string _title; int _x, _y; // etc... } property { auto title() { return _title; } auto x() { return _x; } auto y() { return _y; } // etc... } } // Or... struct Vector2(T) { T x, y; property { auto xy() { return this; } auto yx() { return Vector2(y, x); } } property static { auto zero() { return Vector2(0, 0); } auto one() { return Vector2(1, 1); } // etc... } } before you even get to functions (namespace -> class -> method).
Mar 17 2012
parent reply Manu <turkeyman gmail.com> writes:
On 18 March 2012 04:14, F i L <witte2008 gmail.com> wrote:

 Manu wrote:

 That's no less ugly though, in fact, it's considerably more ugly. more
 brace spam + indentation levels for nothing.
I actually like the bracket+indentation as a section separator indicator. Usually I'll "group" fields/methods by common attribute: class Window { private { string _title; int _x, _y; // etc... } property { auto title() { return _title; } auto x() { return _x; } auto y() { return _y; } // etc... } } // Or... struct Vector2(T) { T x, y; property { auto xy() { return this; } auto yx() { return Vector2(y, x); } } property static { auto zero() { return Vector2(0, 0); } auto one() { return Vector2(1, 1); } // etc... } } even get to functions (namespace -> class -> method).
Yeah, I'm not really into that. I group things conceptually. Either way, I've never written a class where non-virtuals don't outweigh virtuals in the realm of 20:1. There needs to be a way to declare it the other way around without polluting my indentation levels. final: at the top and explicit 'virtual' would make a big difference.
Mar 17 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
I'm a bit confused. Reading through the virtual function's docs 
(http://dlang.org/function.html#virtual-functions) it says:

"All non-static non-private non-template member functions are 
virtual. This may sound inefficient, but since the D compiler 
knows all of the class hierarchy when generating code, all 
functions that are not overridden can be optimized to be 
non-virtual."

So if all functions are automatically optimized to non-virtual 
where applicable, then the "final" keyword is for conceptual 
access limitation only. This makes a lot of sense to me. Is there 
something I'm not getting that makes you want an explicit 
"virtual" keyword?
Mar 17 2012
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
F i L:

 I'm a bit confused. Reading through the virtual function's docs 
 (http://dlang.org/function.html#virtual-functions) it says:
 
 "All non-static non-private non-template member functions are 
 virtual. This may sound inefficient, but since the D compiler 
 knows all of the class hierarchy when generating code, all 
 functions that are not overridden can be optimized to be 
 non-virtual."
This is so much theoretical that I think this should be removed from the D docs. And to be put back when one DMD compiler is able to do this. Otherwise it's just false advertising :-) Bye, bearophile
Mar 17 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
On Sunday, 18 March 2012 at 03:27:40 UTC, bearophile wrote:
 F i L:

 I'm a bit confused. Reading through the virtual function's 
 docs (http://dlang.org/function.html#virtual-functions) it 
 says:
 
 "All non-static non-private non-template member functions are 
 virtual. This may sound inefficient, but since the D compiler 
 knows all of the class hierarchy when generating code, all 
 functions that are not overridden can be optimized to be 
 non-virtual."
This is so much theoretical that I think this should be removed from the D docs. And to be put back when one DMD compiler is able to do this. Otherwise it's just false advertising :-) Bye, bearophile
Dammit, I was afraid someone would say something like that. Well at least it's a good goal. It is a bit of false advertising though, honestly it should just be marked "implementation in progress" or something like that.
Mar 17 2012
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Sun, 18 Mar 2012 04:49:12 +0100
schrieb "F i L" <witte2008 gmail.com>:

 On Sunday, 18 March 2012 at 03:27:40 UTC, bearophile wrote:
 F i L:

 I'm a bit confused. Reading through the virtual function's 
 docs (http://dlang.org/function.html#virtual-functions) it 
 says:
 
 "All non-static non-private non-template member functions are 
 virtual. This may sound inefficient, but since the D compiler 
 knows all of the class hierarchy when generating code, all 
 functions that are not overridden can be optimized to be 
 non-virtual."
This is so much theoretical that I think this should be removed from the D docs. And to be put back when one DMD compiler is able to do this. Otherwise it's just false advertising :-) Bye, bearophile
Dammit, I was afraid someone would say something like that. Well at least it's a good goal. It is a bit of false advertising though, honestly it should just be marked "implementation in progress" or something like that.
"the D compiler knows all of the class hierarchy when generating code" This is just wrong, and if that was the base for deciding on virtual as default, I believe it is natural to think about it again. Otherwise it should read "if you deal with non exported classes and don't use incremental compilation as well as refrain from compiling your code into static libraries, a D compiler can optimize methods to be non-virtual. As of the time of writing [...] no such compiler exists." Now I feel better :) -- Marco
Mar 23 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Marco Leise wrote:
 "the D compiler knows all of the class hierarchy when 
 generating code"
 This is just wrong, and if that was the base for deciding on 
 virtual as default, I believe it is natural to think about it 
 again.
Yes, further reading has led me to believe that Manu is right in his request for a virtual keyword (at least). Final by default is a great concept on paper, but unless it's possible across Lib boundaries (which I'm not sure it is) then to me it does seem a bit backwards given that efficiency is a key feature of D and most programmers are already used to fixed by default anyways.
Mar 23 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/23/2012 02:47 PM, F i L wrote:
 ...  and most programmers are already used
 to fixed by default anyways.
This assertion is unjustified.
Mar 23 2012
parent reply "F i L" <witte2008 gmail.com> writes:
On Friday, 23 March 2012 at 13:58:00 UTC, Timon Gehr wrote:
 On 03/23/2012 02:47 PM, F i L wrote:
 ...  and most programmers are already used
 to fixed by default anyways.
This assertion is unjustified.
Given that the four most popular languages today (Java, C, C++, I also didn't to say Final by default should be default in D (though I wouldn't really disagree with that direction either), I do think D should have a virtual keyword.
Mar 23 2012
parent "F i L" <witte2008 gmail.com> writes:
 Given that the four most popular languages today (Java, C, C++, 

 But I also didn't to say Final by default should be default in 
 D (though I wouldn't really disagree with that direction 
 either), I do think D should have a virtual keyword.
Whoops, that's wrong. Java is virtual by default. So I guess you're right, my statements aren't really justified.
Mar 23 2012
prev sibling next sibling parent Mike Parker <aldacron gmail.com> writes:
On 3/18/2012 12:27 PM, bearophile wrote:
 F i L:

 I'm a bit confused. Reading through the virtual function's docs
 (http://dlang.org/function.html#virtual-functions) it says:

 "All non-static non-private non-template member functions are
 virtual. This may sound inefficient, but since the D compiler
 knows all of the class hierarchy when generating code, all
 functions that are not overridden can be optimized to be
 non-virtual."
This is so much theoretical that I think this should be removed from the D docs. And to be put back when one DMD compiler is able to do this. Otherwise it's just false advertising :-) Bye, bearophile
It says "can be optimized", not "are optimized". Big difference.
Mar 18 2012
prev sibling next sibling parent reply "David Nadlinger" <see klickverbot.at> writes:
On Sunday, 18 March 2012 at 03:27:40 UTC, bearophile wrote:
 F i L:
 "All non-static non-private non-template member functions are 
 virtual. This may sound inefficient, but since the D compiler 
 knows all of the class hierarchy when generating code, all 
 functions that are not overridden can be optimized to be 
 non-virtual."
This is so much theoretical that I think this should be removed from the D docs. And to be put back when one DMD compiler is able to do this. Otherwise it's just false advertising :-)
Is this even possible without LTO/WPO? Extending a class defined in a library you link in (and for which codegen already happened) is certainly possible… David
Mar 18 2012
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 18 March 2012 06:42, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>
 wrote:

 On 3/17/12 9:24 PM, Manu wrote:

 Yeah, I'm not really into that. I group things conceptually.
 Either way, I've never written a class where non-virtuals don't outweigh
 virtuals in the realm of 20:1.
Then probably struct is what you're looking for.
No, I definitely want a class. ref type, gc mem, etc. struct doesn't support virtual at all. I have 2 virtuals, this particular class has around 50 public methods, almost all of which are trivial accessors, called extremely heavily in hot loops. More similar classes to come. I've never in 15 years seen a large-ish class where the majority of methods are virtual. Who writes code like that? It's never come up in my industry at least. Maybe you'll occasionally see it in a small interface class, but D has real interfaces... On 18 March 2012 11:00, David Nadlinger <see klickverbot.at> wrote:
 Is this even possible without LTO/WPO? Extending a class defined in a
 library you link in (and for which codegen already happened) is certainly
 possible=E2=80=A6
It's not possible without LTO, which is crazy. Depending on an advanced optimiser to generate the most basic code is a clear mistake. I think we just need the ability to state 'final:' and mark explicit 'virtual's, the problem is mitigated without breaking the language. I can live with a policy where everyone is instructed to write code that way.
Mar 18 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/18/12 6:37 AM, Manu wrote:
 On 18 March 2012 06:42, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org
 <mailto:SeeWebsiteForEmail erdani.org>> wrote:
     Then probably struct is what you're looking for.


 No, I definitely want a class. ref type, gc mem, etc.
 struct doesn't support virtual at all. I have 2 virtuals, this
 particular class has around 50 public methods, almost all of which are
 trivial accessors, called extremely heavily in hot loops. More similar
 classes to come.
Then perhaps it's a good idea to move accessors outside and take advantage of UFCS.
 I've never in 15 years seen a large-ish class where the majority of
 methods are virtual. Who writes code like that? It's never come up in
 my industry at least.
I consider thick interfaces and shallow hierarchies good design. An interface that's too small invites "inherit to extend" approaches and casts. The fact that Java made "extend" a keyword that really means "narrow" is quite ironic. Andrei
Mar 18 2012
prev sibling parent reply "Martin Nowak" <dawg dawgfoto.de> writes:
 This is so much theoretical that I think this should be removed from =
=
 the D docs. And to be put back when one DMD compiler is able to do  =
 this. Otherwise it's just false advertising :-)
Is this even possible without LTO/WPO? Extending a class defined in a =
=
 library you link in (and for which codegen already happened) is  =
 certainly possible=E2=80=A6

 David
This is not even possible with LTO because new classes could be loaded at runtime. Could somebody please fix this.
Mar 18 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 17:02, Martin Nowak a écrit :
 This is so much theoretical that I think this should be removed from
 the D docs. And to be put back when one DMD compiler is able to do
 this. Otherwise it's just false advertising :-)
Is this even possible without LTO/WPO? Extending a class defined in a library you link in (and for which codegen already happened) is certainly possible… David
This is not even possible with LTO because new classes could be loaded at runtime. Could somebody please fix this.
That is limited to export classes. In this case, final/virtual should be managed very precisely anyway if performance matter.
Mar 18 2012
prev sibling parent "David Nadlinger" <see klickverbot.at> writes:
On Sunday, 18 March 2012 at 16:02:04 UTC, Martin Nowak wrote:
 Is this even possible without LTO/WPO? Extending a class 
 defined in a library you link in (and for which codegen 
 already happened) is certainly possible…

 David
This is not even possible with LTO because new classes could be loaded at runtime.
Sure, you can't just devirtualize everything you come across even with LTO, but it greatly increases the portion of calls where you can deduce the actual type of an instance. David
Mar 18 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-18 04:27, bearophile wrote:
 F i L:

 I'm a bit confused. Reading through the virtual function's docs
 (http://dlang.org/function.html#virtual-functions) it says:

 "All non-static non-private non-template member functions are
 virtual. This may sound inefficient, but since the D compiler
 knows all of the class hierarchy when generating code, all
 functions that are not overridden can be optimized to be
 non-virtual."
This is so much theoretical that I think this should be removed from the D docs. And to be put back when one DMD compiler is able to do this. Otherwise it's just false advertising :-)
I agree that can be misleading. But I think the D docs should be about the D language and not DMD implementation of D. -- /Jacob Carlborg
Mar 18 2012
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On 18 March 2012 04:47, F i L <witte2008 gmail.com> wrote:

 I'm a bit confused. Reading through the virtual function's docs (
 http://dlang.org/function.**html#virtual-functions<http://dlang.org/function.html#virtual-functions>)
 it says:

 "All non-static non-private non-template member functions are virtual.
 This may sound inefficient, but since the D compiler knows all of the class
 hierarchy when generating code, all functions that are not overridden can
 be optimized to be non-virtual."

 So if all functions are automatically optimized to non-virtual where
 applicable, then the "final" keyword is for conceptual access limitation
 only. This makes a lot of sense to me. Is there something I'm not getting
 that makes you want an explicit "virtual" keyword?
It's not dependable. Virtually everything meets those criteria and will be virtual, but I want to be confident that NOTHING is EVER virtual, unless I absolutely say so. D knows nothing about the class hierarchy when generating code, I don't know how it can make that claim? Anything that's not private can be extended by another module, and only the linker could ever know out about that. Aside from that, I want a compile error if someone tries to randomly override stuff. virtuals are a heinous crime, and should only be used explicitly. It should not be possible for someone to accidentally create a virtual.
Mar 18 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Manu wrote:
 D knows nothing about the class hierarchy when generating code, 
 I don't know how it can make that claim?
How does D not know about class hierarchy when generating code? That doesn't make sense to me. It *has* to know to even generate code.
 Anything that's not private can be
 extended by another module, and only the linker could ever know 
 out about that.
This shouldn't be an issue: export void method() // virtual export final void method() // non-virtual
 Aside from that, I want a compile error if someone tries to 
 randomly override stuff.
This only really applies if the compiler can't optimize virtuals away. If the compiler was very good, then getting compiler errors would only make extending object structure a pain, IMO. I can see how one programmer might accidentally create a function with the same name as the base classes name, and how that would be annoying. That's why...
 virtuals are a heinous crime, and should only be used
 explicitly. It should not be possible for someone to 
 accidentally create a virtual.
...I don't think having a virtual keyword would be a bad thing. Still, I think conceptually saying "you _can't_ override this" makes more sense than saying "you _can_ override this" when the biggest reason for using Classes is to build extendable object types. I think at the end of the day both arguments are highly arbitrary. virtual and final keywords could probably exist peacefully, and wouldn't dent the learning curve by much, so I don't have any strong argument against virtual. It's just not the one I'd choose.
Mar 18 2012
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 18 March 2012 13:59, F i L <witte2008 gmail.com> wrote:

 Manu wrote:

 D knows nothing about the class hierarchy when generating code, I don't
 know how it can make that claim?
How does D not know about class hierarchy when generating code? That doesn't make sense to me. It *has* to know to even generate code.
I mean it can't possibly know the complete 'final' class hierarchy, ie, the big picture. Anything anywhere could extend it. The codegen must assume such.
 Aside from that, I want a compile error if someone tries to randomly
 override stuff.
This only really applies if the compiler can't optimize virtuals away. If the compiler was very good, then getting compiler errors would only make extending object structure a pain, IMO. I can see how one programmer might accidentally create a function with the same name as the base classes name, and how that would be annoying. That's why...
Are you saying someone might accidentally override something that's not virtual? That's what 'override' is for. If a method is final, it is a compile error to override in any way, you either need to make the base virtual, or explicitly 'override' on the spot if you want to do that.
 virtuals are a heinous crime, and should only be used
 explicitly. It should not be possible for someone to accidentally create
 a virtual.
...I don't think having a virtual keyword would be a bad thing. Still, I think conceptually saying "you _can't_ override this" makes more sense than saying "you _can_ override this" when the biggest reason for using Classes is to build extendable object types.
I see it precisely the other way around. You still need strict control over precisely HOW to extend that thing. The virtual methods are the exception, not the common case. Explicit virtual even gives a nice informative cue to the programmer just how they are supposed to work with/extend something. You can clearly see what can/should to be extended. Add to that the requirement for an advanced optimiser to clean up the mess with LTO, and the fact a programmer can never have confidence in the final state of the function, I want it the other way around. I sincerely fear finding myself false-virtual hunting on build night until 2am trying to get the game to hold its frame rate (I already do this in C++, but at least you can grep for and validate them!). Or cutting content because we didn't take the time required to manually scan for false virtuals that could have given us more frame time. I think at the end of the day both arguments are highly arbitrary. virtual
 and final keywords could probably exist peacefully, and wouldn't dent the
 learning curve by much, so I don't have any strong argument against
 virtual. It's just not the one I'd choose.
You're welcome to it, but granted that, I have an additional fear that someone with your opinion is capable of writing classes in libs that I might really like to use, but can't, because they are a severe performance hazard. It will be a shame if there is eventually a wealth of D libraries, and only some of them are usable in realtime code because the majority of programmers are blind to this problem. (Again, this is also common in C++. I've encountered many libraries over the years that we had to reject, or even more costly, remove later on after integrating and realising they were unusable)
Mar 18 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Manu wrote:
 I mean it can't possibly know the complete 'final' class 
 hierarchy, ie, the
 big picture. Anything anywhere could extend it. The codegen 
 must assume
 such.
I still don't understand why you think this. The compiler must understand the full hierarchy it's compiling, and like I said before, there are very distinct rules as to what should get virtualed across a lib boundary.
 Are you saying someone might accidentally override something 
 that's not
 virtual? That's what 'override' is for. If a method is final, 
 it is a
 compile error to override in any way, you either need to make 
 the base
 virtual, or explicitly 'override' on the spot if you want to do 
 that.
I was saying that I see how: class Base { // author: Bob void commonNamedMethod() {} } // ~~~~~ class Foo : Base { // author: Steve // didn't look at base class, before writing: void commonNamedMethod() {} // therefor didn't realize he overwrote it } is a valid concern of having things default to virtual. I just don't think it would happen often, but I've been known to be wrong.
 The virtual methods are the exception, not the common case.
I don't thinks it's so black and white, and that's why I like having the compiler make the optimization. I think marking (public) methods you know for sure who's functionality you don't want overwritten is often a smaller case than methods who's functionality could *potentially* be overwritten. By letting the compiler optimize un-overwritten methods, you're giving the Class users more freedom over the grey areas without sacrificing performances. However, I also think the level of freedom is largely dependent on the situation. Given the fact that you write highly optimized and tightly controlled core game engine code, I can see why your perspective leans towards control. Given this specialization unbalance, I think that both virtual and final should be available.
 Explicit virtual even gives a nice informative cue to
 the programmer just how they are supposed to work with/extend 
 something. You can clearly see what can/should to be extended.
This is a good argument. If nothing else, I think there should be a way for Class authors to specify (in a way code-completion can understand) a method attribute which marks a it as being designed to be overwritten.
 I sincerely fear finding myself false-virtual hunting on build 
 night until
 2am trying to get the game to hold its frame rate (I already do 
 this in
 C++, but at least you can grep for and validate them!). Or 
 cutting content
 because we didn't take the time required to manually scan for 
 false
 virtuals that could have given us more frame time.
I think tool that maps hierarchy (showing override) would be best. like: dmd -hierarchy"map.txt"
 You're welcome to it, but granted that, I have an additional 
 fear that
 someone with your opinion is capable of writing classes in libs 
 that I
 might really like to use, but can't, because they are a severe 
 performance
 hazard.
I would argue that any such performance critical libraries should be tightly "finalized" in the first place. I think you're assuming the compiler can't, in good faith, optimize out virtual functions. Whereas I'm assuming it can.
Mar 18 2012
parent reply "David Nadlinger" <see klickverbot.at> writes:
On Sunday, 18 March 2012 at 13:54:20 UTC, F i L wrote:
 […] I think you're assuming the compiler can't, in good 
 faith, optimize out virtual functions. Whereas I'm assuming it 
 can.
Which is wrong as long as you don't do link-time optimization, and DMD probably won't in the foreseeable future. I tried to explain that above, think extending Thread, which has already been compiled into druntime, from your application (which is a bad example, because thread member method calls are most probably not performance sensitive, but you get the point). That's just for the technical details, though, as far as the actual language design is concerned, I don't think virtual by default is an unreasonable choice. David
Mar 18 2012
next sibling parent reply "David Nadlinger" <see klickverbot.at> writes:
On Sunday, 18 March 2012 at 14:27:04 UTC, David Nadlinger wrote:
 Which is wrong as long as you don't do link-time optimization, 
 and DMD probably won't in the foreseeable future. I tried to 
 explain that above, think extending Thread, which has already 
 been compiled into druntime, from your application (which is a 
 bad example, because thread member method calls are most 
 probably not performance sensitive, but you get the point).
Also note that this applies to the general case where you get passed in an arbitrary instance only – if the place where an object is created is in the same translation unit where its methods are invoked, the compiler _might_ be able to prove the runtime type of the instance even without LTO. David
Mar 18 2012
parent "David Nadlinger" <see klickverbot.at> writes:
On Sunday, 18 March 2012 at 14:46:55 UTC, David Nadlinger wrote:
 On Sunday, 18 March 2012 at 14:27:04 UTC, David Nadlinger wrote:
 Which is wrong as long as you don't do link-time optimization, 
 and DMD probably won't in the foreseeable future. I tried to 
 explain that above, think extending Thread, which has already 
 been compiled into druntime, from your application (which is a 
 bad example, because thread member method calls are most 
 probably not performance sensitive, but you get the point).
Also note that this applies to the general case where you get passed in an arbitrary instance only – if the place where an object is created is in the same translation unit where its methods are invoked, the compiler _might_ be able to prove the runtime type of the instance even without LTO.
And thinking even more about it, devirtualization could also be performed by present-day DMD when directly generating an executable with all the modules being passed in via at the command line. This might actually be good enough for smaller projects which don't use separate libraries or incremental compilation. David
Mar 18 2012
prev sibling parent reply "F i L" <witte2008 gmail.com> writes:
David Nadlinger wrote:
 Which is wrong as long as you don't do link-time optimization, 
 and DMD probably won't in the foreseeable future.
Are GDC and LDC limited by DMD in this regard? I know LDC has a LTO flag. If GDC/LDC supports LTO and/or DMD will in the eventual future, then I think defaulting to final is best. If you're saying that even with LTO you wouldn't be able to do automatic de-virtualization ever, then I think Manu might be right in saying the model is backwards. I don't know enough about LTO to comment either way though. FeepingCreature wrote:
 class Foo : Bar final {
 }

 as alternative syntax for

 class Foo : Bar { final {
 } }

 Advantages: internally consistent, no need for completely new 
 syntax, "final class" can be deprecated (it never worked well 
 anyway).

 Alternate aspects of this syntax change:

 void foo(ObjectThing ot, int a, int b) with (ot) {
 }

 void bar() synchronized {
 }
+1 This syntax makes a lot of sense.
Mar 18 2012
parent reply "David Nadlinger" <see klickverbot.at> writes:
On Sunday, 18 March 2012 at 17:24:15 UTC, F i L wrote:
 […] I know LDC has a  LTO flag.
Unfortunately it doesn't (-O4/-O5 are defunct), but working on seamless LTO integration (and better optimization pass scheduling in general) would be low-hanging fruit for anybody wanting to join LDC development. David
Mar 18 2012
parent reply James Miller <james aatch.net> writes:
On 19 March 2012 06:41, David Nadlinger <see klickverbot.at> wrote:
 On Sunday, 18 March 2012 at 17:24:15 UTC, F i L wrote:
 [=E2=80=A6] I know LDC has a =C2=A0LTO flag.
Unfortunately it doesn't (-O4/-O5 are defunct), but working on seamless L=
TO
 integration (and better optimization pass scheduling in general) would be
 low-hanging fruit for anybody wanting to join LDC development.

 David
I think that simply adding a `virtual` keyword that explicitly makes things virtual, even if they would otherwise be final, makes sense. Keep all the current semantics the same, relegate use of `virtual` to the 'advanced' section of D usage, everybody is happy. I'm with Manu in the case of "I don't trust the compiler". I'm perfectly happy for the compile to optimize short sections of code that I probably could optimize myself, but its not much of an issue, but I am reluctant to rely on the tooling to make decisions for me. For small programs, where it doesn't matter if it's half as fast as it could be, but that just means 2ms vs 1ms, I don't care. But in intensive programs, then I want to be sure that the compiler will do what I want. -- James Miller
Mar 18 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 22:36, James Miller a écrit :
 On 19 March 2012 06:41, David Nadlinger<see klickverbot.at>  wrote:
 On Sunday, 18 March 2012 at 17:24:15 UTC, F i L wrote:
 […] I know LDC has a  LTO flag.
Unfortunately it doesn't (-O4/-O5 are defunct), but working on seamless LTO integration (and better optimization pass scheduling in general) would be low-hanging fruit for anybody wanting to join LDC development. David
I think that simply adding a `virtual` keyword that explicitly makes things virtual, even if they would otherwise be final, makes sense. Keep all the current semantics the same, relegate use of `virtual` to the 'advanced' section of D usage, everybody is happy. I'm with Manu in the case of "I don't trust the compiler". I'm perfectly happy for the compile to optimize short sections of code that I probably could optimize myself, but its not much of an issue, but I am reluctant to rely on the tooling to make decisions for me. For small programs, where it doesn't matter if it's half as fast as it could be, but that just means 2ms vs 1ms, I don't care. But in intensive programs, then I want to be sure that the compiler will do what I want. -- James Miller
+1
Mar 19 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/18/12 6:59 AM, F i L wrote:
 Manu wrote:
 D knows nothing about the class hierarchy when generating code, I
 don't know how it can make that claim?
How does D not know about class hierarchy when generating code? That doesn't make sense to me. It *has* to know to even generate code.
It knows about ancestors of each type but not about descendants. Andrei
Mar 18 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 03:47, F i L a écrit :
 I'm a bit confused. Reading through the virtual function's docs
 (http://dlang.org/function.html#virtual-functions) it says:

 "All non-static non-private non-template member functions are virtual.
 This may sound inefficient, but since the D compiler knows all of the
 class hierarchy when generating code, all functions that are not
 overridden can be optimized to be non-virtual."
The compiler can. But ATM, it doesn't. This is an implementation issue, not a language design issue.
Mar 18 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/17/12 9:24 PM, Manu wrote:
 Yeah, I'm not really into that. I group things conceptually.
 Either way, I've never written a class where non-virtuals don't outweigh
 virtuals in the realm of 20:1.
Then probably struct is what you're looking for. Andrei
Mar 17 2012
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 6:23 PM, Manu wrote:
 Tag everything as final individually?
You can use: final { ... all the final members ... } instead of individual tags.
Mar 17 2012
prev sibling next sibling parent reply "F i L" <witte2008 gmail.com> writes:
On Sunday, 18 March 2012 at 01:23:42 UTC, Manu wrote:
 The virtual model broken. I've complained about it lots, and 
 people always
 say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' 
 keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... 
 so it's no
 good (unless I rearrange my class, breaking the logical 
 grouping of stuff
 in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to 
 variable", allegedly
 a D1 remnant.
 So what do I do? Another workaround? Tag everything as final 
 individually?

 My minimum recommendation: D needs an explicit 'virtual' 
 keyword, and to
 fix that D1 bug, so putting final: at the top of your class 
 works, and
 everything from there works as it should.
what's so bad with: class Test { final { void nonVirtualMethod1() { ... } void nonVirtualMethod2() { ... } void nonVirtualMethod3() { ... } void nonVirtualMethod4() { ... } } void virtualMethod1() { ... } void virtualMethod2() { ... } } I actually think this model makes a lot of sense because when you're starting out with just an object concept, explicitly specifying "these functions I don't want overridden" on the few you're positive you don't want final is, at least to me, more inline with how my thought process works. Optimize after the object model is more concrete. However, I don't think that having the option of doing it in reverse would be a bad idea either: // dmd -explicitVirtual test.d class Test { void nonVirtualMethod1() { ... } void nonVirtualMethod2() { ... } void nonVirtualMethod3() { ... } void nonVirtualMethod4() { ... } virtual { void virtualMethod1() { ... } void virtualMethod2() { ... } } }
Mar 17 2012
parent "F i L" <witte2008 gmail.com> writes:
F i L wrote:
 specifying "these functions I don't want overridden" on the few
 you're positive you don't want final is...
**you're positive you *DO* want final is...
Mar 17 2012
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 02:23, Manu a écrit :
 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
This problem isn't virtual by default at all. It would just flip the problem around. It just show the need of keyword to express the opposite of final, virtual. The same problem occur with const immutable, you cannot go back to the mutable world when you use « const: » for example.
Mar 18 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/18/12 8:39 AM, deadalnix wrote:
 It just show the need of keyword to express the opposite of final,
 virtual. The same problem occur with const immutable, you cannot go back
 to the mutable world when you use « const: » for example.
Yah, ~const etc. have been suggested a couple of times. Helps casts too. Andrei
Mar 18 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 16:26, Andrei Alexandrescu a écrit :
 On 3/18/12 8:39 AM, deadalnix wrote:
 It just show the need of keyword to express the opposite of final,
 virtual. The same problem occur with const immutable, you cannot go back
 to the mutable world when you use « const: » for example.
Yah, ~const etc. have been suggested a couple of times. Helps casts too. Andrei
This seems definitively an issue to me. ~const/~final, or mutable/virtual would be a huge benefice to the « : » syntax. And youa re right, it is also a big plus for casting. I would argue for teh last one, especially for the const case, because mutable oppose to both const and immutable, so it isn't the opposite of const. The case is very similar to public/private/protected, and each have it's own keyword, and it doesn't seems armful. Note that the same issue exist for shared (but that one doesn't work anyway).
Mar 18 2012
prev sibling next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 18.03.2012 5:23, Manu wrote:
 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
Following this thread and observing that you don't trust optimizer and compiler in many cases or have to double check them anyway, I have a suggestion: do virtual dispatch by hand via func-pointer table and use structs. I'm serious, with a bit of metaprogramming it wouldn't be half bad, and as a bonus you don't have to pay for a monitor field per object as classes do, and in general less compiler magic to keep track of. You also gain the ability to fine tune their layout, the performance maniac side of yours must see the potential it brings :) And since you have a few of virtuals anyway and keep them in constant check it should be double and be much less of a hassle then hunting down and second-guessing the compiler on every single step. Bottom line thought: there is a point when a given feature doesn't bring significant convenience for a specific use case, it's then better to just stop pushing it over. -- Dmitry Olshansky
Mar 18 2012
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 03/18/12 15:37, Dmitry Olshansky wrote:
 On 18.03.2012 5:23, Manu wrote:
 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
Following this thread and observing that you don't trust optimizer and compiler in many cases or have to double check them anyway, I have a suggestion: do virtual dispatch by hand via func-pointer table and use structs. I'm serious, with a bit of metaprogramming it wouldn't be half bad, and as a bonus you don't have to pay for a monitor field per object as classes do, and in general less compiler magic to keep track of. You also gain the ability to fine tune their layout, the performance maniac side of yours must see the potential it brings :)
I was going to suggest the very same thing - but there are (at least) two problems with that approach: 1) pass-by-value -- it's dangerous, ugly to work-around (and compiler bugs don't help, like the one where just having a "this(this)" causes problems); the workarounds also have compiler/ABI issues (like the 'File' case posted in D.learn some time ago, or GDC not passing/returning the pseudo-refs in registers) 2) no inheritance. ie 'struct A{}; struct B{A super; alias super this;}' cannot be written as just 'struct B:A {}' - which would not be just syntax sugar, but also allow (more) implicit conversions, (explicit) function overrides etc. So - yes, D structs should be enough for everything, but right now they're still missing some required basic features. Ideally "class" would just be sugar, and everything should be expressible using just structs - obviously in a much more verbose, but 100% compatible way (incl vtables, monitors etc) After all, real programmers don't use classes. :) artur
Mar 18 2012
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 19.03.2012 2:17, Artur Skawina wrote:
 On 03/18/12 15:37, Dmitry Olshansky wrote:
 On 18.03.2012 5:23, Manu wrote:
 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
Following this thread and observing that you don't trust optimizer and compiler in many cases or have to double check them anyway, I have a suggestion: do virtual dispatch by hand via func-pointer table and use structs. I'm serious, with a bit of metaprogramming it wouldn't be half bad, and as a bonus you don't have to pay for a monitor field per object as classes do, and in general less compiler magic to keep track of. You also gain the ability to fine tune their layout, the performance maniac side of yours must see the potential it brings :)
I was going to suggest the very same thing - but there are (at least) two problems with that approach: 1) pass-by-value -- it's dangerous, ugly to work-around (and compiler bugs don't help, like the one where just having a "this(this)" causes problems); the workarounds also have compiler/ABI issues (like the 'File' case posted in D.learn some time ago, or GDC not passing/returning the pseudo-refs in registers)
GDC not passing pseudo-refs in registers is cleanly a non-issue, in a sense, that it's not a good excuse at all, as well the other bugs. All in all, nobody is going to kill you if in performance sensitive code you'd use pointers: BigStruct* my_big = allocateSomewhere(...ctor_args...); //ultimately using emplace
 2) no inheritance. ie 'struct A{}; struct B{A super; alias super this;}'
cannot be
     written as just 'struct B:A {}' - which would not be just syntax sugar,
but also
     allow (more) implicit conversions, (explicit) function overrides etc.
Template mixins? I envision: struct Foo{ mixin Inherit!(Bar); } I see that it is not a cake-walk but acceptable for the special nature of requirements.
 So - yes, D structs should be enough for everything, but right now they're
still
 missing some required basic features. Ideally "class" would just be sugar, and
 everything should be expressible using just structs - obviously in a much more
 verbose, but 100% compatible way (incl vtables, monitors etc)
Yes, that the point. And if one doesn't like this kind of compiler sugar, he is free to synthesize Xylitol. -- Dmitry Olshansky
Mar 19 2012
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 03/19/12 08:30, Dmitry Olshansky wrote:
 On 19.03.2012 2:17, Artur Skawina wrote:
 On 03/18/12 15:37, Dmitry Olshansky wrote:
 On 18.03.2012 5:23, Manu wrote:
 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
Following this thread and observing that you don't trust optimizer and compiler in many cases or have to double check them anyway, I have a suggestion: do virtual dispatch by hand via func-pointer table and use structs. I'm serious, with a bit of metaprogramming it wouldn't be half bad, and as a bonus you don't have to pay for a monitor field per object as classes do, and in general less compiler magic to keep track of. You also gain the ability to fine tune their layout, the performance maniac side of yours must see the potential it brings :)
I was going to suggest the very same thing - but there are (at least) two problems with that approach: 1) pass-by-value -- it's dangerous, ugly to work-around (and compiler bugs don't help, like the one where just having a "this(this)" causes problems); the workarounds also have compiler/ABI issues (like the 'File' case posted in D.learn some time ago, or GDC not passing/returning the pseudo-refs in registers)
GDC not passing pseudo-refs in registers is cleanly a non-issue, in a sense, that it's not a good excuse at all, as well the other bugs.
Something that should work in theory, but does not behave as expected, *is* an issue, if it means you can't actually use that solution right now. [Note the GDC problem may or may not still be there; i tried it a while ago; the other issues cause enough trouble anyway]
 All in all, nobody is going to kill you if in performance sensitive code you'd
use pointers:
 
 BigStruct* my_big = allocateSomewhere(...ctor_args...); //ultimately using
emplace
struct A{} struct B{A sup; alias sup this;} void f1(A* a) {/*...*/} // Fail: void f2(B* b) {f1(b);/*...*/} A* b = new B; // And, yes, "void f1(ref A a);" etc would work, but then it's just a question // of time before you'll end up searching the whole project for erroneous struct // copies, instead of virtuals. And the virtual methods are easier to find...
 2) no inheritance. ie 'struct A{}; struct B{A super; alias super this;}'
cannot be
     written as just 'struct B:A {}' - which would not be just syntax sugar,
but also
     allow (more) implicit conversions, (explicit) function overrides etc.
Template mixins? I envision: struct Foo{ mixin Inherit!(Bar); } I see that it is not a cake-walk but acceptable for the special nature of requirements.
This thread was about larger non-trivial projects, and the difficulty in finding all methods that do not need to be virtual -- i don't know if replacing the whole class hierarchy with a template-mixin-wrapped-in-structs hierarchy would really be such a good idea. ;) Also, see the above example - doing struct inheritance by hand, w/o compiler help, quickly gets ugly and dangerous.
 So - yes, D structs should be enough for everything, but right now they're
still
 missing some required basic features. Ideally "class" would just be sugar, and
 everything should be expressible using just structs - obviously in a much more
 verbose, but 100% compatible way (incl vtables, monitors etc)
Yes, that the point. And if one doesn't like this kind of compiler sugar, he is free to synthesize Xylitol.
I'm saying "use structs instead of classes" is a good suggestion, but *right now* the language and compiler do not provide enough support to make this practical. There's a lot of room for (backwards compatible) improvements, though. artur
Mar 19 2012
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 19.03.2012 14:45, Artur Skawina wrote:
 On 03/19/12 08:30, Dmitry Olshansky wrote:
 On 19.03.2012 2:17, Artur Skawina wrote:
 On 03/18/12 15:37, Dmitry Olshansky wrote:
 On 18.03.2012 5:23, Manu wrote:
 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
Following this thread and observing that you don't trust optimizer and compiler in many cases or have to double check them anyway, I have a suggestion: do virtual dispatch by hand via func-pointer table and use structs. I'm serious, with a bit of metaprogramming it wouldn't be half bad, and as a bonus you don't have to pay for a monitor field per object as classes do, and in general less compiler magic to keep track of. You also gain the ability to fine tune their layout, the performance maniac side of yours must see the potential it brings :)
I was going to suggest the very same thing - but there are (at least) two problems with that approach: 1) pass-by-value -- it's dangerous, ugly to work-around (and compiler bugs don't help, like the one where just having a "this(this)" causes problems); the workarounds also have compiler/ABI issues (like the 'File' case posted in D.learn some time ago, or GDC not passing/returning the pseudo-refs in registers)
GDC not passing pseudo-refs in registers is cleanly a non-issue, in a sense, that it's not a good excuse at all, as well the other bugs.
Something that should work in theory, but does not behave as expected, *is* an issue, if it means you can't actually use that solution right now. [Note the GDC problem may or may not still be there; i tried it a while ago; the other issues cause enough trouble anyway]
 All in all, nobody is going to kill you if in performance sensitive code you'd
use pointers:

 BigStruct* my_big = allocateSomewhere(...ctor_args...); //ultimately using
emplace
struct A{} struct B{A sup; alias sup this;} void f1(A* a) {/*...*/} // Fail: void f2(B* b) {f1(b);/*...*/} A* b = new B; // And, yes, "void f1(ref A a);" etc would work, but then it's just a question // of time before you'll end up searching the whole project for erroneous struct // copies, instead of virtuals. And the virtual methods are easier to find...
Sure sounds like fun challenge, that's my start before being killed by compiler internal error (damn, it's an *issue* after all) Assertion failure: 't' on line 7911 in file 'mtype.c' Anyway here is the code: struct A{ int a; } template Inherit(alias X) { X __super; alias __super this; } struct B{ mixin Inherit!A; int b; } struct PolyPtr(X)//no opaque pointers or loose polymorphism { X* _payload; static if(is(typeof(X.init.__super))) { alias typeof(X.init.__super) Super; //chain up the rest of possible super classes property auto getSuper(){ return PolyPtr!Super(&_payload.__super); } alias getSuper this; } // alias _payload this;//multiple alias this, sigh auto opDispatch(string s)(){ return mixin("_payload."~s); } } template create(X) { PolyPtr!X create(X, T...)(T args){ return PolyPtr!X(args); } } void f1(PolyPtr!A a) {/*...*/} void f2(PolyPtr!B b) {f1(b);/*...*/} void main(){ auto b = create!B(42, 31); }
 2) no inheritance. ie 'struct A{}; struct B{A super; alias super this;}'
cannot be
      written as just 'struct B:A {}' - which would not be just syntax sugar,
but also
      allow (more) implicit conversions, (explicit) function overrides etc.
Template mixins? I envision: struct Foo{ mixin Inherit!(Bar); } I see that it is not a cake-walk but acceptable for the special nature of requirements.
This thread was about larger non-trivial projects, and the difficulty in finding all methods that do not need to be virtual -- i don't know if replacing the whole class hierarchy with a template-mixin-wrapped-in-structs hierarchy would really be such a good idea. ;) Also, see the above example - doing struct inheritance by hand, w/o compiler help, quickly gets ugly and dangerous.
 So - yes, D structs should be enough for everything, but right now they're
still
 missing some required basic features. Ideally "class" would just be sugar, and
 everything should be expressible using just structs - obviously in a much more
 verbose, but 100% compatible way (incl vtables, monitors etc)
Yes, that the point. And if one doesn't like this kind of compiler sugar, he is free to synthesize Xylitol.
I'm saying "use structs instead of classes" is a good suggestion, but *right now* the language and compiler do not provide enough support to make this practical. There's a lot of room for (backwards compatible) improvements, though. artur
-- Dmitry Olshansky
Mar 19 2012
prev sibling next sibling parent reply FeepingCreature <default_357-line yahoo.de> writes:
On 03/18/12 02:23, Manu wrote:
 The virtual model broken. I've complained about it lots, and people always say
"stfu, use 'final:' at the top of your class".
 
 That sounds tolerable in theory, except there's no 'virtual' keyword to keep
the virtual-ness of those 1-2 virtual functions I have... so it's no good
(unless I rearrange my class, breaking the logical grouping of stuff in it).
 So I try that, and when I do, it complains: "Error: variable
demu.memmap.MemMap.machine final cannot be applied to variable", allegedly a D1
remnant.
 So what do I do? Another workaround? Tag everything as final individually?
 
 My minimum recommendation: D needs an explicit 'virtual' keyword, and to fix
that D1 bug, so putting final: at the top of your class works, and everything
from there works as it should.
See subject. Example; class Foo : Bar final { } as alternative syntax for class Foo : Bar { final { } } Advantages: internally consistent, no need for completely new syntax, "final class" can be deprecated (it never worked well anyway). Alternate aspects of this syntax change: void foo(ObjectThing ot, int a, int b) with (ot) { } void bar() synchronized { }
Mar 18 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/18/2012 05:25 PM, FeepingCreature wrote:
 Advantages: internally consistent, no need for completely new syntax, "final
class" can be deprecated (it never worked well anyway).
final class means that the class cannot be inherited from.
Mar 18 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 17:49, Timon Gehr a écrit :
 On 03/18/2012 05:25 PM, FeepingCreature wrote:
 Advantages: internally consistent, no need for completely new syntax,
 "final class" can be deprecated (it never worked well anyway).
final class means that the class cannot be inherited from.
What is the point to inherit is no virtual method exists ?
Mar 18 2012
next sibling parent reply =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
On Sun, 18 Mar 2012 18:07:10 +0100, deadalnix <deadalnix gmail.com> wrot=
e:

 Le 18/03/2012 17:49, Timon Gehr a =C3=A9crit :
 On 03/18/2012 05:25 PM, FeepingCreature wrote:
 Advantages: internally consistent, no need for completely new syntax=
,
 "final class" can be deprecated (it never worked well anyway).
final class means that the class cannot be inherited from.
What is the point to inherit is no virtual method exists ?
Access to protected members.
Mar 18 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 18:06, Simen Kjærås a écrit :
 On Sun, 18 Mar 2012 18:07:10 +0100, deadalnix <deadalnix gmail.com> wrote:

 Le 18/03/2012 17:49, Timon Gehr a écrit :
 On 03/18/2012 05:25 PM, FeepingCreature wrote:
 Advantages: internally consistent, no need for completely new syntax,
 "final class" can be deprecated (it never worked well anyway).
final class means that the class cannot be inherited from.
What is the point to inherit is no virtual method exists ?
Access to protected members.
That is a reason, but I never saw that except for dirty hacks :D
Mar 19 2012
parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
Having a final class is conceptually different from having a class
with only final methods. You can legitimately inherit from a class
with no virtual methods and add to its interface and implement other
methods using its final ones. The final class concept is extremely
useful when you just don't want to be inherited from. For instance,
Your class may be closely tied to an implementation and inheriting
from it would wreck the implementation.

On Mon, Mar 19, 2012 at 2:04 PM, deadalnix <deadalnix gmail.com> wrote:
 Le 18/03/2012 18:06, Simen Kj=C3=A6r=C3=A5s a =C3=A9crit :
 On Sun, 18 Mar 2012 18:07:10 +0100, deadalnix <deadalnix gmail.com> wrot=
e:
 Le 18/03/2012 17:49, Timon Gehr a =C3=A9crit :
 On 03/18/2012 05:25 PM, FeepingCreature wrote:
 Advantages: internally consistent, no need for completely new syntax,
 "final class" can be deprecated (it never worked well anyway).
final class means that the class cannot be inherited from.
What is the point to inherit is no virtual method exists ?
Access to protected members.
That is a reason, but I never saw that except for dirty hacks :D
--=20 Bye, Gor Gyolchanyan.
Mar 19 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/03/2012 14:38, Gor Gyolchanyan a écrit :
 Having a final class is conceptually different from having a class
 with only final methods. You can legitimately inherit from a class
 with no virtual methods and add to its interface and implement other
 methods using its final ones. The final class concept is extremely
 useful when you just don't want to be inherited from. For instance,
 Your class may be closely tied to an implementation and inheriting
 from it would wreck the implementation.
It looks like a bad usage of inheritance. If you want to use these methods, why not use composition ? It make no sense to use such an inherited class in a polymorphic context, so why use inheritance at all ?
Mar 19 2012
next sibling parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
Because of the syntax sugar.

On Mon, Mar 19, 2012 at 8:26 PM, deadalnix <deadalnix gmail.com> wrote:
 Le 19/03/2012 14:38, Gor Gyolchanyan a =C3=A9crit :

 Having a final class is conceptually different from having a class
 with only final methods. You can legitimately inherit from a class
 with no virtual methods and add to its interface and implement other
 methods using its final ones. The final class concept is extremely
 useful when you just don't want to be inherited from. For instance,
 Your class may be closely tied to an implementation and inheriting
 from it would wreck the implementation.
It looks like a bad usage of inheritance. If you want to use these method=
s,
 why not use composition ?

 It make no sense to use such an inherited class in a polymorphic context,=
so
 why use inheritance at all ?
--=20 Bye, Gor Gyolchanyan.
Mar 19 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 19/03/2012 17:28, Gor Gyolchanyan a écrit :
 Because of the syntax sugar.
This is exactly the type of behavior that a language should discourage.
Mar 19 2012
prev sibling parent reply "F i L" <witte2008 gmail.com> writes:
deadalnix wrote:
 It looks like a bad usage of inheritance. If you want to use 
 these methods, why not use composition ?

 It make no sense to use such an inherited class in a 
 polymorphic context, so why use inheritance at all ?
Am I missing something about what you're saying? Having a final class is completely different than locking up it's functions: abstruct class Actor { void create() {} void update() {} } class Ship : Actor { final void create() {} final void update() {} final void fire() {} } class MotherShip : Ship { final void fireMissle() {} } void main() { Actor[] actors = [ new Ship(); new MotherShip(); ]; foreach (actor; actors) { actor.fire(); } } If Ship was final simply because all it's methods where, then you couldn't inherit it's functionality into MotherShip.
Mar 19 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/03/2012 18:41, F i L a écrit :
 deadalnix wrote:
 It looks like a bad usage of inheritance. If you want to use these
 methods, why not use composition ?

 It make no sense to use such an inherited class in a polymorphic
 context, so why use inheritance at all ?
Am I missing something about what you're saying? Having a final class is completely different than locking up it's functions: abstruct class Actor { void create() {} void update() {} } class Ship : Actor { final void create() {} final void update() {} final void fire() {} } class MotherShip : Ship { final void fireMissle() {} } void main() { Actor[] actors = [ new Ship(); new MotherShip(); ]; foreach (actor; actors) { actor.fire(); } } If Ship was final simply because all it's methods where, then you couldn't inherit it's functionality into MotherShip.
That is totally broken design. The fact that mothership inherit from ship is a pretty good example of misusing inheritance. The fireMissle is useless unless you KNOW that you are manipulating a MotherShip, so you totally break all the abstraction the OOP could provide.
Mar 19 2012
parent reply "F i L" <witte2008 gmail.com> writes:
deadalnix wrote:
 That is totally broken design. The fact that mothership inherit 
 from ship is a pretty good example of misusing inheritance. The 
 fireMissle is useless unless you KNOW that you are manipulating 
 a MotherShip, so you totally break all the abstraction the OOP 
 could provide.
Other objects can contain references to MotherShip types directly, while the main update loop works with the base type. Or, perhaps more relevant, an object could execute "fire()" on a list of Ships, which MotherShip can be a part of because it IS a Ship, regardless if weather it can overwrite Ship's functions or not. I can't imagine a different way of defining this relationship short of inheriting from a common interface which would require duplicating the fire() function in both types (given that fire() is designed to ensure common behavior among all Ship types). This is not a broken concept at all. I'm always open to different ideas, but I'm fairly surprised you're arguing this case. Being able to extend a type, even when the extensions are entirely additive, is a fundamental principle of OOP design.
Mar 19 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/03/2012 21:39, F i L a écrit :
 deadalnix wrote:
 That is totally broken design. The fact that mothership inherit from
 ship is a pretty good example of misusing inheritance. The fireMissle
 is useless unless you KNOW that you are manipulating a MotherShip, so
 you totally break all the abstraction the OOP could provide.
Other objects can contain references to MotherShip types directly, while the main update loop works with the base type. Or, perhaps more relevant, an object could execute "fire()" on a list of Ships, which MotherShip can be a part of because it IS a Ship, regardless if weather it can overwrite Ship's functions or not. I can't imagine a different way of defining this relationship short of inheriting from a common interface which would require duplicating the fire() function in both types (given that fire() is designed to ensure common behavior among all Ship types).
As all functionnality of a Ship are final, you basically ends up with a mother ship that is a ship and then have some other function that are de facto separated and unreachable from ship. Separation of concerns tells us that this should be 2 different objects.
 This is not a broken concept at all. I'm always open to different ideas,
 but I'm fairly surprised you're arguing this case. Being able to extend
 a type, even when the extensions are entirely additive, is a fundamental
 principle of OOP design.
As teha ddition of MotherShip to Ship are orthogonal, I would argue that mothership functionality is better suited for its own class/struct/whatever, and agregated with a ship. The whole « is a » thing is largely misunderstood. See http://www.objectmentor.com/resources/articles/lsp.pdf for example.
Mar 19 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/19/12 5:11 PM, deadalnix wrote:
 Le 19/03/2012 21:39, F i L a écrit :
 deadalnix wrote:
 That is totally broken design. The fact that mothership inherit from
 ship is a pretty good example of misusing inheritance. The fireMissle
 is useless unless you KNOW that you are manipulating a MotherShip, so
 you totally break all the abstraction the OOP could provide.
Other objects can contain references to MotherShip types directly, while the main update loop works with the base type. Or, perhaps more relevant, an object could execute "fire()" on a list of Ships, which MotherShip can be a part of because it IS a Ship, regardless if weather it can overwrite Ship's functions or not. I can't imagine a different way of defining this relationship short of inheriting from a common interface which would require duplicating the fire() function in both types (given that fire() is designed to ensure common behavior among all Ship types).
As all functionnality of a Ship are final, you basically ends up with a mother ship that is a ship and then have some other function that are de facto separated and unreachable from ship. Separation of concerns tells us that this should be 2 different objects.
 This is not a broken concept at all. I'm always open to different ideas,
 but I'm fairly surprised you're arguing this case. Being able to extend
 a type, even when the extensions are entirely additive, is a fundamental
 principle of OOP design.
As teha ddition of MotherShip to Ship are orthogonal, I would argue that mothership functionality is better suited for its own class/struct/whatever, and agregated with a ship. The whole « is a » thing is largely misunderstood. See http://www.objectmentor.com/resources/articles/lsp.pdf for example.
I agree 100%. Inheriting to extend is borderline fallacious. Regarding the matter at hand, it's true that the interplay of final class with final methods has an odd and less useful corner case, but I don't think it's worth worrying about it. Andrei
Mar 19 2012
prev sibling parent "F i L" <witte2008 gmail.com> writes:
deadalnix wrote:
 As all functionnality of a Ship are final, you basically ends 
 up with a mother ship that is a ship and then have some other 
 function that are de facto separated and unreachable from ship.
Yes, because Ship is designed to be the common denominator among Ship types. This is not a problem, it's simply the far right side of access-limitation. There's no need to abstract the type (as you suggest below) simply because the base class went one function "to tight".
 Separation of concerns tells us that this should be 2 different 
 objects.
MotherShip is still concerned with being a Ship. It's intended as a Ship, and should be classified as such. Removing the ability to express this only forces coders into needlessly wrapping functions: class Ship { final void fire() {} } class Bomber : Ship { void bomb() {} } void main() { auto b = new Bomber(); b.fire(); // can fire() like other Ships automatically } vs: class Bomber { Ship ship; void fire() { ship.fire(); } // have to wrap this } This would be very annoying if you had many functions in the base class and would probably lead the authors writing a single unused virtual function into the base, just so they could derive types from it.
 As teha ddition of MotherShip to Ship are orthogonal, I would 
 argue that mothership functionality is better suited for its 
 own class/struct/whatever, and agregated with a ship.
A Ship wrapper is not conceptually a Ship. Nor could you cast a Ship pointer to it. All you're doing by aggregating types is complicating the structure for no-gain and loosing expression in the process.
Mar 19 2012
prev sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/18/12, deadalnix <deadalnix gmail.com> wrote:
 Le 18/03/2012 17:49, Timon Gehr a =E9crit :
 On 03/18/2012 05:25 PM, FeepingCreature wrote:
 Advantages: internally consistent, no need for completely new syntax,
 "final class" can be deprecated (it never worked well anyway).
final class means that the class cannot be inherited from.
What is the point to inherit is no virtual method exists ?
Final classes might be useful for e.g. leaf classes.
Mar 18 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 19:57, Andrej Mitrovic a écrit :
 On 3/18/12, deadalnix<deadalnix gmail.com>  wrote:
 Le 18/03/2012 17:49, Timon Gehr a écrit :
 On 03/18/2012 05:25 PM, FeepingCreature wrote:
 Advantages: internally consistent, no need for completely new syntax,
 "final class" can be deprecated (it never worked well anyway).
final class means that the class cannot be inherited from.
What is the point to inherit is no virtual method exists ?
Final classes might be useful for e.g. leaf classes.
This doesn't address the question.
Mar 19 2012
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/18/2012 9:25 AM, FeepingCreature wrote:
 See subject.
When proposing a new feature, the question is not "why not?". The question is what compelling capability does it bring.
Mar 19 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 19/03/2012 21:43, Walter Bright a écrit :
 On 3/18/2012 9:25 AM, FeepingCreature wrote:
 See subject.
When proposing a new feature, the question is not "why not?". The question is what compelling capability does it bring.
In this case, this isn't really a feature, but more syntaxic sugar. Anyway, I don't think this is that important.
Mar 19 2012
prev sibling parent reply Ary Manzana <ary esperanto.org.ar> writes:
On 3/18/12 9:23 AM, Manu wrote:
 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
Is virtual-ness your performance bottleneck?
Mar 23 2012
parent reply Manu <turkeyman gmail.com> writes:
On 23 March 2012 17:24, Ary Manzana <ary esperanto.org.ar> wrote:

 On 3/18/12 9:23 AM, Manu wrote:

 The virtual model broken. I've complained about it lots, and people
 always say "stfu, use 'final:' at the top of your class".

 That sounds tolerable in theory, except there's no 'virtual' keyword to
 keep the virtual-ness of those 1-2 virtual functions I have... so it's
 no good (unless I rearrange my class, breaking the logical grouping of
 stuff in it).
 So I try that, and when I do, it complains: "Error: variable
 demu.memmap.MemMap.machine final cannot be applied to variable",
 allegedly a D1 remnant.
 So what do I do? Another workaround? Tag everything as final individually?

 My minimum recommendation: D needs an explicit 'virtual' keyword, and to
 fix that D1 bug, so putting final: at the top of your class works, and
 everything from there works as it should.
Is virtual-ness your performance bottleneck?
Frequently. It's often the most expensive 'trivial' operation many processors can be asked to do. Senior programmers (who have much better things to waste their time on considering their pay bracket) frequently have to spend late nights mitigating this even in C++ where virtual isn't default. In D, I'm genuinely concerned by this prospect. Now I can't just grep for virtual and fight them off, which is time consuming alone, I will need to take every single method, one by one, prove it is never overloaded anywhere (hard to do), before I can even begin the normal process of de-virtualising it like you do in C++. The problem is elevated by the fact that many programmers are taught in university that virtual functions are okay. They come to the company, write code how they were taught in university, and then we're left to fix it up on build night when we can't hold our frame rate. virtual functions and scattered/redundant memory access are usually the first thing you go hunting for. Fixing virtuals is annoying when the system was designed to exploit them, it often requires some extensive refactoring, much harder to fix than a bad memory access pattern, which might be as simple as rearranging a struct.
Mar 23 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
Something that *might* help is to do unit tests. Yeah,
that's kinda ass, but it would catch a stray virtual early.


Do a unit test that does a traits check for virtuals:

http://dlang.org/traits.html#getVirtualFunctions

if the name isn't on a list of approved virtuals,
static assert fail.

You'd then maintain the list of approved virtuals
in the unit test, where it is easier to check over.


idk though, I've never worked on a project like this.
Mar 23 2012
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 23 March 2012 21:11, Adam D. Ruppe <destructionator gmail.com> wrote:

 Something that *might* help is to do unit tests. Yeah,
 that's kinda ass, but it would catch a stray virtual early.
They're not necessarily 'stray' virtuals, they're inappropriate ones There's a time and place for virtuals, but there's many more times and places they shouldn't be :) It's nice to be able to skim a class and see 'virtual' written clearly all over it to gather some basic performance/usage information about the class. The visual cue is important, and the ability to grep for them. Rememer that virtuals are Do a unit test that does a traits check for virtuals:


 if the name isn't on a list of approved virtuals,
 static assert fail.
I've said this all before, but I'll repeat my general reasoning on the issue. My objection to virtual-by-default, but, acknowledging that can't be changed, insistence on a virtual keyword is this. (uh oh, manu-rant alert) :P The top most compelling reason for switching from an extremely mature, efficient, reliable, commercially accepted language like C/C++ to something still relatively experimental (D) is to simplify code and maintenance long term. The goal is to do the same work we already do with less lines of code. They should also be cleaner, tidier, less confusing, more informative lines of code. Adding a system like you describe to validate virtuals is not a complexity I'm interested in implementing, it is just one clear reason to sick with C++. Imagine presenting that to a building full of programmers who are sceptical about changing from C++ in the first place? What will they make of such a crude requirement when they already have a perfectly good virtual keyword in C++? D offers some amazing steps forwards in terms of powerful meta-programming. Many typical C/C++ systems that require maintaining (and synchronising) ugly tables of data, enums, stupid little functions and macros to do trivial stuff; these can largely be removed in D. It is possible to invent systems that take code and comprehend it implicitly, generating the desired functionality, without having to pollute the classes themselves with extra rubbish used by such a generator (see: user attributes thread). Removing this sort of crap is something everyone can appreciate. But if we have to revert to this behaviour to work around a different set of problems, then we haven't really gained anything. Code that remains tidy and maintainable for 10+ years (common in gamedev, game engines live ~2 console generations on average, often longer), but is still very fluid and dynamic (how gamedev code differs from other enterprise code, it changes frequently), and doesn't degrade in performance due to added complexity over years, is, fundamentally, SIMPLE code. It is also *LESS* code, the fewer lines, the more maintainable as a rule. And most certainly, code WITHOUT explicit tables of codegen related data; these are always the first things to rot and fall out of sync. There are often weird ancillary systems grown around these things to try and auto-magically maintain them, which themselves are just redundant noise, and contributes to further complexity and eventual bitrot. I've seen it time and time again. This is C++'s biggest shortcoming; the language only goes 90% of the way, and untold complexity is added to achieve the final 10%. These such sorts of tables are why I fear an enum based serialisation system, or an enum based virtual function verification system. Who maintains these tables? It's only a matter of time before some clever bugger comes along and writes some fancy system to auto-magically manage these tables, and then every other programmer comes along, has no idea what to make of it anymore, and wonders what said clever bugger was smoking. This is just how it works out in practise. Then said clever bugger quits...
_<
This is one of the key pains I hope to eliminate by using D, but I can't trade performance for it. The bar is high, competition is staunch, games consoles are fixed hardware. All the performance tuning mechanisms available in C/C++ must also be available in D (and generally are, plus more waiting to be taken advantage of). I shouldn't need to add complex workarounds for something so trivial as the missing virtual keyword :) Side note.. I'm a language nerd, and I get excited by D, but I'm trying to think critically about transplanting it realistically into the commercial environment. I know what happens there, and it can only be adopted if a significant numer of C++'s shortcoings are overcome, AND no new shortcomings are added in the process. Most people aren't interested in D, they may be sceptical and apprehensive to abandon something with 40 years of maturity and a whole industry of support. They will not take the risk to their business for a small number of improvements, especially if they lose ANYTHING in the process. It is in my interest to prove to those people undeniably that D is a step forward in every regard. I believe that is what it will take to allow it to be adopted widely in the commercial software world. D is a competitor to C++. I know there are a lot that might disagree with me, maybe it explores some other avenues too, but in terms of direct competition to C++ (still the only real choice for realtime programming on limited systems), there is NOTHING ELSE. I think D needs to embrace what it is; a native systems language (finally) introducing modern concepts, and by extension, needs to take performance very seriously. SIMD support is a great start, I really appreciate that one appearing out of nowhere almost overnight! :)
Mar 24 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 24 March 2012 at 10:56:27 UTC, Manu wrote:
 My objection to virtual-by-default, but, acknowledging that 
 can't be changed, insistence on a virtual keyword
 is this.
I like the idea of a virtual keyword btw.
 Adding a system like you describe to validate virtuals is not a 
 complexity I'm interested in implementing
You already have! Let me quote one of your previous posts: "I sincerely fear finding myself false-virtual hunting on build night until 2am trying to get the game to hold its frame rate (I already do this in C++, but at least you can grep for and validate them!)." In C++, you're hunting for false virtuals right now. Your solution is to grep for and validate them. That's what I'm talking about here, but instead of grepping, using the language's reflection capabilities for an automated test. Check out the example I pasted at the end of this message. The bottom part of that is reusable, the top part is my test data. Run it through the compiler with the test code added: $ dmd test13 -unittest Warning: A.lol is an unauthorized virtual function Warning: B.amazing is an unauthorized virtual function And it greps for virtuals for you, and reports it at compile time. If it turns out you want one of them to be virtual, you add the name to the authorizedVirtuals list. How would you validate a virtual in C++? If you have a list of functions you're OK with, that's exactly what this is! If you look at the source of each virtual your grep finds, to ensure it absolutely needs to be virtual... well, you can do that here too. Between the warnings and the authorized virtuals list, you know all the virts here and can review them. The best way to silence a warning btw is to just write "final" on the method. Your code review process can keep the authorized virtual list to themselves, so most developers either add final or break the build. Either way, no virtuals slip in without review. Also from an old post: "Aside from that, I want a compile error if someone tries to randomly override stuff." Warning: C.goodVirtual is an unauthorized virtual function the code below checks on a per class level, so if you don't authorize the override, it gets a warning to. (You can change these to errors by making it static assert or something instead of pragma(msg).)
 Who maintains these tables?
Who decides who can write "virtual" in C++? Example follows: === module test13; class A { void goodVirtual() {} void lol() {} } class B { int amazing() { return 0; } } class C : A { override void goodVirtual() {} final void drox() {} } template isClass(alias T) if(!is(T)) { enum bool isClass = false; } template isClass(alias T) if(is(T)) { enum bool isClass = is(T == class); } unittest { enum string[][string] authorizedVirtuals = [ "A" : ["goodVirtual"], "B" : [], "C" : ["goodVirtual"], ]; import algore = std.algorithm; foreach(member; __traits(allMembers, test13)) { static if(isClass!(__traits(getMember, test13, member))) { foreach(possibleVirt; __traits(derivedMembers, __traits(getMember, test13, member))) { static if( __traits(isVirtualMethod, __traits(getMember, __traits(getMember, test13, member), possibleVirt)) && !algore.canFind(authorizedVirtuals[member], possibleVirt)) { pragma(msg, "Warning: " ~ member ~ "." ~ possibleVirt ~ " is an unauthorized virtual function"); } } } } } void main() {} ===
Mar 24 2012
next sibling parent "F i L" <witte2008 gmail.com> writes:
I think a better system would be to explicitly mark functions are 
virtual, and then use unittesting to catch virtual functions that 
don't need to be.
Mar 24 2012
prev sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 03/24/12 16:16, Adam D. Ruppe wrote:
 On Saturday, 24 March 2012 at 10:56:27 UTC, Manu wrote:
 My objection to virtual-by-default, but, acknowledging that can't be changed,
insistence on a virtual keyword
 is this.
I like the idea of a virtual keyword btw.
 Adding a system like you describe to validate virtuals is not a complexity I'm
interested in implementing
You already have! Let me quote one of your previous posts: "I sincerely fear finding myself false-virtual hunting on build night until 2am trying to get the game to hold its frame rate (I already do this in C++, but at least you can grep for and validate them!)." In C++, you're hunting for false virtuals right now. Your solution is to grep for and validate them. That's what I'm talking about here, but instead of grepping, using the language's reflection capabilities for an automated test. Check out the example I pasted at the end of this message.
The question is -- are there false positives? (Ie situations where the compiler managed to devirtualize methods which __traits reported earlier as being virtual) artur
Mar 24 2012
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 24 March 2012 at 16:27:41 UTC, Artur Skawina wrote:
 The question is -- are there false positives?
Yes, almost certainly. This only looks at the function definition; it is run well before the optimizer.
Mar 24 2012
prev sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 03/23/12 20:11, Adam D. Ruppe wrote:
 
 http://dlang.org/traits.html#getVirtualFunctions
 
 if the name isn't on a list of approved virtuals,
 static assert fail.
And you could probably do it in a clean and unintrusive way, by, for example, extending Object, or doing it on a per-module basis. If it actually worked... The obvious problem is that the 'virtual' check pretty much has to *prevent* devirtualization, or lie about it and always report the functions as virtual -- otherwise devirtualization has to happen before the checks, and this could change their results. Also, my gdc (which i haven't updated for a while) does not even devirtualize this simple case: ------------------------------------------------------- private class C { int bar() { return 42; } } pragma(attribute, externally_visible) int main() { C c = new C; //writeln(__traits(isVirtualFunction, c.bar)); return c.bar(); } ------------------------------------------------------- It needs at least the class or method marked as 'final' to do the right thing. Note that the isVirtualFunction check returns true even for the cases where bar() does get inlined. (*VirtualMethod* do not exist here) artur
Mar 24 2012
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 24 March 2012 at 14:43:21 UTC, Artur Skawina wrote:
 It needs at least the class or method marked as 'final' to do 
 the right thing.
Indeed, that's what I'm after. I'm making another post with an implementation in reply to manu in a minute.
Mar 24 2012
prev sibling parent Ary Manzana <ary esperanto.org.ar> writes:
On 3/24/12 3:03 AM, Manu wrote:
 On 23 March 2012 17:24, Ary Manzana <ary esperanto.org.ar
 <mailto:ary esperanto.org.ar>> wrote:

     On 3/18/12 9:23 AM, Manu wrote:

         The virtual model broken. I've complained about it lots, and people
         always say "stfu, use 'final:' at the top of your class".

         That sounds tolerable in theory, except there's no 'virtual'
         keyword to
         keep the virtual-ness of those 1-2 virtual functions I have...
         so it's
         no good (unless I rearrange my class, breaking the logical
         grouping of
         stuff in it).
         So I try that, and when I do, it complains: "Error: variable
         demu.memmap.MemMap.machine final cannot be applied to variable",
         allegedly a D1 remnant.
         So what do I do? Another workaround? Tag everything as final
         individually?

         My minimum recommendation: D needs an explicit 'virtual'
         keyword, and to
         fix that D1 bug, so putting final: at the top of your class
         works, and
         everything from there works as it should.


     Is virtual-ness your performance bottleneck?


 Frequently. It's often the most expensive 'trivial' operation many
 processors can be asked to do. Senior programmers (who have much better
 things to waste their time on considering their pay bracket) frequently
 have to spend late nights mitigating this even in C++ where virtual
 isn't default. In D, I'm genuinely concerned by this prospect. Now I
 can't just grep for virtual and fight them off, which is time consuming
 alone, I will need to take every single method, one by one, prove it is
 never overloaded anywhere (hard to do), before I can even begin the
 normal process of de-virtualising it like you do in C++.
 The problem is elevated by the fact that many programmers are taught in
 university that virtual functions are okay. They come to the company,
 write code how they were taught in university, and then we're left to
 fix it up on build night when we can't hold our frame rate. virtual
 functions and scattered/redundant memory access are usually the first
 thing you go hunting for. Fixing virtuals is annoying when the system
 was designed to exploit them, it often requires some extensive
 refactoring, much harder to fix than a bad memory access pattern, which
 might be as simple as rearranging a struct.
Interesting. I spend most of my work time programming in Ruby, where everything is virtual+ :-P It's good to know that virtual-ness can be a bottleneck.
Mar 24 2012