www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Class inheritance and string question.

reply "Marcin" <sixpackguy earthlink.net> writes:
I haven't experimented much with D yet, but I spotted something that could
be considered an anomaly to a C++ programmer. Inside the ctor of a derived
class you can call to super to initialize the base portion (this is done
with initializer lists in C++, before the constructor is entered to signify
that base parts are always initialized first, which makes total sense), but
the call to super can be placed anywhere inside the constructor. That means
you get to play around with the derived object before the base parts are set
up, which can cause hell if contained member objects try to use the base
portions. Why isn't this dealt with like in Java, where the call to super
has to be the first call in the constructor, otherwise the default base ctor
is called.
An unrelated question is why does D insist on calling a constructor "this()"
unlike most (all?) other OOP languages? Seems like name pollution to me, and
the keyword this is already reserved for the this reference.

Lastly, I've observed that strings can be initialized like this:
char[] str = null;
That looks very backwards because it allows for code like this:
char[] str = null;
if (str == "")
printf("whatever");

a null reference means there's no string object at all. Yet it (the phantom
string) compares equal to an empty string. Totally unituitive and doesn't
look too well thought out. There's a big discussion about this here:
http://www.osnews.com/comment.php?news_id=6761&offset=30&rows=45 (search for
posts by Marcus, Owen, J.F., ignore the rest)
Jun 09 2004
next sibling parent "Vathix" <vathixSpamFix dprogramming.com> writes:
"Marcin" <sixpackguy earthlink.net> wrote in message
news:ca8hdu$1jnl$1 digitaldaemon.com...
...
 Lastly, I've observed that strings can be initialized like this:
 char[] str = null;
 That looks very backwards because it allows for code like this:
 char[] str = null;
 if (str == "")
 printf("whatever");

 a null reference means there's no string object at all. Yet it (the
phantom
 string) compares equal to an empty string. Totally unituitive and doesn't
 look too well thought out. There's a big discussion about this here:
 http://www.osnews.com/comment.php?news_id=6761&offset=30&rows=45 (search
for
 posts by Marcus, Owen, J.F., ignore the rest)
null is a special case char[] that means address 0 and length 0. When you compare its value with "", they are equal, like: memcmp("", NULL, 0);
Jun 09 2004
prev sibling next sibling parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Marcin" <sixpackguy earthlink.net> wrote in message
news:ca8hdu$1jnl$1 digitaldaemon.com...
 I haven't experimented much with D yet, but I spotted something that could
 be considered an anomaly to a C++ programmer.
There are lots of those. D does not purport to be a haven solely for disenchanted C++ programmers. It offers many things to many viewpoints.
 Inside the ctor of a derived
 class you can call to super to initialize the base portion (this is done
 with initializer lists in C++, before the constructor is entered to signify
 that base parts are always initialized first, which makes total sense), but
 the call to super can be placed anywhere inside the constructor. That means
 you get to play around with the derived object before the base parts are set
 up, which can cause hell if contained member objects try to use the base
 portions. Why isn't this dealt with like in Java, where the call to super
 has to be the first call in the constructor, otherwise the default base ctor
 is called.
This is an important point.
 An unrelated question is why does D insist on calling a constructor "this()"
 unlike most (all?) other OOP languages? Seems like name pollution to me, and
 the keyword this is already reserved for the this reference.
This is a pretty trivial issue, don't you think?
 Lastly, I've observed that strings can be initialized like this:
 char[] str = null;
 That looks very backwards because it allows for code like this:
 char[] str = null;
 if (str == "")
 printf("whatever");

 a null reference means there's no string object at all. Yet it (the phantom
 string) compares equal to an empty string. Totally unituitive and doesn't
 look too well thought out.
I think when you get used to it you'll see that it's anything but. It's actually extremenly convenient.
 There's a big discussion about this here:
 http://www.osnews.com/comment.php?news_id=6761&offset=30&rows=45 (search for
 posts by Marcus, Owen, J.F., ignore the rest)
Jun 09 2004
prev sibling next sibling parent James Widman <james jwidman.com> writes:
In article <ca8hdu$1jnl$1 digitaldaemon.com>,
 "Marcin" <sixpackguy earthlink.net> wrote:
 An unrelated question is why does D insist on calling a constructor "this()"
 unlike most (all?) other OOP languages? 
