Stroustrup is disappointed with D :(
"D disappointed me so much when it went the Java way". https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules It's something about virtual calls, but I didn't understand what he means. What does he mean?
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 18:58:31 UTC, Tourist wrote: "D disappointed me so much when it went the Java way". https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules It's something about virtual calls, but I didn't understand what he means. What does he mean? I doubt he is talking about the whole language. From my skimming it seems he is talking about how D copies java's classes.
Re: Stroustrup is disappointed with D :(
Perhaps he meant how in Java, methods are virtual by default, unlike C++ where they are final by default. -- Ziad On Tue, Sep 22, 2015 at 12:15 PM, Freddy via Digitalmars-d < digitalmars-d@puremagic.com> wrote: > On Tuesday, 22 September 2015 at 18:58:31 UTC, Tourist wrote: > >> "D disappointed me so much when it went the Java way". >> >> https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules >> >> It's something about virtual calls, but I didn't understand what he >> means. What does he mean? >> > > I doubt he is talking about the whole language. From my skimming it seems > he is talking about how D copies java's classes. >
Re: Stroustrup is disappointed with D :(
On 09/22/2015 11:58 AM, Tourist wrote: "D disappointed me so much when it went the Java way". https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules It's something about virtual calls, but I didn't understand what he means. What does he mean? It is about virtual calls in ctors and dtors. Here is the problem: import std.stdio; class B { this() { foo(); } void foo() { writeln("base"); } } class D : B { this() { writeln("derived is only now complete"); } override void foo() { writeln("derived"); } } void main() { auto b = new D; } Although we are in the middle of consructing a D, the call foo() inside B's ctor is dispatched to D's virtual foo() even though the D part of the object has not been constructed yet. This is in contrast to C++, where the object goes through multiple personalities during its construction: First B, then D, etc. The program above prints derived derived is only now complete As can be seen, D.foo is called before D is ready for use. Here is the equivalent C++ program: #include using std::cout; class B { public: B() { foo(); } virtual ~B() {} virtual void foo() { cout << "base\n"; } }; class D : public B { public: D() { cout << "derived is only now complete\n"; } virtual void foo() { cout << "derived\n"; } }; int main() { D d; } The output of the C++ program: base derived is only now complete C++'s approach is better from the point of view of corretness. However, it is slower because the object's vtbl pointer must be stamped several times during construction. (I am not aware of available compiler optimizations there.) Ali
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 19:38:35 UTC, Ali Çehreli wrote: C++'s approach is better from the point of view of corretness. However, it is slower because the object's vtbl pointer must be stamped several times during construction. (I am not aware of available compiler optimizations there.) No dispatch needed if calls it's own functions, so no vtable needed for the constructor. But neither approach is good for correctness. (OP: The guidelines have 30 committers or something, I somehow doubt Stroustrup wrote all that...)
Re: Stroustrup is disappointed with D :(
On 09/22/2015 12:52 PM, Ola Fosheim Grøstad wrote: On Tuesday, 22 September 2015 at 19:38:35 UTC, Ali Çehreli wrote: C++'s approach is better from the point of view of corretness. However, it is slower because the object's vtbl pointer must be stamped several times during construction. (I am not aware of available compiler optimizations there.) No dispatch needed if calls it's own functions, so no vtable needed for the constructor. But neither approach is good for correctness. Agreed. Only if all potential virtual uses of the object inside the ctor can be proven to be to the base's type, then yes, setting the vtbl pointer can be elided. However, it is not possible in general e.g. if the object is passed to a function by reference, that function can call any virtual method on it so the vtbl pointer must have been set upon entry to the constructor, at every level of the hierarchy. (OP: The guidelines have 30 committers or something, I somehow doubt Stroustrup wrote all that...) I tried to determine the actual author. It was not easy and I still don't know. :) Ali
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 19:52:48 UTC, Ola Fosheim Grøstad wrote: But neither approach is good for correctness. The approach that would be "Good for Correctness" is "Hey! You're doing to much work in a constructor, that's a code smell anyway. And invoking virtual methods in a constructor is bound to be flaky no matter what the language designer chooses... so don't do that."
Re: Stroustrup is disappointed with D :(
On 9/22/15 12:38 PM, Ali Çehreli via Digitalmars-d wrote: On 09/22/2015 11:58 AM, Tourist wrote: "D disappointed me so much when it went the Java way". https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules It's something about virtual calls, but I didn't understand what he means. What does he mean? It is about virtual calls in ctors and dtors. Here is the problem: snip for length Although we are in the middle of consructing a D, the call foo() inside B's ctor is dispatched to D's virtual foo() even though the D part of the object has not been constructed yet. This is in contrast to C++, where the object goes through multiple personalities during its construction: First B, then D, etc. The program above prints derived derived is only now complete As can be seen, D.foo is called before D is ready for use. The output of the C++ program: base derived is only now complete C++'s approach is better from the point of view of correctness. However, it is slower because the object's vtbl pointer must be stamped several times during construction. (I am not aware of available compiler optimizations there.) Ali Keep in mind there's another core difference between c++ and d here and that is: when member variables are set their initial values. In D, it's before any ctors are called for the full object. In c++ they're split into the parts associated with each step in the hierarchy and set as effectively line 0 of the ctor (even though syntactically outside the body of the ctor). These differences are subtle, but can be critical for code that's doing what many would say is too much in the ctor.
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 19:52:48 UTC, Ola Fosheim Grøstad wrote: On Tuesday, 22 September 2015 at 19:38:35 UTC, Ali Çehreli wrote: C++'s approach is better from the point of view of corretness. However, it is slower because the object's vtbl pointer must be stamped several times during construction. (I am not aware of available compiler optimizations there.) No dispatch needed if calls it's own functions, so no vtable needed for the constructor. But neither approach is good for correctness. You can call super, so you need the virtual dispatch.
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 20:42:44 UTC, Ali Çehreli wrote: However, it is not possible in general e.g. if the object is passed to a function by reference, that function can call any virtual method on it so the vtbl pointer must have been set upon entry to the constructor, at every level of the hierarchy. Ugh, yes, but using "this" externally on a partially constructed object is reeelly ugly... If we want to get closer to something not horribly flawed we would have to do something like: constructor() { super(); this.vtable = super::vtable dostuff( this cast as super) fully_init_myself() this.vtable = myself::vtable; domorestuff(this) } What a mess... BTW, the language Beta had type variables that could be virtual. It was used for things like the element types for containers. In that case the super class could instantiate a specialized type provided by subclasses as a virtual type. Kind of neat, but I never used it much... I tried to determine the actual author. It was not easy and I still don't know. :) Me neither. I'm getting the impression that I am looking at a wall of guidelines-graffiti.
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 21:25:06 UTC, deadalnix wrote: You can call super, so you need the virtual dispatch. I understand Ali's argument about setting local vtable before calling external functions in C++, but other than that it should be sufficient to set vtable in the new() or equivalent topmost call?
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 22:09:59 UTC, Ola Fosheim Grostad wrote: On Tuesday, 22 September 2015 at 21:25:06 UTC, deadalnix wrote: You can call super, so you need the virtual dispatch. I understand Ali's argument about setting local vtable before calling external functions in C++, but other than that it should be sufficient to set vtable in the new() or equivalent topmost call? It is part of the .init, so the compiler would set it BEFORE calling the constructor. constructor can then call each other and rely on the fact that the vtable is initialized. However, constructor need to do the virtual dispatch as one can call super.
Re: Stroustrup is disappointed with D :(
On 9/22/15 3:38 PM, Ali Çehreli wrote: On 09/22/2015 11:58 AM, Tourist wrote: "D disappointed me so much when it went the Java way". https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules It's something about virtual calls, but I didn't understand what he means. What does he mean? It is about virtual calls in ctors and dtors. Here is the problem: import std.stdio; class B { this() { foo(); } void foo() { writeln("base"); } } class D : B { this() { writeln("derived is only now complete"); } override void foo() { writeln("derived"); } } void main() { auto b = new D; } Although we are in the middle of consructing a D, the call foo() inside B's ctor is dispatched to D's virtual foo() even though the D part of the object has not been constructed yet. This is in contrast to C++, where the object goes through multiple personalities during its construction: First B, then D, etc. The program above prints derived derived is only now complete As can be seen, D.foo is called before D is ready for use. Here is the equivalent C++ program: #include using std::cout; class B { public: B() { foo(); } virtual ~B() {} virtual void foo() { cout << "base\n"; } }; class D : public B { public: D() { cout << "derived is only now complete\n"; } virtual void foo() { cout << "derived\n"; } }; int main() { D d; } The output of the C++ program: base derived is only now complete C++'s approach is better from the point of view of corretness. However, it is slower because the object's vtbl pointer must be stamped several times during construction. (I am not aware of available compiler optimizations there.) Ali Yeah, but you can't do this in C++ though: class D : B { this() { writeln("derived is only now complete"); super(); } } I find the ability to control the construction order far more important than virtual calls for base constructors. -Steve
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 22:14:56 UTC, deadalnix wrote: It is part of the .init, so the compiler would set it BEFORE calling the constructor. constructor can then call each other and rely on the fact that the vtable is initialized. We were discussing the cost if doing it like c++, where virtual calls during construction acts as if the object isn't subclassed. That way all object local member function calls can be devirtualized and inlined. In D we have to use the init method you mention.
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 23:21:20 UTC, Steven Schveighoffer wrote: Yeah, but you can't do this in C++ though: class D : B { this() { writeln("derived is only now complete"); super(); } } I find the ability to control the construction order far more important than virtual calls for base constructors. You could do it in old C++ compilers, and some have a permissive switch that allows you to do it, but you should not do it. It leads to incorrect initialization and breaks encapsulation (unsound typing). Forcing construction order is a Good Thing. In Beta, the successor to Simula, all execution follows this pattern: this(){ begin_prepare_stuff(); subclass.this(); end_prepare_stuff(); } This way the superclass defines what you can override which is better for correctness.
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 19:52:48 UTC, Ola Fosheim Grøstad wrote: On Tuesday, 22 September 2015 at 19:38:35 UTC, Ali Çehreli wrote: C++'s approach is better from the point of view of corretness. However, it is slower because the object's vtbl pointer must be stamped several times during construction. (I am not aware of available compiler optimizations there.) No dispatch needed if calls it's own functions, so no vtable needed for the constructor. But neither approach is good for correctness. (OP: The guidelines have 30 committers or something, I somehow doubt Stroustrup wrote all that...) we can always do a git blame :-D
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 18:58:31 UTC, Tourist wrote: "D disappointed me so much when it went the Java way". https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules It's something about virtual calls, but I didn't understand what he means. What does he mean? I fail to see how the multi-part C++ object initialization is any better than the one of D. It just is very simple in D: first assign .init, then call the destructor, virtual calls allowed (of course!). The weird rules of virtual functions in ctor/dtor in C++ just feel like one more special case. It doesn't even seem more efficient, quite the contrary. But it makes good interview questions I guess.
Re: Stroustrup is disappointed with D :(
On Wednesday, 23 September 2015 at 08:27:43 UTC, ponce wrote: On Tuesday, 22 September 2015 at 18:58:31 UTC, Tourist wrote: "D disappointed me so much when it went the Java way". https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#to-do-unclassified-proto-rules It just is very simple in D: first assign .init, then call the destructor, virtual calls allowed (of course!). the call the *constructor*
Re: Stroustrup is disappointed with D :(
On Wednesday, 23 September 2015 at 08:27:43 UTC, ponce wrote: I fail to see how the multi-part C++ object initialization is any better than the one of D. It just is very simple in D: first assign .init, then call the destructor, virtual calls allowed (of course!). The combination of being able to override most member functions and them being called in super constructors makes for a brittle inheritance mechanism. If virtual was explicit and the superclass could make some parts of the virtual function non-overridable then it would be less problematic. The weird rules of virtual functions in ctor/dtor in C++ just feel like one more special case. It doesn't even seem more efficient, quite the contrary. Devirtualized inlining is trivially more efficient than virtual calls...
Re: Stroustrup is disappointed with D :(
On Wednesday, 23 September 2015 at 06:06:42 UTC, Ola Fosheim Grøstad wrote: In Beta, the successor to Simula, all execution follows this pattern: this(){ begin_prepare_stuff(); subclass.this(); end_prepare_stuff(); } Actually, this was how Simula did it too. If you didn't provide the slot for the subclass constructor ("inner") it would inject it at the end. Unfortunately C++ didn't add the slot, and just mimics default construction in Simula... But Beta is actually much more powerful than I suggested, more like this (Cish syntaxification): B { int x,y,a,b,sum; virtual init1 { int aa; enter(aa,b); b = b*2; inner; a = aa; } enter (a,b,x,y); enter (init1,(x,y)); sum = 0; inner; sum = sum + x + y + a + b; } D : B { int z; init1 : super.init1 { z = aa+b; inner; } sum = sum + z; inner; } "enter" is the parameter list, the compiler will pick the one that matches the tuple and execute it before it executes the body. "inner" is where you inject code when specializing. (Beta is a very minimal stackless language, it does not differentiate between construction and function call. Which bring some other problems.)
Re: Stroustrup is disappointed with D :(
On Wednesday, 23 September 2015 at 09:09:53 UTC, Ola Fosheim Grøstad wrote: The weird rules of virtual functions in ctor/dtor in C++ just feel like one more special case. It doesn't even seem more efficient, quite the contrary. Devirtualized inlining is trivially more efficient than virtual calls... Called methods call other virtual methods and you can't inline the entire call tree.
Re: Stroustrup is disappointed with D :(
On Tuesday, 22 September 2015 at 21:28:27 UTC, Ola Fosheim Grøstad wrote: I tried to determine the actual author. It was not easy and I still don't know. :) Me neither. I'm getting the impression that I am looking at a wall of guidelines-graffiti. It was introduced in this commit: https://github.com/isocpp/CppCoreGuidelines/commit/947cf3affcdfc392a316a32f73cdea7383ae55bd
Re: Stroustrup is disappointed with D :(
On 9/23/15 2:06 AM, Ola Fosheim Grøstad wrote: On Tuesday, 22 September 2015 at 23:21:20 UTC, Steven Schveighoffer wrote: Yeah, but you can't do this in C++ though: class D : B { this() { writeln("derived is only now complete"); super(); } } I find the ability to control the construction order far more important than virtual calls for base constructors. You could do it in old C++ compilers, and some have a permissive switch that allows you to do it, but you should not do it. It leads to incorrect initialization and breaks encapsulation (unsound typing). Forcing construction order is a Good Thing. You can do it in C++ via initializers too, just not as useful. D still enforces sound construction. -Steve
Re: Stroustrup is disappointed with D :(
On Wednesday, 23 September 2015 at 13:14:54 UTC, Steven Schveighoffer wrote: You can do it in C++ via initializers too, just not as useful. D still enforces sound construction. The key quality for a good OO paradigm is that you can independently modify super-classes and sub-classes in an encapsulated way without knowing the concrete implementation of the other. Like most languages C++ and D does not ensure sound object construction, but C++ is a bit better than D. When you allow super() to be called in arbitrary locations then modifications of ancestor classes are much more likely to cause issues for subclasses. So that approach does not scale.
Re: Stroustrup is disappointed with D :(
On 9/23/15 9:48 AM, Ola Fosheim Grøstad wrote: On Wednesday, 23 September 2015 at 13:14:54 UTC, Steven Schveighoffer wrote: You can do it in C++ via initializers too, just not as useful. D still enforces sound construction. The key quality for a good OO paradigm is that you can independently modify super-classes and sub-classes in an encapsulated way without knowing the concrete implementation of the other. Any usage of virtual functions by the base class ctor is bound to be something that is known by the derived class author. In fact, it's likely to be a feature. If the base ctor doesn't call any virtual functions, when it's constructed doesn't really matter. Like most languages C++ and D does not ensure sound object construction, but C++ is a bit better than D. When you allow super() to be called in arbitrary locations then modifications of ancestor classes are much more likely to cause issues for subclasses. So that approach does not scale. I don't see how you come to that conclusion. The derived class must control construction of the base class. All D does is allow you to write code that can build the parameters for the base class to use for construction, instead of forcing you to write it in one expression like C++. It requires slightly more careful thought (and some rules from the compiler), but it scales just fine. We don't have to be purists for a paradigm to reap the benefits. -Steve
Re: Stroustrup is disappointed with D :(
On Wednesday, 23 September 2015 at 14:01:11 UTC, Steven Schveighoffer wrote: If the base ctor doesn't call any virtual functions, when it's constructed doesn't really matter. 1. In D members are virtual by default, so the virtuality can be a mistake. 2. In D/C++ you can do full override of virtual members, that means that enforcing semantics are delegated to documentation and code review. This is a flaw inherited from Simula. I don't see how you come to that conclusion. The derived class must control construction of the base class. No, this is not how it is done in Simula/Beta. The construction is controlled from the base classes and follows the inheritance chain. Doing construction in the opposite direction is a C++ flaw, probably related to C. But neither C/C++ are good role models when it comes to typing. It requires slightly more careful thought (and some rules from the compiler), but it scales just fine. I don't think it scales "just fine". OO in C++ does not scale fine either. If you easily can break the super-class semantics in a sub-class then it does not scale. Alias this suffers from similar issues.
Re: Stroustrup is disappointed with D :(
On 09/23/2015 05:16 AM, Nemanja Boric wrote: On Tuesday, 22 September 2015 at 21:28:27 UTC, Ola Fosheim Grøstad wrote: I tried to determine the actual author. It was not easy and I still don't know. :) Me neither. I'm getting the impression that I am looking at a wall of guidelines-graffiti. It was introduced in this commit: https://github.com/isocpp/CppCoreGuidelines/commit/947cf3affcdfc392a316a32f73cdea7383ae55bd I had hit that one but the quoted phrase does not appear on that page. Is that diff abridged? Oh I see: It says "11,900 additions, 0 deletions not shown". Ali
Re: Stroustrup is disappointed with D :(
On Wednesday, 23 September 2015 at 17:49:28 UTC, Ali Çehreli wrote: On 09/23/2015 05:16 AM, Nemanja Boric wrote: On Tuesday, 22 September 2015 at 21:28:27 UTC, Ola Fosheim Grøstad wrote: I tried to determine the actual author. It was not easy and I still don't know. :) Me neither. I'm getting the impression that I am looking at a wall of guidelines-graffiti. It was introduced in this commit: https://github.com/isocpp/CppCoreGuidelines/commit/947cf3affcdfc392a316a32f73cdea7383ae55bd I had hit that one but the quoted phrase does not appear on that page. Is that diff abridged? Oh I see: It says "11,900 additions, 0 deletions not shown". That is the original commit, which was written and edited by a group, so git is no use. The reddit discussion of the blog post announcing the guidelines has a fair amount of positive comments about D: https://www.reddit.com/r/programming/comments/3ltwwf/bjarne_stroustrup_announces_c_core_guidelines/