digitalmars.D - virtual-by-default rant
- Manu (13/13) Mar 17 2012 The virtual model broken. I've complained about it lots, and people alwa...
- Bernard Helyer (3/8) Mar 17 2012 Agreed. Final by default is a proposition long gone, but that
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (10/26) Mar 17 2012 class Foo {
- Manu (14/39) Mar 17 2012 ys
- F i L (34/37) Mar 17 2012 I actually like the bracket+indentation as a section separator
- Manu (6/42) Mar 17 2012 Yeah, I'm not really into that. I group things conceptually.
- F i L (12/12) Mar 17 2012 I'm a bit confused. Reading through the virtual function's docs
- bearophile (4/12) Mar 17 2012 This is so much theoretical that I think this should be removed from the...
- F i L (5/20) Mar 17 2012 Dammit, I was afraid someone would say something like that. Well
- Marco Leise (7/32) Mar 23 2012 "the D compiler knows all of the class hierarchy when generating code"
- F i L (7/12) Mar 23 2012 Yes, further reading has led me to believe that Manu is right in
- Timon Gehr (2/4) Mar 23 2012 This assertion is unjustified.
- Mike Parker (2/14) Mar 18 2012 It says "can be optimized", not "are optimized". Big difference.
- David Nadlinger (5/14) Mar 18 2012 Is this even possible without LTO/WPO? Extending a class defined
- Manu (18/27) Mar 18 2012 No, I definitely want a class. ref type, gc mem, etc.
- Andrei Alexandrescu (8/20) Mar 18 2012 Then perhaps it's a good idea to move accessors outside and take
- Martin Nowak (5/12) Mar 18 2012 This is not even possible with LTO because new classes
- deadalnix (3/15) Mar 18 2012 That is limited to export classes. In this case, final/virtual should be...
- David Nadlinger (5/12) Mar 18 2012 Sure, you can't just devirtualize everything you come across even
- Jacob Carlborg (5/15) Mar 18 2012 I agree that can be misleading. But I think the D docs should be about
- Manu (12/23) Mar 18 2012 It's not dependable. Virtually everything meets those criteria and will ...
- F i L (23/33) Mar 18 2012 How does D not know about class hierarchy when generating code?
- Manu (31/56) Mar 18 2012 I mean it can't possibly know the complete 'final' class hierarchy, ie, ...
- F i L (42/75) Mar 18 2012 I still don't understand why you think this. The compiler must
- David Nadlinger (11/14) Mar 18 2012 Which is wrong as long as you don't do link-time optimization,
- David Nadlinger (7/13) Mar 18 2012 Also note that this applies to the general case where you get
- David Nadlinger (8/20) Mar 18 2012 And thinking even more about it, devirtualization could also be
- F i L (11/26) Mar 18 2012 Are GDC and LDC limited by DMD in this regard? I know LDC has a
- David Nadlinger (6/7) Mar 18 2012 Unfortunately it doesn't (-O4/-O5 are defunct), but working on
- James Miller (16/23) Mar 18 2012 I think that simply adding a `virtual` keyword that explicitly makes
- deadalnix (2/27) Mar 19 2012 +1
- Andrei Alexandrescu (3/8) Mar 18 2012 It knows about ancestors of each type but not about descendants.
- deadalnix (3/9) Mar 18 2012 The compiler can. But ATM, it doesn't.
- Andrei Alexandrescu (3/6) Mar 17 2012 Then probably struct is what you're looking for.
- Walter Bright (7/8) Mar 17 2012 You can use:
- F i L (33/54) Mar 17 2012 what's so bad with:
- F i L (2/4) Mar 17 2012 **you're positive you *DO* want final is...
- deadalnix (6/19) Mar 18 2012 This problem isn't virtual by default at all. It would just flip the
- Andrei Alexandrescu (3/6) Mar 18 2012 Yah, ~const etc. have been suggested a couple of times. Helps casts too.
- deadalnix (10/16) Mar 18 2012 This seems definitively an issue to me. ~const/~final, or
- Dmitry Olshansky (18/31) Mar 18 2012 Following this thread and observing that you don't trust optimizer and
- Artur Skawina (16/36) Mar 18 2012 I was going to suggest the very same thing - but there are (at least) tw...
- Dmitry Olshansky (17/51) Mar 19 2012 GDC not passing pseudo-refs in registers is cleanly a non-issue, in a
- Artur Skawina (24/76) Mar 19 2012 Something that should work in theory, but does not behave as expected,
- Dmitry Olshansky (43/119) Mar 19 2012 Sure sounds like fun challenge, that's my start before being killed by
- FeepingCreature (14/21) Mar 18 2012 See subject.
- Timon Gehr (2/3) Mar 18 2012 final class means that the class cannot be inherited from.
- deadalnix (2/8) Mar 18 2012 What is the point to inherit is no virtual method exists ?
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (4/13) Mar 18 2012 ,
- deadalnix (2/14) Mar 19 2012 That is a reason, but I never saw that except for dirty hacks :D
- Gor Gyolchanyan (12/33) Mar 19 2012 Having a final class is conceptually different from having a class
- deadalnix (5/12) Mar 19 2012 It looks like a bad usage of inheritance. If you want to use these
- Gor Gyolchanyan (7/20) Mar 19 2012 s,
- deadalnix (2/3) Mar 19 2012 This is exactly the type of behavior that a language should discourage.
- F i L (29/33) Mar 19 2012 Am I missing something about what you're saying? Having a final
- deadalnix (5/39) Mar 19 2012 That is totally broken design. The fact that mothership inherit from
- F i L (15/20) Mar 19 2012 Other objects can contain references to MotherShip types
- deadalnix (10/28) Mar 19 2012 As all functionnality of a Ship are final, you basically ends up with a
- Andrei Alexandrescu (6/36) Mar 19 2012 I agree 100%. Inheriting to extend is borderline fallacious.
- F i L (29/37) Mar 19 2012 Yes, because Ship is designed to be the common denominator among
- Andrej Mitrovic (2/11) Mar 18 2012 Final classes might be useful for e.g. leaf classes.
- deadalnix (2/15) Mar 19 2012 This doesn't address the question.
- Walter Bright (3/4) Mar 19 2012 When proposing a new feature, the question is not "why not?". The questi...
- deadalnix (3/7) Mar 19 2012 In this case, this isn't really a feature, but more syntaxic sugar.
- Ary Manzana (2/15) Mar 23 2012 Is virtual-ness your performance bottleneck?
- Manu (19/37) Mar 23 2012 Frequently. It's often the most expensive 'trivial' operation many
- Adam D. Ruppe (9/9) Mar 23 2012 Something that *might* help is to do unit tests. Yeah,
- Manu (83/89) Mar 24 2012 They're not necessarily 'stray' virtuals, they're inappropriate ones
- Adam D. Ruppe (92/98) Mar 24 2012 You already have! Let me quote one of your previous posts:
- F i L (3/3) Mar 24 2012 I think a better system would be to explicitly mark functions are
- Artur Skawina (5/31) Mar 24 2012 The question is -- are there false positives?
- Adam D. Ruppe (3/4) Mar 24 2012 Yes, almost certainly. This only looks at the function
- Artur Skawina (26/31) Mar 24 2012 And you could probably do it in a clean and unintrusive way,
- Adam D. Ruppe (4/6) Mar 24 2012 Indeed, that's what I'm after.
- Ary Manzana (5/46) Mar 24 2012 Interesting.
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
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
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
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=ysosay "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=fgood (unless I rearrange my class, breaking the logical grouping of stuf=lyin it). So I try that, and when I do, it complains: "Error: variable demu.memmap.MemMap.machine final cannot be applied to variable", alleged=y?a D1 remnant. So what do I do? Another workaround? Tag everything as final individuall=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 toclass Foo { final { // Final functions here. } // Virtual functions here. } Good?onfix 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=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
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
On 18 March 2012 04:14, F i L <witte2008 gmail.com> wrote: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. 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.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).
Mar 17 2012
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
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
On Sunday, 18 March 2012 at 03:27:40 UTC, bearophile wrote:F i L: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.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
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:"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 :) -- MarcoF i L: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.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 23 2012
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
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
On Friday, 23 March 2012 at 13:58:00 UTC, Timon Gehr wrote:On 03/23/2012 02:47 PM, F i L wrote: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.... and most programmers are already used to fixed by default anyways.This assertion is unjustified.
Mar 23 2012
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
On 3/18/2012 12:27 PM, bearophile wrote:F i L:It says "can be optimized", not "are optimized". Big difference.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 18 2012
On Sunday, 18 March 2012 at 03:27:40 UTC, bearophile wrote:F i L: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"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 :-)
Mar 18 2012
On 18 March 2012 06:42, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 3/17/12 9:24 PM, Manu wrote: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: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.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=A6It'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
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
=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 DavidThis is not even possible with LTO because new classes could be loaded at runtime. Could somebody please fix this.
Mar 18 2012
Le 18/03/2012 17:02, Martin Nowak a écrit :That is limited to export classes. In this case, final/virtual should be managed very precisely anyway if performance matter.This is not even possible with LTO because new classes could be loaded at runtime. Could somebody please fix this.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
On Sunday, 18 March 2012 at 16:02:04 UTC, Martin Nowak wrote: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. DavidIs 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… DavidThis is not even possible with LTO because new classes could be loaded at runtime.
Mar 18 2012
On 2012-03-18 04:27, bearophile wrote:F i L: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 CarlborgI'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 :-)
Mar 18 2012
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
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-virtualAside 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
On 18 March 2012 13:59, F i L <witte2008 gmail.com> wrote: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.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.Aside from that, I want a compile error if someone tries to randomlyAre 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.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 usedI 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. virtualexplicitly. 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.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
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
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
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
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: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. DavidWhich 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.
Mar 18 2012
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
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
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:TO[=E2=80=A6] I know LDC has a =C2=A0LTO flag.Unfortunately it doesn't (-O4/-O5 are defunct), but working on seamless L=integration (and better optimization pass scheduling in general) would be low-hanging fruit for anybody wanting to join LDC development. DavidI 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
Le 18/03/2012 22:36, James Miller a écrit :On 19 March 2012 06:41, David Nadlinger<see klickverbot.at> wrote:+1On Sunday, 18 March 2012 at 17:24:15 UTC, F i L wrote: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[…] 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 19 2012
On 3/18/12 6:59 AM, F i L wrote:Manu wrote:It knows about ancestors of each type but not about descendants. AndreiD 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.
Mar 18 2012
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
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
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
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
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
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
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
Le 18/03/2012 16:26, Andrei Alexandrescu a écrit :On 3/18/12 8:39 AM, deadalnix wrote: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).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
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
On 03/18/12 15:37, Dmitry Olshansky wrote:On 18.03.2012 5:23, Manu wrote: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. :) arturThe 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 :)
Mar 18 2012
On 19.03.2012 2:17, Artur Skawina wrote:On 03/18/12 15:37, Dmitry Olshansky wrote: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 emplaceOn 18.03.2012 5:23, Manu wrote: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)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 :)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
On 03/19/12 08:30, Dmitry Olshansky wrote:On 19.03.2012 2:17, Artur Skawina wrote: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]On 03/18/12 15:37, Dmitry Olshansky wrote: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.On 18.03.2012 5:23, Manu wrote: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)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 :)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 emplacestruct 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...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.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.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. arturSo - 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.
Mar 19 2012
On 19.03.2012 14:45, Artur Skawina wrote:On 03/19/12 08:30, Dmitry Olshansky wrote: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); }On 19.03.2012 2:17, Artur Skawina wrote: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]On 03/18/12 15:37, Dmitry Olshansky wrote: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.On 18.03.2012 5:23, Manu wrote: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)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 :)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 emplacestruct 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...-- Dmitry OlshanskyThis 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.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.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. arturSo - 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.
Mar 19 2012
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
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
Le 18/03/2012 17:49, Timon Gehr a écrit :On 03/18/2012 05:25 PM, FeepingCreature wrote:What is the point to inherit is no virtual method exists ?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
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=Access to protected members.What is the point to inherit is no virtual method exists ?"final class" can be deprecated (it never worked well anyway).final class means that the class cannot be inherited from.
Mar 18 2012
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:That is a reason, but I never saw that except for dirty hacks :DLe 18/03/2012 17:49, Timon Gehr a écrit :Access to protected members.On 03/18/2012 05:25 PM, FeepingCreature wrote:What is the point to inherit is no virtual method exists ?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 19 2012
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 :e:On Sun, 18 Mar 2012 18:07:10 +0100, deadalnix <deadalnix gmail.com> wrot=--=20 Bye, Gor Gyolchanyan.That is a reason, but I never saw that except for dirty hacks :DLe 18/03/2012 17:49, Timon Gehr a =C3=A9crit :Access to protected members.On 03/18/2012 05:25 PM, FeepingCreature wrote:What is the point to inherit is no virtual method exists ?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 19 2012
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
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 :s,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=why not use composition ? It make no sense to use such an inherited class in a polymorphic context,=sowhy use inheritance at all ?--=20 Bye, Gor Gyolchanyan.
Mar 19 2012
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
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
Le 19/03/2012 18:41, 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.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
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
Le 19/03/2012 21:39, F i L a écrit :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. Separation of concerns tells us that this should be 2 different objects.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.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
On 3/19/12 5:11 PM, deadalnix wrote:Le 19/03/2012 21:39, F i L a écrit :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. Andreideadalnix 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. Separation of concerns tells us that this should be 2 different objects.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.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
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
On 3/18/12, deadalnix <deadalnix gmail.com> wrote:Le 18/03/2012 17:49, Timon Gehr a =E9crit :Final classes might be useful for e.g. leaf classes.On 03/18/2012 05:25 PM, FeepingCreature wrote:What is the point to inherit is no virtual method exists ?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
Le 18/03/2012 19:57, Andrej Mitrovic a écrit :On 3/18/12, deadalnix<deadalnix gmail.com> wrote:This doesn't address the question.Le 18/03/2012 17:49, Timon Gehr a écrit :Final classes might be useful for e.g. leaf classes.On 03/18/2012 05:25 PM, FeepingCreature wrote:What is the point to inherit is no virtual method exists ?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 19 2012
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
Le 19/03/2012 21:43, Walter Bright a écrit :On 3/18/2012 9:25 AM, FeepingCreature wrote:In this case, this isn't really a feature, but more syntaxic sugar. Anyway, I don't think this is that important.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
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
On 23 March 2012 17:24, Ary Manzana <ary esperanto.org.ar> wrote:On 3/18/12 9:23 AM, Manu wrote: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.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
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
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
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 implementingYou 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
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
On 03/24/12 16:16, Adam D. Ruppe wrote:On Saturday, 24 March 2012 at 10:56:27 UTC, Manu wrote:The question is -- are there false positives? (Ie situations where the compiler managed to devirtualize methods which __traits reported earlier as being virtual) arturMy 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 implementingYou 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.
Mar 24 2012
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
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
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
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