It probably makes life simpler for the parser to have a special name for special functions like that. At any rate, it's simpler than C++, where ctors don't really have names (seeISO C++2003 12.1). Also, special names for ctors are not unheard of in other OO languages. See Python and Ruby for examples. Using 'this' as that special name shouldn't lead us down the path of extreme ambiguity. this() is a ctor, this is the literal "this", and this.this is probably the ctor. :-) James
Jun 09 2004
prev sibling next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <ca8hdu$1jnl$1 digitaldaemon.com>, Marcin says...
I haven't experimented much with D yet, but I spotted something that could
be considered an anomaly to a C++ programmer. Inside the ctor of a derived
class you can call to super to initialize the base portion (this is done
with initializer lists in C++, before the constructor is entered to signify
that base parts are always initialized first, which makes total sense), but
the call to super can be placed anywhere inside the constructor. That means
you get to play around with the derived object before the base parts are set
up, which can cause hell if contained member objects try to use the base
portions. Why isn't this dealt with like in Java, where the call to super
has to be the first call in the constructor, otherwise the default base ctor
is called.
I actually really like that way that D does this. I've had some problems with both C++ and Java. I can't remember the details, but I think it was something like:
    MyClass()  // Java constructor - D would use this()
    {
        try
        {
            super();
            /* the rest */
        }
        catch (Exception e)
        {
             /* do something else */
        }
    }
