www.digitalmars.com         C & C++   DMDScript  

D - forgetting to call super doesn't call any super constructors.

reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
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
next sibling parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
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
parent reply "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D9A286E.9070900 deming-os.org...
 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.
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.
Oct 01 2002
next sibling parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
 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
parent reply "Walter" <walter digitalmars.com> writes:
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...
 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
next sibling parent reply Evan McClanahan <evan dontSPAMaltarinteractive.com> writes:
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
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"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 effective
manner.

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
parent "Walter" <walter digitalmars.com> writes:
"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, 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.
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 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.
I believe it does.
 this behaviour combined with the new interface symantics has started me

or
 investigate the gcc Java complier.
The interface semantics are going to get redone <g>.
Oct 04 2002
prev sibling parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"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 derived
constructor,
 and will so guarantee that the derived constructor did its work as the
base
 class 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's
 static 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
next sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
In article <anej18$lmi$1 digitaldaemon.com>, Mike Wynn says...
"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.
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()). Mac
Oct 02 2002
next sibling parent "Roberto Mariottini" <rmariottini lycosmail.com> writes:
"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 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).
This is a VERY GOOD point. I completely agree with you, Mac. Ciao
Oct 04 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"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 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.
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
next sibling parent "Walter" <walter digitalmars.com> writes:
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
prev sibling next sibling parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
 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.
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
prev sibling parent "Sandor Hojtsy" <hojtsy index.hu> writes:
"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...
 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.
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.
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
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"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...
 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,
Correct, 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.
 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>.
 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.
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 ?
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.
 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.
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.
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 to
be
 called 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
next sibling parent reply Evan McClanahan <evan dontSPAMaltarinteractive.com> writes:
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
parent "Walter" <walter digitalmars.com> writes:
"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
prev sibling parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
 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.
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()
{
 ... }` 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.
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
prev sibling parent reply "Sandor Hojtsy" <hojtsy index.hu> writes:
"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...
 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.
 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.
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
parent "Walter" <walter digitalmars.com> writes:
"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
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
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
parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:
 Ok, you've all convinced me. I'll change the semantics so a super() is
 inserted automatically if none is already present. -Walter
I 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
next sibling parent Joe Battelle <Joe_member pathlink.com> writes:
In article <anng7t$1m75$1 digitaldaemon.com>, Burton Radons says...
Walter wrote:
 Ok, you've all convinced me. I'll change the semantics so a super() is
 inserted automatically if none is already present. -Walter
I 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.
I agree.
Oct 05 2002
prev sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
In article <anng7t$1m75$1 digitaldaemon.com>, Burton Radons says...
Walter wrote:
 Ok, you've all convinced me. I'll change the semantics so a super() is
 inserted automatically if none is already present. -Walter
I 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.
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... Mac
Oct 07 2002
next sibling parent Mark Evans <Mark_member pathlink.com> writes:
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
prev sibling parent reply Joe Battelle <Joe_member pathlink.com> writes:
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
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
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...
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 08 2002
parent reply "Walter" <walter digitalmars.com> writes:
"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 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.
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
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
 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:
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 it
I 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
parent "Walter" <walter digitalmars.com> writes:
"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 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'
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 within
a
 single class.
The vtbl[] can be set up to be anything the compiler implementor wants. The only issue is what semantics make sense.
 1) it is a lot faster
 2) working with COM requires it
I 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).
Yes, it will use more memory. D, though in general will use less memory because far fewer class objects are needed.
 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
B.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