www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Stroustrup is disappointed with D :(

reply Tourist <gravatar gravatar.com> writes:
"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?
Sep 22 2015
next sibling parent reply Freddy <Hexagonalstar64 gmail.com> writes:
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.
Sep 22 2015
parent Ziad Hatahet via Digitalmars-d <digitalmars-d puremagic.com> writes:
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.
Sep 22 2015
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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 <iostream> 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
Sep 22 2015
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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...)
Sep 22 2015
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Sep 22 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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 reeeaaaally 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.
Sep 22 2015
parent reply Nemanja Boric <4burgos gmail.com> writes:
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
Sep 23 2015
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Sep 23 2015
parent Joakim <dlang joakim.fea.st> writes:
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/
Sep 23 2015
prev sibling next sibling parent John Carter <john.carter taitradio.com> writes:
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."
Sep 22 2015
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
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.
Sep 22 2015
parent reply Ola Fosheim Grostad <ola.fosheim.grostad+dlang gmail.com> writes:
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?
Sep 22 2015
parent reply deadalnix <deadalnix gmail.com> writes:
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.
Sep 22 2015
parent Ola Fosheim Grostad <ola.fosheim.grostad+dlang gmail.com> writes:
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.
Sep 22 2015
prev sibling parent Mengu <mengukagan gmail.com> writes:
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
Sep 23 2015
prev sibling next sibling parent Brad Roberts via Digitalmars-d <digitalmars-d puremagic.com> writes:
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.
Sep 22 2015
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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 <iostream> 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
Sep 22 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.
Sep 22 2015
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.)
Sep 23 2015
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Sep 23 2015
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.
Sep 23 2015
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Sep 23 2015
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.
Sep 23 2015
prev sibling parent reply ponce <contact gam3sfrommars.fr> writes:
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.
Sep 23 2015
next sibling parent ponce <contact gam3sfrommars.fr> writes:
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*
Sep 23 2015
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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...
Sep 23 2015
parent Kagamin <spam here.lot> writes:
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.
Sep 23 2015