I may have got the details wrong, but it was something like that. Anyway, Java freaked out because super() wasn't the first thing in the constructor - but you can clearly see it makes perfect sense. Similar problems arise (in Java) if you try to replace try { ... } with synchronized(SomeObject) { ... }. Another situation is:
    this(MyEnum e)
    {
        switch(e)
        {
            case MyEnum.CASE1: super(parameters for case 1); break;
            case MyEnum.CASE2: super(parameters for case 2); break;
            case MyEnum.CASE3: super(parameters for case 3); break;
    ...etc.
No, on the whole, I think D has got it right. If you want super() called first, then call it first, or don't explicitly call it at all. But there will always be the odd special case where you really need to do something differently. Arcane Jill
Jun 09 2004
parent reply "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:ca8vto$2ahj$1 digitaldaemon.com...
 In article <ca8hdu$1jnl$1 digitaldaemon.com>, Marcin says...
I haven't experimented much with D yet, but I spotted something that could
be considered an anomaly to a C++ programmer. Inside the ctor of a derived
class you can call to super to initialize the base portion (this is done
with initializer lists in C++, before the constructor is entered to signify
that base parts are always initialized first, which makes total sense), but
the call to super can be placed anywhere inside the constructor. That means
you get to play around with the derived object before the base parts are set
up, which can cause hell if contained member objects try to use the base
portions. Why isn't this dealt with like in Java, where the call to super
has to be the first call in the constructor, otherwise the default base ctor
is called.
I actually really like that way that D does this. I've had some problems with both C++ and Java. I can't remember the details, but I think it was something like:
    MyClass()  // Java constructor - D would use this()
    {
        try
        {
            super();
            /* the rest */
        }
        catch (Exception e)
        {
             /* do something else */
        }
    }
I may have got the details wrong, but it was something like that. Anyway, Java freaked out because super() wasn't the first thing in the constructor - but you can clearly see it makes perfect sense.
Not in the slightest, I'm afraid. If you were allowed to do that, you'd have a "validly" constructed MyClass instance whose base class portion was undefined. Very bad.
Jun 10 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <ca9286$2fe1$1 digitaldaemon.com>, Matthew says...
Not in the slightest, I'm afraid. If you were allowed to do that, you'd have a
"validly" constructed MyClass instance whose base class portion was undefined.
Very bad.
Well, of course, you can do that in C++. This compiles fine using gcc:
   class A
   {
   public :
           A(void);
   };
   
   A::A(void)
   {
   }
   
   class B : public A
   {
   public :
           B(void);
   };

   // This is the interesting bit
   B::B(void)
   try : A()
   {
   }
   catch (...)
   {
   }
   
   int main(void)
   {
   }
Check out the strange and rarely used C++ syntax for the constructor B::B. It will catch exceptions which are thrown in the base class. Arcane Jill
Jun 10 2004
parent reply "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:ca94m4$2ius$1 digitaldaemon.com...
 In article <ca9286$2fe1$1 digitaldaemon.com>, Matthew says...
Not in the slightest, I'm afraid. If you were allowed to do that, you'd have a
"validly" constructed MyClass instance whose base class portion was undefined.
Very bad.
Well, of course, you can do that in C++. This compiles fine using gcc:
   class A
   {
   public :
           A(void);
   };

   A::A(void)
   {
   }

   class B : public A
   {
   public :
           B(void);
   };

   // This is the interesting bit
   B::B(void)
   try : A()
   {
   }
   catch (...)
   {
   }

   int main(void)
   {
   }
Check out the strange and rarely used C++ syntax for the constructor B::B. It will catch exceptions which are thrown in the base class.
Of course. But C++ trusts the programmer and Java does not.
Jun 10 2004
parent "Jan-Eric Duden" <jeduden whisset.com> writes:
And D trusts the programmer, too. :)
And I like that.

-- 
Jan-Eric Duden
"Matthew" <matthew.hat stlsoft.dot.org> wrote in message
news:ca96fo$2lbm$1 digitaldaemon.com...
 "Arcane Jill" <Arcane_member pathlink.com> wrote in message
 news:ca94m4$2ius$1 digitaldaemon.com...
 In article <ca9286$2fe1$1 digitaldaemon.com>, Matthew says...
Not in the slightest, I'm afraid. If you were allowed to do that, you'd
have a
"validly" constructed MyClass instance whose base class portion was
undefined.
Very bad.
Well, of course, you can do that in C++. This compiles fine using gcc:
   class A
   {
   public :
           A(void);
   };

   A::A(void)
   {
   }

   class B : public A
   {
   public :
           B(void);
   };

   // This is the interesting bit
   B::B(void)
   try : A()
   {
   }
   catch (...)
   {
   }

   int main(void)
   {
   }
Check out the strange and rarely used C++ syntax for the constructor
B::B. It
 will catch exceptions which are thrown in the base class.
Of course. But C++ trusts the programmer and Java does not.
Jun 10 2004
prev sibling parent reply "Marcin" <sixpackguy earthlink.net> writes:
"Matthew" <matthew.hat stlsoft.dot.org> wrote in message
news:ca9286$2fe1$1 digitaldaemon.com...
 Not in the slightest, I'm afraid. If you were allowed to do that, you'd
have a
 "validly" constructed MyClass instance whose base class portion was
undefined.
 Very bad.
syntax because it lets you catch base class exceptions and then translate and rethrow. With D you have to be careful about your member objects not to call any base portions or it will be undefined, and you have to be careful not to call any methods/functions that will use base poritions or the result will be undefined. Wouldn't it be much better and logical if base class portions were guaranteed to have finished before you can write a single line of code in the derived ctor?
Jun 10 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <caa5q6$1844$1 digitaldaemon.com>, Marcin says...

syntax because it lets you catch base class exceptions and then translate
and rethrow. With D you have to be careful about your member objects not to
call any base portions or it will be undefined, and you have to be careful
not to call any methods/functions that will use base poritions or the result
will be undefined. Wouldn't it be much better and logical if base class
portions were guaranteed to have finished before you can write a single line
of code in the derived ctor?
Is this even an issue? Why not just do this: class A {} class B { this() { try { super(); } catch( Exception e ) { throw new DerivedException(); } } } It seems far cleaner than the function try blocks in C++. Sean
Jun 10 2004
next sibling parent reply "Marcin" <sixpackguy earthlink.net> writes:
"Sean Kelly" <sean f4.ca> wrote in message
news:caa790$1adj$1 digitaldaemon.com...
 In article <caa5q6$1844$1 digitaldaemon.com>, Marcin says...

syntax because it lets you catch base class exceptions and then translate
and rethrow. With D you have to be careful about your member objects not
to
call any base portions or it will be undefined, and you have to be
careful
not to call any methods/functions that will use base poritions or the
result
will be undefined. Wouldn't it be much better and logical if base class
portions were guaranteed to have finished before you can write a single
line
of code in the derived ctor?
Is this even an issue? Why not just do this: class A {} class B { this() { try { super(); } catch( Exception e ) { throw new DerivedException(); } } } It seems far cleaner than the function try blocks in C++. Sean
You're missing the big picture. I'm not arguing over which syntax is better. Take a look at this: class base { private: int x; public: void print() { printf("%d\n", x); } this() { x = 8; printf("constructing base\n"); } } class derived : base { this() { print(); super(); } } When you construct a derived object, you're accessing a function (print) in an object that doesn't exist yet - base. If this isn't a recipe for disaster then I don't know what is. This must not ever happen. You should really read this: http://www.gotw.ca/gotw/066.htm .
Jun 10 2004
next sibling parent reply Ant <Ant_member pathlink.com> writes:
In article <caadke$1jmk$1 digitaldaemon.com>, Marcin says...
class derived : base
{
 this()
 {
  print();
  super();
 }
}
This is just trying to use an object that wasn't instanciated. It's the same as: base b; b.print(); (javac for example will issue an error) I don't see a big problem with it. Ant
Jun 10 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <caaed0$1km1$1 digitaldaemon.com>, Ant says...
In article <caadke$1jmk$1 digitaldaemon.com>, Marcin says...
class derived : base
{
 this()
 {
  print();
  super();
 }
}
This is just trying to use an object that wasn't instanciated. It's the same as: base b; b.print(); (javac for example will issue an error) I don't see a big problem with it.
This is slightly different. If a class has a constructor then it's safe to assume that something inside that class requires initialization in order to work properly. Assuming the class only contains standard D class references and such then the worst that will happen is the runtime error you've mentioned. But assume for a moment that the class uses pointers, special memory allocators, and all sorts of other weirdness. In that case the program may crash horribly if a function is called on the class before it is properly initialized. So in general terms: "the result of calling a function on an unconstructed class instance is undefined." Usually this means the app will crash but it can also lead to either subtle bugs or crazy stuff like your hard drive being erased. Slightly different from trying to access a null reference :) Sean
Jun 10 2004
parent Ant <Ant_member pathlink.com> writes:
In article <caaevs$1llt$1 digitaldaemon.com>, Sean Kelly says...
This is slightly different.
[...]
assume for a moment that the class uses pointers, special memory allocators, and
all sorts of other weirdness.
[...] I see, thanks. But I still like this feature, I believe I use it somewhere. Ant
Jun 10 2004
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <caadke$1jmk$1 digitaldaemon.com>, Marcin says...
You're missing the big picture. I'm not arguing over which syntax is better.
Take a look at this:

