D - forgetting to call super doesn't call any super constructors.
- Mike Wynn (39/39) Oct 01 2002 I quote ....
- Russell Lewis (2/18) Oct 01 2002 IIRC, this is how things are supposed to work. It may be a compiler bug...
- Walter (11/29) Oct 01 2002 features so
- Mike Wynn (28/31) Oct 01 2002 this make life a pain for people writing library classes that are intend...
- Walter (22/55) Oct 01 2002 The static initializer must be evaluated at compile time, so it can't be
- Evan McClanahan (8/30) Oct 02 2002 I think that keeping the focus on unit testing and design-by-contract is...
- Mike Wynn (87/93) Oct 02 2002 manner.
- Walter (22/44) Oct 04 2002 I don't agree that the way C++ constructors works does work. I've had so
- Mike Wynn (49/64) Oct 02 2002 yes that will catch the error at runtime, but not tell the user of the
- Mac Reiter (78/95) Oct 02 2002 First, let me state that my personal feeling is that a derived class's t...
-
Roberto Mariottini
(20/42)
Oct 04 2002
"Mac Reiter"
ha scritto nel messaggio - Walter (19/23) Oct 04 2002 super(). I
- Walter (18/18) Oct 04 2002 I forgot to mention that Java requires any this() or super() call to be ...
- Mike Wynn (6/15) Oct 04 2002 yes you can ! by walking through the syntax tree and checking that there...
- Sandor Hojtsy (11/34) Oct 07 2002 be
- Walter (26/91) Oct 04 2002 of
- Evan McClanahan (11/15) Oct 04 2002 Would it be possible (or even useful) to have the compiler throw a
- Walter (4/13) Oct 04 2002 That's an important issue. The problem is, I think it is impossible for ...
- Mike Wynn (84/94) Oct 04 2002 but what if later on the item CAN be null, just not when initialised (ho...
- Sandor Hojtsy (15/45) Oct 02 2002 in
- Walter (3/4) Oct 04 2002 See my reply to Mac Reiter.
- Walter (2/2) Oct 04 2002 Ok, you've all convinced me. I'll change the semantics so a super() is
- Burton Radons (4/6) Oct 05 2002 I won't put implicit code generation of something so important in my
- Joe Battelle (2/8) Oct 05 2002 I agree.
- Mac Reiter (29/35) Oct 07 2002 What? We've got a garbage collector, and we now have RAII
- Mark Evans (6/8) Oct 07 2002 I agree. If it's called D, then it should be D.
- Joe Battelle (6/8) Oct 07 2002 While I agree with your larger sentiment about not fragmenting ports, I ...
I quote .... D aims to reduce software development costs by at least 10% by adding in proven productivity enhancing features and by adjusting language features so that common, time-consuming bugs are eliminated from the start. but surely forceing the user to remember to explicitly call a super(...); somewhere in a this(...) that does not call this(.other.params.); is asking for trouble. again my Java and C++ background, where if you don't put in a call to super, then super() is assumed I feel that D should either call super() at the begining of any constructor that does not call super(...) or another this(...) or warn you that you've forgotten something. I have an example class Base { int i; public: this() { i = 10;} void func() { printf( "i:%i\n", i ); } } class Derived : Base { int j; this() { j = 10; } this( int j0 ) { j=j0; } void func() { printf( "i:%i, j:%i\n", i, j ); } } void main( char[][] args ) { Derived d; d = new Derived(); d.func(); d = new Derived(44); d.func(); } which or course outputs i:0, j:10 i:0, j:44 but had 'i' been an object then it would have been null etc etc. (even more frustrated by the effects of simple typo's and omissions) Mike.
Oct 01 2002
Mike Wynn wrote:I quote .... D aims to reduce software development costs by at least 10% by adding in proven productivity enhancing features and by adjusting language features so that common, time-consuming bugs are eliminated from the start. but surely forceing the user to remember to explicitly call a super(...); somewhere in a this(...) that does not call this(.other.params.); is asking for trouble. again my Java and C++ background, where if you don't put in a call to super, then super() is assumed I feel that D should either call super() at the begining of any constructor that does not call super(...) or another this(...) or warn you that you've forgotten something.IIRC, this is how things are supposed to work. It may be a compiler bug.
Oct 01 2002
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:3D9A286E.9070900 deming-os.org...Mike Wynn wrote:features soI quote .... D aims to reduce software development costs by at least 10% by adding in proven productivity enhancing features and by adjusting languagesuper(...);that common, time-consuming bugs are eliminated from the start. but surely forceing the user to remember to explicitly call aaskingsomewhere in a this(...) that does not call this(.other.params.); issuper,for trouble. again my Java and C++ background, where if you don't put in a call toconstructorthen super() is assumed I feel that D should either call super() at the begining of anyyou'vethat does not call super(...) or another this(...) or warn you thatNo, super must be called explicitly. If super is not called anywhere, then you get the static initializer, which is what the static default values of each member are.forgotten something.IIRC, this is how things are supposed to work. It may be a compiler bug.
Oct 01 2002
No, super must be called explicitly. If super is not called anywhere, then you get the static initializer, which is what the static default values of each member are.this make life a pain for people writing library classes that are intended for other to use, and still keep them robust. doesn't this also mean that D will have performance and extra memory usage if you have a class like this class MyClass { // assume MyOtherClass allocates a LOT of resources in all cases MyOtherClass item = new MyOtherClass(); // force a default in case of error; this( char[] a ) { item = new MyOtherClass(a ); } this( char[] a, char[][] maps ) { item = new MyOtherClass( a, maps ); } } because for every instance of MyClass I have to an instance of MyOtherClass so that my code is robust. instead of having this( ) { item = new MyOtherClass(); } allowing the item member to be initialised only one no matter how the instance of MyClass is constructed. which lead me to D allowing both the C++ x = new MyClass; and the Java x = new MyClass(); does x = new MyClass; call MyClass.this() ? if not then at least there is a consistancy in that all classes have a "hidden" implicit constuctor. but I thought the basic idea behind D was to remove pitfalls and allow programmers to write the code in an intuative fashion, adding more "hidden" features IMHO does not do this. Mike.
Oct 01 2002
The static initializer must be evaluated at compile time, so it can't be MyOtherClass item = new MyOtherClass(); // force a default in case of It should be null. To make a robust class, try a class invariant: invariant { assert(item != null); } The base invariant will get called at the close of the derived constructor, and will so guarantee that the derived constructor did its work as the base class designer intended. C++ has no static initialization of class members, so if you do not explicitly initialize them in the constructor (and there is no check for this), they are initialized to garbage. I've had endless bugs from that. D's static initialization of all members before the constructor is called eliminates those errors. -Walter "Mike Wynn" <mike.wynn l8night.co.uk> wrote in message news:andk7d$2mtl$1 digitaldaemon.com...thenNo, super must be called explicitly. If super is not called anywhere,ofyou get the static initializer, which is what the static default valuesmaps ); }each member are.this make life a pain for people writing library classes that are intended for other to use, and still keep them robust. doesn't this also mean that D will have performance and extra memory usage if you have a class like this class MyClass { // assume MyOtherClass allocates a LOT of resources in all cases MyOtherClass item = new MyOtherClass(); // force a default in case of error; this( char[] a ) { item = new MyOtherClass(a ); } this( char[] a, char[][] maps ) { item = new MyOtherClass( a,} because for every instance of MyClass I have to an instance ofMyOtherClassso that my code is robust. instead of having this( ) { item = new MyOtherClass(); } allowing the item member to be initialised only one no matter how the instance of MyClass is constructed. which lead me to D allowing both the C++ x = new MyClass; and the Java x = new MyClass(); does x = new MyClass; call MyClass.this() ? if not then at least there is a consistancy in that all classes have a "hidden" implicit constuctor. but I thought the basic idea behind D was to remove pitfalls and allow programmers to write the code in an intuative fashion, adding more "hidden" features IMHO does not do this. Mike.
Oct 01 2002
I think that keeping the focus on unit testing and design-by-contract is more important that tradition here. Even though it seems to me to violate the principle of least surprise (in terms of someone coming in from C++), I think that it's easy enough to keep in mind, and getting caught by a bug in this once will likely push new people more strongly towards using invariants, unittests and the like is a more effective manner. Evan Walter wrote:The static initializer must be evaluated at compile time, so it can't be MyOtherClass item = new MyOtherClass(); // force a default in case of It should be null. To make a robust class, try a class invariant: invariant { assert(item != null); } The base invariant will get called at the close of the derived constructor, and will so guarantee that the derived constructor did its work as the base class designer intended. C++ has no static initialization of class members, so if you do not explicitly initialize them in the constructor (and there is no check for this), they are initialized to garbage. I've had endless bugs from that. D's static initialization of all members before the constructor is called eliminates those errors. -Walter
Oct 02 2002
"Evan McClanahan" <evan dontSPAMaltarinteractive.com> wrote in message news:anege2$ijt$1 digitaldaemon.com...I think that keeping the focus on unit testing and design-by-contract is more important that tradition here. Even though it seems to me to violate the principle of least surprise (in terms of someone coming in from C++), I think that it's easy enough to keep in mind, and getting caught by a bug in this once will likely push new people more strongly towards using invariants, unittests and the like is a more effectivemanner.Tradition is not always wrong, and why change it if it works ? I'm not sure its so much "least surprise" but more implied by syntax in Delphi/Object pascal, where constructors can have any name (and can be virtual) to call the super constructor you use `inherited Create;` (in the constuctor or to allow a base constructor to be inherited by a abstract class.) the syntax is different enought that it implicity warns the unwary that what they know to be true might not be, the same is true with Perl. Php also has classes and again as it looks like a Perl+Java hybrid so its easy to not be expect any predefined behaviour. D however looks like C++ and Java both of which have predefined behaviours related to constructors. I know the vtables are different in constructors in C++ and Java, but I always found the C++ method annoying, (look at Borlands TurboVision Sources which uses a virtual base class to allow a base class to call a derived class method at construction time). also Delph/Object Pascal have a more verbose syntax, no +=,++ etc so it is more natural to expect to have to type more code. C/C++/Java have a lot of nice short cuts, break, continue, return etc as keywords not function calls so it seems logical that any language that inherits their syntax will also inherit their default behaviours. I never got to grips with Smalltalk/Self syntax so have no idea what they do not that "becuase they do it" is any argument for doing something, but the reason why they do is. I belive that the question should be what method is the most robust, most intuative and least likely to cause problems. I personally think that it should be this( params ) : this/super( possible static method calls with params, or just params or consts) { body } within any code obj.renew( params ); or within a constructor this() can be called to "reconstruct" and object into a new state. and also have Delphi sytle 'virtual' constructors so a type of 'class' can be passed to methods as in. Object makeANew( char[] name, class objClass ) { return objClass.new( name ); } MyObj makeANew( int len, class<MyObj> objClass ) { return objClass.new( len ); } or MyObj makeANew( int len, classref(MyObj) objClass ) { return objClass.new( len ); } I feel that class invariants should be evaluated when the code leaves that classes methods but not if you are calling super.whatever(); because that is the "contract". To be realy safe all super.invariants should be called too to make sure this class have not disturbed the super classes state before entering/reentering subclass or external code. I do not see how not calling a default constructor will make those coming from a C++/Java background use invariants/unittest, (especially unittest) it's not my Base class that will have the bugs its the derived classes and I have no way to "force" default behaviour, the best I can achive is checks at runtime that they(the writers of the derived classes) wrote the right think. I want to make these checks at compile time becuase as we all know runtime checks are great, but some conditions only occur under special conditions, it is much better to be able to catch potential problems at compile time. than lots of testing, unit test are a great help, but as I've kept saying the problem is that the derived classes need the unittests not the base class. I worked on a large (ish) perl project and had quite a lot of trouble getting other to 'use strict' because it was not until they had actually run fowl of a few problems could they understand why making sure variables where at least declared first was a good idea. this is a very similar situation. I confess I'm not the worlds greatest programmer, I make all manner of stupid mistakes, I cut'n'paste classes and forget to change things, I forget to do all manner of things when writing code that I should, and I like languages that warn me that I'm being an idiot when if forget to do the basics. especially as I have used several with similar syntax and am for every getting operator precidence wrong or trying to use language features in the wrong language how I wish C have Perl's redo. Who is D aimed at and why should I use it in perference to C/C++/Java/Delphi/Kylix/Perl/Php/Lua and what does it offer that would stop I was under the impression that D would offer me most of C/C++/Java/Delphi without having to remember 3 different syntaxes and better compile time error checking, more robust code, performance matched feature for feature (so I should not be able to gain a huge speed up by changing away from D) all with less pitfalls for the unwary. allowing me to write better code, quicker that was less prone to simple programmer errors. this behaviour combined with the new interface symantics has started me investigate the gcc Java complier. Mike.
Oct 02 2002
"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message news:aner8q$utg$1 digitaldaemon.com...Tradition is not always wrong, and why change it if it works ?I don't agree that the way C++ constructors works does work. I've had so many bugs from having multiple constructors for an object, adding a field to the class, and forgetting to initialize it in one of the constructors. I've wanted many times to have more complex constructors call a more basic constructor for the same class (not just the super class). I've wanted to do some complex computation before calling the base class constructor. All these are broken in C++. D offers great flexibility in building constructors, as well as offering guarantees that every field gets initialized to something predictable. D offers contracts to verify more complex relationships that must be held.I confess I'm not the worlds greatest programmer, I make all manner of stupid mistakes, I cut'n'paste classes and forget to change things, Iforgetto do all manner of things when writing code that I should, and I like languages that warn me that I'm being an idiot when if forget to do the basics. especially as I have used several with similar syntax and am for every getting operator precidence wrong or trying to use language features in the wrong language how I wish C have Perl's redo.While D offers better compile time checking than C++, especially for the case of inadvertantly not initializing a class member, you can get huge gains in robustness with even a modest use of class invariants, contracts, and unit tests. I was surprised at how many of my stupid bugs unit tests have caught in even the simple runtime library code.Who is D aimed at and why should I use it in perference to C/C++/Java/Delphi/Kylix/Perl/Php/Lua and what does it offer that wouldstopI was under the impression that D would offer me most of C/C++/Java/Delphi without having to remember 3 different syntaxes and better compile time error checking, more robust code, performance matched feature for feature (so I should not be able to gain a huge speed up by changing away from D) all with less pitfalls for the unwary. allowing me to write better code, quicker that was less prone to simple programmer errors.I believe it does.this behaviour combined with the new interface symantics has started meorinvestigate the gcc Java complier.The interface semantics are going to get redone <g>.
Oct 04 2002
"Walter" <walter digitalmars.com> wrote in message news:andpff$2s31$1 digitaldaemon.com...The static initializer must be evaluated at compile time, so it can't be MyOtherClass item = new MyOtherClass(); // force a default in case of It should be null. To make a robust class, try a class invariant: invariant { assert(item != null); }yes that will catch the error at runtime, but not tell the user of the library (my consern here is classes that form part of a library) what they did wrong, and to an inexperianced (in D) programmer they may think that it is the library at fault not their code.The base invariant will get called at the close of the derivedconstructor,and will so guarantee that the derived constructor did its work as thebaseclass designer intended.why is base invariant not called at the close of the base constructor ? that would be where I would expect it to be, does this mean that it is not called if the class is not subclassed ? is it just after the immediate sub class constructor, or after the outermost ? given that I can call this() and super() repeated times within any given constructor and have code between them surely the Base state should be 'correct' at all times when the 'thread' is not running Base code.C++ has no static initialization of class members, so if you do not explicitly initialize them in the constructor (and there is no check for this), they are initialized to garbage. I've had endless bugs from that.D'sstatic initialization of all members before the constructor is called eliminates those errors.I agree, Java solves this by initialising all members to 'null' before calling the constructor I personally would rather this behaviour, and this() gettting called to the current scheme. it seems to me that you have just changed all the problem with members not getting initialised from the base class into the derived class and created a new set of possible programmer errors. so the base class designer have to remember that there are two this()'s one implicit and one explicit, one that allow code to be run, one that does not consider: class Base { MemberItem item; this() { item = new MemberItem(); setParent.item( this ); } } how do I make sure MemberItem::setParent is called and item is set? I assume `auto MemberItem item;` would make item's static constructor to be called not "MemberItem::this()" class Base { MemberItem item; bit initialised; this() { item = new MemberItem(); initialised = true; setParent.item( this ); } invariant { if ( !initialised ) { item = new MemberItem(); initialised = true; setParent.item( this ); } } } why is `static this() { ... }` always called for a module ? when `this() { ... }` has to be manually called for a base class.
Oct 02 2002
In article <anej18$lmi$1 digitaldaemon.com>, Mike Wynn says..."Walter" <walter digitalmars.com> wrote in message news:andpff$2s31$1 digitaldaemon.com...First, let me state that my personal feeling is that a derived class's this() should be required to call super(), causing a compiler error if it is not present. If a real need exists for derived classes that must not (or should not, if the "should not" is strong enough to warrant a language mod) call their base class constructor, then I think that special case should warrant a special purpose keyword or other tagging construct. Perhaps "uninitialized" or something similar, to flag that this derived class will not being calling the superclass constructor. Having said all of that, if we are stuck with the invariant approach, one way to ensure that the invariant catches what you want and is somewhat clear as well is to do something like this (pardon any syntax errors in this -- I haven't done any real programming in D, so my reflexes aren't there yet...): class MyBaseClass { public: // whatever this() {MyBaseClass_has_been_constructed = true; /* other stuff */} protected: // whatever private: // whatever bit MyBaseClass_has_been_constructed = false; } invariant { assert(MyBaseClass_has_been_constructed); } Because MyBaseClass_has_been_constructed is a private member, the only way for derived classes to change its value is by calling super(). A better name is probably possible for the variable -- telling someone that the base class was not constructed doesn't explain how it failed to be constructed. If the assertion can only ever fail due to a derived class forgetting super(), the name might be better as MyBaseClass_has_been_built_with_call_to_super, or some similarly painful thing... This kind of programming, while still a runtime check, is not dependent on situations or conditions. The assertion will fail unless super() was called. It does, however, require all base class developers to be conscientious and write this kind of invariant. More painfully, it requires all class developers to be this conscientious, because even though they may not have expected anyone to derive a class from their class, someone may find a need to do so. To me, this is highly reminiscent of the problem with the "virtual" keyword in C++. The designers chose to not do virtual by default because of the performance and memory penalties, but the result is that almost all C++ programmers make extremely difficult to locate bugs because they forget to specify virtual on a member function. Similarly, the major reason I can see for not wanting to call super() is when you know (or think you know) that you are going to overwrite all of its variables, so "why waste time letting it set them up?". The problem with this thinking is: 1. You don't know about the private members it may be setting up, and you can't do anything about them even if you did. 2. If a problem is discovered in the base class, and the fix changes either the number of members or the constructor, your derived class will not take advantage of the fix because you won't be aware of the new members and you aren't using the constructor. In theory, the base invariant should be modified to catch this sort of thing, but the reality is that it won't be able to catch everything. And, as mentioned by others, even if it catches it, the explanation for what is wrong won't be terribly clear. Like I said earlier, my preference is strongly in favor of requiring a call to super() in any derived class this(), with the addition of some kind of "uninitialized" keyword/tag if such functionality is deemed necessary (either for correctness or performance, but I really only think that correctness is a valid argument here). I also agree with someone else's comment that the base invariant should be called at the end of the base constructor, even when invoked through super(). I also think it should be called _again_ at the end of the derived constructor, since it is a valid invariant for the derived class. I think the earlier call should be made so that the derived class constructor is guaranteed that the base constructor has satisfied itself and that the object is in a valid state (at least for the base class) before the remaining derived code tries to use any of it. After all, you call super() because you want yourself to be set up like an instance of the base class, so you are probably going to assume that that has been done. A guarantee about that should be made. As an optimization step, of course, if super() is the _last_ thing in the derived constructor, then the "end of derived constructor multiple invariant check" would suffice -- the base constructor would not need to be called twice in a row (end of super() and end of this()). MacThe static initializer must be evaluated at compile time, so it can't be MyOtherClass item = new MyOtherClass(); // force a default in case of It should be null. To make a robust class, try a class invariant: invariant { assert(item != null); }yes that will catch the error at runtime, but not tell the user of the library (my consern here is classes that form part of a library) what they did wrong, and to an inexperianced (in D) programmer they may think that it is the library at fault not their code.
Oct 02 2002
"Mac Reiter" <Mac_member pathlink.com> ha scritto nel messaggio news:anf1ji$15s4$1 digitaldaemon.com... [...]To me, this is highly reminiscent of the problem with the "virtual"keyword inC++. The designers chose to not do virtual by default because of the performance and memory penalties, but the result is that almost all C++ programmers make extremely difficult to locate bugs because they forget to specify virtual on a member function. Similarly, the major reason I cansee fornot wanting to call super() is when you know (or think you know) that youaregoing to overwrite all of its variables, so "why waste time letting it setthemup?". The problem with this thinking is: 1. You don't know about the private members it may be setting up, and youcan'tdo anything about them even if you did. 2. If a problem is discovered in the base class, and the fix changeseither thenumber of members or the constructor, your derived class will not takeadvantageof the fix because you won't be aware of the new members and you aren'tusingthe constructor. In theory, the base invariant should be modified tocatch thissort of thing, but the reality is that it won't be able to catcheverything.And, as mentioned by others, even if it catches it, the explanation forwhat iswrong won't be terribly clear. Like I said earlier, my preference is strongly in favor of requiring acall tosuper() in any derived class this(), with the addition of some kind of "uninitialized" keyword/tag if such functionality is deemed necessary(eitherfor correctness or performance, but I really only think that correctnessis avalid argument here).This is a VERY GOOD point. I completely agree with you, Mac. Ciao
Oct 04 2002
"Mac Reiter" <Mac_member pathlink.com> wrote in message news:anf1ji$15s4$1 digitaldaemon.com...I also agree with someone else's comment that the base invariant should be called at the end of the base constructor, even when invoked throughsuper(). Ialso think it should be called _again_ at the end of the derivedconstructor,since it is a valid invariant for the derived class.That should be the way it works now, I miswrote it. Some issues with the compiler flagging not calling super: class A { this() { .. do general construction .. } this(int x) { this(); .. do more construction ..} } Calling super in each constructor means it gets called twice. Secondly, there is: this() { if (...) super(); } So super() isn't always run. It's impossible for the compiler to verify that super() is called exactly once in the general case.
Oct 04 2002
I forgot to mention that Java requires any this() or super() call to be the first statement in a constructor. Things like: this() { a = b; super(); } are not allowed. Java's syntax is better than C++'s, but it still does not allow any non-trivial computation before the super constructor gets called. D will allow things like: this() { .. complex calculation .. if (...) super(); else super(abc); }
Oct 04 2002
Calling super in each constructor means it gets called twice. Secondly, there is: this() { if (...) super(); } So super() isn't always run. It's impossible for the compiler to verifythatsuper() is called exactly once in the general case.yes you can ! by walking through the syntax tree and checking that there is a super() on all possible paths just like javac can detect if you have not assigned a value to a local before use (does err in faviour of safetly) Mike.
Oct 04 2002
"Walter" <walter digitalmars.com> wrote in message news:anjhap$2t9h$1 digitaldaemon.com..."Mac Reiter" <Mac_member pathlink.com> wrote in message news:anf1ji$15s4$1 digitaldaemon.com...beI also agree with someone else's comment that the base invariant shouldthatcalled at the end of the base constructor, even when invoked throughsuper(). Ialso think it should be called _again_ at the end of the derivedconstructor,since it is a valid invariant for the derived class.That should be the way it works now, I miswrote it. Some issues with the compiler flagging not calling super: class A { this() { .. do general construction .. } this(int x) { this(); .. do more construction ..} } Calling super in each constructor means it gets called twice. Secondly, there is: this() { if (...) super(); } So super() isn't always run. It's impossible for the compiler to verifysuper() is called exactly once in the general case.To choose from super constructors at runtime is a good idea. I see, that this makes (near) impossible to detect all cases where a constructor avoids calling the super. But the compiler could still try to detect where it is easy. If there is no mention of same-level or super constructors in a constructor, then it is clearly an error. This would help to avoid a typical error of forgetting the super call.
Oct 07 2002
"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message news:anej18$lmi$1 digitaldaemon.com..."Walter" <walter digitalmars.com> wrote in message news:andpff$2s31$1 digitaldaemon.com...ofThe static initializer must be evaluated at compile time, so it can't be MyOtherClass item = new MyOtherClass(); // force a default in caseCorrect, but you can always add: if (item == null) printf("item is not set at close of constructor - perhaps you need to call super()?\n"); before the assert.It should be null. To make a robust class, try a class invariant: invariant { assert(item != null); }yes that will catch the error at runtime, but not tell the user of the library (my consern here is classes that form part of a library) what they did wrong,and to an inexperianced (in D) programmer they may think that it is the library at fault not their code.It may take a while for new D programmers to get used to the idea of getting contract exceptions rather than the crashes one gets from using C++ api's wrong <g>.afterThe base invariant will get called at the close of the derivedconstructor,and will so guarantee that the derived constructor did its work as thebaseclass designer intended.why is base invariant not called at the close of the base constructor ? that would be where I would expect it to be, does this mean that it is not called if the class is not subclassed ? is it just after the immediate sub class constructor, orthe outermost ?Oh, you're right, I made a mistake. It *is* called at the close of the base constructor, as well as at the close of the derived constructor.given that I can call this() and super() repeated times within any given constructor and have code between them surely the Base state should be 'correct' at all times when the 'thread' is not running Base code.You're right.theC++ has no static initialization of class members, so if you do not explicitly initialize them in the constructor (and there is no check for this), they are initialized to garbage. I've had endless bugs from that.D'sstatic initialization of all members before the constructor is called eliminates those errors.I agree, Java solves this by initialising all members to 'null' before calling the constructor I personally would rather this behaviour, and this() gettting called tocurrent scheme.I'm not sure that Java's semantics here offer anything over D's.it seems to me that you have just changed all the problem with members not getting initialised from the base class into the derived class and created a new set of possible programmer errors. so the base class designer have to remember that there are two this()'s one implicit and one explicit, one that allow code to be run, one that does not consider: class Base { MemberItem item; this() { item = new MemberItem(); setParent.item( this ); } } how do I make sure MemberItem::setParent is called and item is set? I assume `auto MemberItem item;` would make item's static constructor tobecalled not "MemberItem::this()"Yes.class Base { MemberItem item; bit initialised; this() { item = new MemberItem(); initialised = true; setParent.item( this ); } invariant { if ( !initialised ) { item = new MemberItem(); initialised = true; setParent.item( this ); } } }No, invariant should not be used to set any state. It should only be used to verify that the class invariants hold - this is because invariants can be removed with a compile time switch. You also don't need an 'initialised' flag, just test 'item' for null.why is `static this() { ... }` always called for a module ? when `this() { ... }` has to be manually called for a base class.this() does not need to be manually called. It is implicitly called for any: new Base; It's the super() that needs to be manually called.
Oct 04 2002
Walter wrote:No, invariant should not be used to set any state. It should only be used to verify that the class invariants hold - this is because invariants can be removed with a compile time switch. You also don't need an 'initialised' flag, just test 'item' for null.Would it be possible (or even useful) to have the compiler throw a warning or error if someone does something with side effects in a invariant, unittest, or contract statement? I realize that the checking would be non-trivial, but it might be helpful to transitioning programmers in terms of laerning how to use them correctly. I could see, for example, someone using in{} pre-conditions to do something like flattening their input into a certain range, and then having weird bugs when they compile for release and then it isn't flattend into that range anymore. Perhaps it's a non-issue, but it's an idea, at the least. Evan
Oct 04 2002
"Evan McClanahan" <evan dontSPAMaltarinteractive.com> wrote in message news:anjk6f$308i$1 digitaldaemon.com...Would it be possible (or even useful) to have the compiler throw a warning or error if someone does something with side effects in a invariant, unittest, or contract statement? I realize that the checking would be non-trivial, but it might be helpful to transitioning programmers in terms of laerning how to use them correctly. I could see, for example, someone using in{} pre-conditions to do something like flattening their input into a certain range, and then having weird bugs when they compile for release and then it isn't flattend into that range anymore. Perhaps it's a non-issue, but it's an idea, at the least.That's an important issue. The problem is, I think it is impossible for the compiler to verify that the contracts have no side effects.
Oct 04 2002
No, invariant should not be used to set any state. It should only be usedtoverify that the class invariants hold - this is because invariants can be removed with a compile time switch. You also don't need an 'initialised' flag, just test 'item' for null.but what if later on the item CAN be null, just not when initialised (horrid case I agree, and I'm being devil advocate here).{why is `static this() { ... }` always called for a module ? when `this()any:... }` has to be manually called for a base class.this() does not need to be manually called. It is implicitly called fornew Base; It's the super() that needs to be manually called.this I find inconsistant, why `new Base();` and `new Base;` the former I prefer, the latter if you have it should not call this(); I had a good chat yesterday with a C++ programmer friend on mine, and we discussed the constructor problem, and why Java has the restrictions that it does; Java much have a call to super as the first source instruction, but the VM allows bytecode before the invoke_special call to the super( blablabla ); as long as the 'this' pointer is not referenced (checked by the verifier). so a constructor can be : class MyClass { MyClass( int a, int b ) { super( MyStaticFunc( a ), b ); ..... } String MyStaticFunc( int a ) { return new Integer( a ).toString(); } } BUT you cannot catch an exception from the base class because that would allow an incomplete Object to be created. the solution (not perfect but influenced but this and Delphi/Perl which allow a constructor to have any name) was this (mix of code and BNF, regexp); class MyClass { this( params ) : [super( static_call(params) | params )], [member(static_call(params) | params )] { .... code with try catch allowed, any members not set in the initialiser list ... will be null 0, 0.0, 0.0+i0.0 etc. } // 'creator' <name>'(' <param list> ' )' <block> creator restore( params ) { // on entry this will be null if called direct or be non null if called from a sub class this( params ) ... code .... .. many calls to this as you like try { // will create a clone and then call this on the new object, if good will update this pointer // items that are not explicitly modified by the constructor will not be effected and will // be unchanged. this( stuff ); super( params ); // will call super.restore( params ); // again this will create a clone first so that on exception the state is consistent. } catch ( Exception e ) { // this will be the this BEFORE the try block NOT the partial made object. } super( params ); // calls super.restore(); // no need to clone (not in a try clause) } } syntax for using MyClass obj = MyClass.restore( params ); // obj MAY BE NULL a bit like Delphi. a creator may apear just a static function that calls new with an implicit this, but the return type is that of the class and it supports inherited calls (a class virtual if you like). I'm not sure by you may consider the following MyClass obj = MyClass.restore( params ); obj.restore( other_params ); // re make the object; again if this is wrapped in a try clause the compiler will have to "clone" it first to retain a stable state. and the very delphi class MyDerivedClass : MyClass { ...defines a creator restore ... } MyClass func( class MyClass cptr ) { return cptr.restore( params ); } MyClass obj = func( MyDerivedClass ); obj will be of type MyDerivedClass, and will have be created via the MyDerivedClass.restore. this allows both the quick obj = new Class(); for simple objects, and the creator methods for more complex construction. IMHO: a constructor should make the object and initialise it, but not do a huge amount of work and above all should always give you an object that is in a 'stable' state. Mike.
Oct 04 2002
"Walter" <walter digitalmars.com> wrote in message news:andhua$2kli$1 digitaldaemon.com..."Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:3D9A286E.9070900 deming-os.org...inMike Wynn wrote:I quote .... D aims to reduce software development costs by at least 10% by addingbug.features soproven productivity enhancing features and by adjusting languagesuper(...);that common, time-consuming bugs are eliminated from the start. but surely forceing the user to remember to explicitly call aaskingsomewhere in a this(...) that does not call this(.other.params.); issuper,for trouble. again my Java and C++ background, where if you don't put in a call toconstructorthen super() is assumed I feel that D should either call super() at the begining of anyyou'vethat does not call super(...) or another this(...) or warn you thatforgotten something.IIRC, this is how things are supposed to work. It may be a compilerNo, super must be called explicitly. If super is not called anywhere, then you get the static initializer, which is what the static default values of each member are.Hmm. Constructor is supposed to initialize the object. The existence of the constructor itself guarantees that an object cannot be created withouth initialization. Actually, with good-designed methods, it guarantees internal consistency of the object. If it is *optional* to call the constructor, then you should not call that method "constructor". It does not fit into the definition. What kind of design needs to avoid the constructor? Example? This ruins one of the important concepts of OOP. Please require calling the super constructor! Yours, Sandor
Oct 02 2002
"Sandor Hojtsy" <hojtsy index.hu> wrote in message news:aneat4$d56$1 digitaldaemon.com...What kind of design needs to avoid the constructor? Example?See my reply to Mac Reiter.
Oct 04 2002
Ok, you've all convinced me. I'll change the semantics so a super() is inserted automatically if none is already present. -Walter
Oct 04 2002
Walter wrote:Ok, you've all convinced me. I'll change the semantics so a super() is inserted automatically if none is already present. -WalterI won't put implicit code generation of something so important in my port. An error, okay - I disagree, but it's acceptable. Putting in code behind the programmer's back is not.
Oct 05 2002
In article <anng7t$1m75$1 digitaldaemon.com>, Burton Radons says...Walter wrote:I agree.Ok, you've all convinced me. I'll change the semantics so a super() is inserted automatically if none is already present. -WalterI won't put implicit code generation of something so important in my port. An error, okay - I disagree, but it's acceptable. Putting in code behind the programmer's back is not.
Oct 05 2002
In article <anng7t$1m75$1 digitaldaemon.com>, Burton Radons says...Walter wrote:What? We've got a garbage collector, and we now have RAII destruction/finalization. Where does "putting in code behind the programmer's back is not (acceptable)" but "An error ... is acceptable" come from? We're already putting in code "behind the programmer's back" to make programming more reliable (GC, RAII finalization). Plus, anybody coming from any OOP language with inheritance is going to expect that the base constructor was called before the derived constructor anyway... Having said that, my personal preference is to require either a call to super() or a keyword that signals that super() is deliberately not called. Then, throw a compilation error if neither "call" is present in the constructor. Forces explicitness, avoids "oops, I forgot" errors, and also doesn't insert code behind anybody's back... I guess my larger grump is with the divergence I already see between the Win32 DMD language and the DLI language. There are already programs that will compile and run under DLI that use features not even present in DMD. I would understand if it was a "completeness of implementation" issue, but it isn't -- DLI has specifically added things to its language that are not part of D/Phobos. It does not appear that these additional features went through the degree of discussion and agreement that the DMD changes seem to be exposed to. I'm not sure if all of those changes would fit under an "extended library" behavior or not. But if a split occurs on something like "what to do with super() in a derived constructor", it will essentially be impossible to call both languages D. Once a "port" decides not to implement a language feature, then it isn't a "port" any more. I guess the even bigger grump is the perceived attitude. "I won't put <feature X> in my port." Fine, but then it isn't a port. Sorry, gotta go to yet another meeting... MacOk, you've all convinced me. I'll change the semantics so a super() is inserted automatically if none is already present. -WalterI won't put implicit code generation of something so important in my port. An error, okay - I disagree, but it's acceptable. Putting in code behind the programmer's back is not.
Oct 07 2002
Mac wrote:I guess the even bigger grump is the perceived attitude. "I won't put <feature X> in my port." Fine, but then it isn't a port.I agree. If it's called D, then it should be D. Still in these early days it might be wise to try two or three different development forks to see what people like; but only with the expectation that all such forks will eventually merge into D proper. Mark
Oct 07 2002
What? We've got a garbage collector, and we now have RAII destruction/finalization.While I agree with your larger sentiment about not fragmenting ports, I don't think these two examples really make your claim. The garbage collector just reclaims memory that is safe to reclaim, and indeed does not even need to be run when the system has enough ram. Second RAII inserts code when the programmer specifies that RAII is desired. Not the inverse, which is: insert code because I (the compiler) think the programmer is a dummy and I know better.
Oct 07 2002
I think these issues are related, what is required is a unamigious statement of mission. I agree in concept with the idea that the compiler should not add code because it considers me a fool but at the same time, we all make fools of ourselfs whilst programming. and to quote murphys laws of technology "make a system fool proof and only a fool would want to use it" I was under them impression part of the design of D was to reduce the pitfalls and allow a programmer to realise the foolishness of an action before they try to run flawed code. the issuse of base constructors, I would consider it potentially dangerous to NOT call super before accessing an inherited public or protected member or invoking a member function that does that. if you want to do this then you should tell the compiler you intention. in exactly the same way that garbage collection allows a programmer to let the compiler/program chose when to cleanup/deallocate any created items, but RAII and static arrays allow you to decide the life time of an item. you have not mentioned dynamic arrays and hashtables as builtin types, these also perform many tasks that are not explicitly requested by the programmer such as allocations. I am finding D quite a confusing language to use, proof" features like type safetly, default virtual and GC etc. it also has script language like features such as builting dynamic array and hashtables rather than templated collections, so generic parameters seem a logical inclusion too. but then allows me to make a bigger fool of myself with simple typo's by allowing a class to never call a super constuctor, stack allocation of arrays that can be referenced in heap objects and very strange dynamic array behaviour when passed as 'in'. follows this and although Java does not, partly because an interface is not a separate vtbl, and Java is much more dynamic and very tuned to the JavaVM, neither is a good reason for any given semantics. having mark a class as implementing an interface if it changes the implementation does make sense, there should also be a mechanism to inherit interface methods that do not require reimplementing. I am finding it odd that features have been added (templates, RAII) while there are still basic issues like array handling, constructor semantics and passing stack allocated items that need to be addressed. I would like to see a simple, design doc with mission statement and design ideology, containing enought information that these issues can be discussed in terms of this document. it should answer the question, who D is aimed at, what you should need to know to understand a piece of D code (i.e. what are the basics of the basic types and how to they interoperate). number one on the list should be IMHO: a D program or section of program should be written to perform a task, the programmer should write a description of the task they wish to apply to a set of items, and the limits within which this occurs, without having to be aware of the full implications of their actions, but with the assurance that their task will be carried out safely and in the most efficient manner. the full implications of a program should be determinable, and should not contain any unwanted side effects that effect the context from which the program or section was called. Mike. "Joe Battelle" <Joe_member pathlink.com> wrote in message news:antf5v$mtr$1 digitaldaemon.com...don'tWhat? We've got a garbage collector, and we now have RAII destruction/finalization.While I agree with your larger sentiment about not fragmenting ports, Ithink these two examples really make your claim. The garbage collectorjustreclaims memory that is safe to reclaim, and indeed does not even need tobe runwhen the system has enough ram. Second RAII inserts code when theprogrammerspecifies that RAII is desired. Not the inverse, which is: insert codebecauseI (the compiler) think the programmer is a dummy and I know better.
Oct 08 2002
"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message news:anuh63$1sc6$1 digitaldaemon.com...but then allows me to make a bigger fool of myself with simple typo's by allowing a class to never call a super constuctor,That is fixed.stack allocation of arrays that can be referenced in heap objects and very strange dynamicarraybehaviour when passed as 'in'.??follows this and although Java does not, partly because an interface isnota separate vtbl, and Java is much more dynamic and very tuned to theJavaVM,neither is a good reason for any given semantics. having mark a class as implementing an interface if it changes the implementation does make sense, there should also be a mechanism toinheritinterface methods that do not require reimplementing.The interface semantics were changed to allow overriding of individual methods. The issue is not one of a separate vtbl[] vs Java's runtime lookup system - the semantics are the same. The separate vtbl[] is there because: 1) it is a lot faster 2) working with COM requires it
Oct 09 2002
The interface semantics were changed to allow overriding of individual methods. The issue is not one of a separate vtbl[] vs Java's runtimelookupsystem - the semantics are the same. The separate vtbl[] is there because:but what about the statment ... A reimplemented interface must implement all the interface functions, it does not inherit them from a super class I do not understand you should implements all of an reinplemented interface, if you what to modify part of it, but not all them you can not comment your code to say 'I implement X' There are differences between the Java signaure based interface and the multipul vtbl approach, the Java Style CAN be implemented as separate vtbls BUT multipul vtbl CAN NOT be implemented with the method signature alone as it allows two interfaces to have the same signature but different implementations within a single class.1) it is a lot faster 2) working with COM requires itI know, when I worked on a JavaVM I suggested it was used to speed up interface calls but it was rejected on the grounds of extra memory usage (and with a JavaVM you'd still have to search for the right vtbl). the docs do not say what will happen with interface D { int foo(); } interface E { int foo(); } class A : D { int foo() { return 1; } } class B : A, E { int foo() { return 2; } } which is valid within COM interfaces or worse interface D { int foo(); } interface E { int foo(); } class A : D { int foo() { return 1; } } class B : A, E { int foo() { return 2; } } class C : B { int foo() { return 2; } // which foo is this foo; D or E ? }
Oct 09 2002
"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message news:ao28o3$2mvi$1 digitaldaemon.com...but what about the statment ... A reimplemented interface must implement all the interface functions, it does not inherit them from a super class I do not understand you should implements all of an reinplementedinterface,if you what to modify part of it, but not all them you can not commentyourcode to say 'I implement X'The idea is if you respecify the interface, you intend to reimplement it.There are differences between the Java signaure based interface and the multipul vtbl approach, the Java Style CAN be implemented as separate vtbls BUT multipul vtbl CAN NOT be implemented with the method signature alone as it allows two interfaces to have the same signature but different implementations withinasingle class.The vtbl[] can be set up to be anything the compiler implementor wants. The only issue is what semantics make sense.Yes, it will use more memory. D, though in general will use less memory because far fewer class objects are needed.1) it is a lot faster 2) working with COM requires itI know, when I worked on a JavaVM I suggested it was used to speed up interface calls but it was rejected on the grounds of extra memory usage (and with a JavaVM you'd still have to search for the right vtbl).the docs do not say what will happen with interface D { int foo(); } interface E { int foo(); } class A : D { int foo() { return 1; } } class B : A, E { int foo() { return 2; } } which is valid within COM interfacesB.foo() is used for D.foo() and E.foo().or worse interface D { int foo(); } interface E { int foo(); } class A : D { int foo() { return 1; } } class B : A, E { int foo() { return 2; } } class C : B { int foo() { return 2; } // which foo is this foo; D or E ? }Both.
Oct 10 2002