class base
{
 private:
 int x;
 public:
 void print()
 {
   printf("%d\n", x);
 }
 this()
 {
  x = 8;
  printf("constructing base\n");
 }
}

class derived : base
{
 this()
 {
  print();
  super();
 }
}

When you construct a derived object, you're accessing a function (print) in
an object that doesn't exist yet - base. If this isn't a recipe for disaster
then I don't know what is. This must not ever happen.
Certainly. D is a tad different from C++ in that it doesn't require superclasses to be initialized first. This is fine so long as the user isn't foolish enough to try your example above :) And I should point out this quote from the D docs: "If no call to constructors via this or super appear in a constructor, and the base class has a constructor, a call to super() is inserted at the beginning of the constructor." So assuming the user isn't deliberately foolish then everything will work out fine. There's really no reason to explicitly call a superclass constructor with no parameters unless you want to catch any exceptions it throws. Sean
Jun 10 2004
parent "Marcin" <sixpackguy earthlink.net> writes:
"Sean Kelly" <sean f4.ca> wrote in message
news:caaekv$1l7h$1 digitaldaemon.com...
 In article <caadke$1jmk$1 digitaldaemon.com>, Marcin says...
 Certainly.  D is a tad different from C++ in that it doesn't require
 superclasses to be initialized first.  This is fine so long as the user
isn't
 foolish enough to try your example above :)  And I should point out this
quote
 from the D docs:

 "If no call to constructors via this or super appear in a constructor, and
the
 base class has a constructor, a call to super() is inserted at the
beginning of
 the constructor."

 So assuming the user isn't deliberately foolish then everything will work
out
 fine.  There's really no reason to explicitly call a superclass
constructor with
 no parameters unless you want to catch any exceptions it throws.


 Sean
The example was contrived on purpose so that you can see my point. A lot of times you write code and unknowingly it pretty much does what I just showed you, except now you will be chasing an extremely obscure bug. It's like programming in C, you always want your programs leak and bug free and you never intend to write such programs, but they still happen more often that you'd like to admit to. Having the language help you out is priceless. D doesn't do that and therefore it invites a whole host of bugs. Secondly, why would you not want base class portions initialized first? Would you not start constructing any building with the foundations? All other mature languages guarantee base parts are set up first because it just makes sense from a design standpoint.
Jun 10 2004
prev sibling next sibling parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <caadke$1jmk$1 digitaldaemon.com>, Marcin says...

When you construct a derived object, you're accessing a function (print) in
an object that doesn't exist yet - base. If this isn't a recipe for disaster
then I don't know what is.
So don't do it then. It's not compulsory. Look, you can reason by similar argument that a constructor should not be allowed to call any member function of its own class, because, by definition, if the constructor hasn't returned yet, the object isn't fully constructed yet. Look see:
   class A
   {
       this()
       {
           print();
           printf("finished constructing A\n");
       }

       void print()
       {
           printf("executing A.print()\n");
       }
    }
We can all write dumb code. The trick is, not to. As things stand, D constructors are nice and readable, and if you do something dumb you can expect your program to fall over. And that's how it should be. Compile-time error checking is there to catch the kinds of errors which are NOT obvious. C++ gives you a tortured syntax which make it much harder to figure out construction order. In D you can just SEE it. So I'm not convinced that either the Java way or the C++ way is better. I still think that D has got the better balance between readability and safety, without compromising what you can do. Arcane Jill
Jun 10 2004
prev sibling next sibling parent Jan-Eric Duden <jeduden whisset.com> writes:
Marcin wrote:
 "Sean Kelly" <sean f4.ca> wrote in message
 news:caa790$1adj$1 digitaldaemon.com...
 
In article <caa5q6$1844$1 digitaldaemon.com>, Marcin says...


syntax because it lets you catch base class exceptions and then translate
and rethrow. With D you have to be careful about your member objects not
to
call any base portions or it will be undefined, and you have to be
careful
not to call any methods/functions that will use base poritions or the
result
will be undefined. Wouldn't it be much better and logical if base class
portions were guaranteed to have finished before you can write a single
line
of code in the derived ctor?
Is this even an issue? Why not just do this: class A {} class B { this() { try { super(); } catch( Exception e ) { throw new DerivedException(); } } } It seems far cleaner than the function try blocks in C++. Sean
You're missing the big picture. I'm not arguing over which syntax is better. Take a look at this: class base { private: int x; public: void print() { printf("%d\n", x); } this() { x = 8; printf("constructing base\n"); } } class derived : base { this() { print(); super(); } } When you construct a derived object, you're accessing a function (print) in an object that doesn't exist yet - base. If this isn't a recipe for disaster then I don't know what is. This must not ever happen. You should really read this: http://www.gotw.ca/gotw/066.htm .
Exactly that type of error can also happen in just normal classes as well. class A { int b; this() { print(); b=1; } void print() { assert(b==1); } } Thus I don't see a problem, with the way D does it with super and derived classes. It actually allows the programmer to do stuff without writing create/inits methods as they are needed in C++. For me this issue is really a no brainer.
Jun 10 2004
prev sibling parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Marcin" <sixpackguy earthlink.net> wrote in message
news:caadke$1jmk$1 digitaldaemon.com...
 "Sean Kelly" <sean f4.ca> wrote in message
 news:caa790$1adj$1 digitaldaemon.com...
 In article <caa5q6$1844$1 digitaldaemon.com>, Marcin says...

syntax because it lets you catch base class exceptions and then translate
and rethrow. With D you have to be careful about your member objects not
to
call any base portions or it will be undefined, and you have to be
careful
not to call any methods/functions that will use base poritions or the
result
will be undefined. Wouldn't it be much better and logical if base class
portions were guaranteed to have finished before you can write a single
line
of code in the derived ctor?
Is this even an issue? Why not just do this: class A {} class B { this() { try { super(); } catch( Exception e ) { throw new DerivedException(); } } } It seems far cleaner than the function try blocks in C++. Sean
You're missing the big picture. I'm not arguing over which syntax is better. Take a look at this: class base { private: int x; public: void print() { printf("%d\n", x); } this() { x = 8; printf("constructing base\n"); } } class derived : base { this() { print(); super(); } }
I agree with what you're saying (and I bet Sean does also).
Jun 10 2004
prev sibling parent reply "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Sean Kelly" <sean f4.ca> wrote in message
news:caa790$1adj$1 digitaldaemon.com...
 In article <caa5q6$1844$1 digitaldaemon.com>, Marcin says...

syntax because it lets you catch base class exceptions and then translate
and rethrow. With D you have to be careful about your member objects not to
call any base portions or it will be undefined, and you have to be careful
not to call any methods/functions that will use base poritions or the result
will be undefined. Wouldn't it be much better and logical if base class
portions were guaranteed to have finished before you can write a single line
of code in the derived ctor?
Is this even an issue? Why not just do this: class A {} class B { this() { try { super(); } catch( Exception e ) { throw new DerivedException(); } } } It seems far cleaner than the function try blocks in C++.
It should have the same requirement as in C++, that one cannot leave the ctor body (once an x has been caught) via a return statement, but should require the coder to write a throw expression (even if it's only "throw;") One thing I find important a lot in C++ is the ability to be able to write and call static methods in the initialiser list. I'd like to be able to do the same prior to calling super(), and I imagine that I probably can at the moment in D. Anyone know?
Jun 10 2004
parent Sean Kelly <sean f4.ca> writes:
In article <caan2q$22ap$1 digitaldaemon.com>, Matthew says...
It should have the same requirement as in C++, that one cannot leave the ctor
body (once an x has been caught) via a return statement, but should require the
coder to write a throw expression (even if it's only "throw;")
I agree completely.
One thing I find important a lot in C++ is the ability to be able to write and
call static methods in the initialiser list. I'd like to be able to do the same
prior to calling super(), and I imagine that I probably can at the moment in D.
Anyone know?
I would be shocked if this weren't allowed. Some pertinent bits from the spec just to back up my guess: "All member initializations must be determinable by the compiler at compile time, hence there is no order-of-evaluation dependency for member initializations, and it is not possible to read a value that has not been initialized." "static this() is called by the startup code before main() is called. . . A current weakness of the static constructors is that the order in which they are called is not defined." So it looks like dependent singleton types may still have some issues but otherwise all should work as expected. Sean
Jun 10 2004
prev sibling parent reply Trejkaz Xaoza <trejkaz xaoza.net> writes:
In article <ca8hdu$1jnl$1 digitaldaemon.com>, Marcin says...
the call to super can be placed anywhere inside the constructor. That means
you get to play around with the derived object before the base parts are set
up, which can cause hell if contained member objects try to use the base
portions. Why isn't this dealt with like in Java, where the call to super
has to be the first call in the constructor, otherwise the default base ctor
is called.
Probably because in Java, you end up with problems like not being able to do this (translated to D for the sake of the group): class NamedObject { private char[] name; this(char[] name) { this.name = name; } } class NonNullNamedObject : NamedObject { this(char[] name) { if (name is null) { //...throw some exception... } super(name); } } The workaround in this case is obviously to call super(name) first and then to check the value, but wouldn't it make more sense to check the value first before creating a useless object? I figure statements before the super() should be permitted, but perhaps D could just prohibit non-static methods being called. TX
Jun 10 2004
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <cabl7p$cm3$1 digitaldaemon.com>, Trejkaz Xaoza says...
In article <ca8hdu$1jnl$1 digitaldaemon.com>, Marcin says...
the call to super can be placed anywhere inside the constructor. That means
you get to play around with the derived object before the base parts are set
up, which can cause hell if contained member objects try to use the base
portions. Why isn't this dealt with like in Java, where the call to super
has to be the first call in the constructor, otherwise the default base ctor
is called.
Probably because in Java, you end up with problems like not being able to do this (translated to D for the sake of the group): class NamedObject { private char[] name; this(char[] name) { this.name = name; } } class NonNullNamedObject : NamedObject { this(char[] name) { if (name is null) { //...throw some exception... } super(name); } } The workaround in this case is obviously to call super(name) first and then to check the value, but wouldn't it make more sense to check the value first before creating a useless object?
I would solve your example by having preconditions for super.this(), but I think that's a flaw in the example rather than the idea itself. Suppose a derived class wants to choose which base class constructor to call depending on calculated data? class Derived : Super { ...this( int x ) ...{ .......switch( calculateSomethingWith( x ) ) .......{ .......case 1: super(); break; .......case 2: super( someValue ); break; .......} ...} } In this case the superclass constructor is neither the first call in the derived constructor nor even the first function/method called in the derived constructor, yet the approach seems perfectly legitimate provided the programmer is careful. Sean
Jun 11 2004
parent reply Trejkaz Xaoza <trejkaz xaoza.net> writes:
In article <cacsum$23rb$1 digitaldaemon.com>, Sean Kelly says... 
I would solve
your example by having preconditions for super.this(), but I think
that's a
flaw in the example rather than the idea itself. The entire _point_ of checking the value was to check the precondition. Or is there another way? Excluding the in and out blocks, of course, as those are compiled out for release builds, and it's hardly acceptable for your program to work in all cases except the release build. TX
Jun 11 2004
parent Sean Kelly <sean f4.ca> writes:
In article <cae121$mon$1 digitaldaemon.com>, Trejkaz Xaoza says...
In article <cacsum$23rb$1 digitaldaemon.com>, Sean Kelly says... 
I would solve
your example by having preconditions for super.this(), but I think
that's a
flaw in the example rather than the idea itself. The entire _point_ of checking the value was to check the precondition. Or is there another way? Excluding the in and out blocks, of course, as those are compiled out for release builds, and it's hardly acceptable for your program to work in all cases except the release build.
I had meant the in/out blocks. Verifying pre and postconditions is what DBC is for, and in many cases debug testing is sufficient to insure acceptable program correctness. Critical software, however, may actually ship with the in/out blocks still in place, as program failure is not an option. If you're worried about performance then just make sure that the code in in/out is fairly efficient, or put in Internal and Production version blocks. Sean
Jun 12 2004
prev sibling parent reply Sam McCall <tunah.d tunah.net> writes:
Trejkaz Xaoza wrote:

 Probably because in Java, you end up with problems like not being able to do
 this (translated to D for the sake of the group):
Which is silly, because you can just do this, if the superclass takes at least one parameter: class NonNullNamedObject : NamedObject { this(char[] name) { super(preconstruct(name)); } char[] preconstruct(char[] name) { if(name is null) { // whatever } return name; } } Just having the compiler add an assert that a superconstructor got called exactly once (only in debug mode of course) sounds like enough to me. Sam
Jun 13 2004
parent reply Trejkaz Xaoza <trejkaz xaoza.net> writes:
Naturally.  That code works in D because it doesn't have the restrictions of
Java.  Java would have complained about code like that since you're calling a
method before the object is instantiated.  I guess you can use a static method
to avoid that, but then again that example I gave wasn't the exact case which I
have encountered anyway, more a simulation. :-)

But anyway... since I have a preference towards developing library code as
opposed to application code, I like being over-careful with what gets passed in.
Having my own code go through debug testing a thousand times doesn't help, if
someone linking to my library passes in a bad value (this is also why I don't
like to rely on the in/out blocks, since I can't guarantee the application which
includes my code will actually compile them in!  I guess if the specification
said in/out blocks were always put in, it would be a different issue.)

Anyway, D's going the right way about it.  'Trust the developer' seems to be its
philosophy and allowing the superconstructor call anywhere is adhering to that.

TX

In article <cahg14$2p7c$1 digitaldaemon.com>, Sam McCall says...
Trejkaz Xaoza wrote:

 Probably because in Java, you end up with problems like not being able to do
 this (translated to D for the sake of the group):
Which is silly, because you can just do this, if the superclass takes at least one parameter: class NonNullNamedObject : NamedObject { this(char[] name) { super(preconstruct(name)); } char[] preconstruct(char[] name) { if(name is null) { // whatever } return name; } } Just having the compiler add an assert that a superconstructor got called exactly once (only in debug mode of course) sounds like enough to me. Sam
Jun 13 2004
parent Sam McCall <tunah.d tunah.net> writes:
Trejkaz Xaoza wrote:

 Naturally.  That code works in D because it doesn't have the restrictions of
 Java.  Java would have complained about code like that since you're calling a
 method before the object is instantiated.  I guess you can use a static method
 to avoid that, but then again that example I gave wasn't the exact case which I
 have encountered anyway, more a simulation. :-)
Oops. I thought that code _would_ work with java, having used something like it before as a hack, but it was static before, which is a different kettle of fish. Of course it works in D, in D you can put the code before the super() call ;-)
 Having my own code go through debug testing a thousand times doesn't help, if
 someone linking to my library passes in a bad value (this is also why I don't
 like to rely on the in/out blocks, since I can't guarantee the application
which
 includes my code will actually compile them in!  I guess if the specification
 said in/out blocks were always put in, it would be a different issue.)
 
 Anyway, D's going the right way about it.  'Trust the developer' seems to be
its
 philosophy and allowing the superconstructor call anywhere is adhering to that.
Agreed. Maybe you should trust the developer more too - if they use your library incorrectly, never did a debug build, and released their program complete with resultant bugs then they were a lost cause anyway ;) Sam
Jun 13 2004