www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - super constructors: I can't take it anymore!

reply Serg Kovrov <kovrov no.spam> writes:
Hello, everybody,

As subject says I am really on the edge. This implicitly generated 
parent constructor calls are... Evil.

Here is my near-real class hierarchy:
 class BaseWindow {
   this() {
     writefln("register BaseWindowClass");
     writefln("create");
   }
 }
 class FrameWindow : BaseWindow {
   this(char[] caption) {
     writefln("register FrameWindowClass");
     writefln("create %s", caption);
   }
 }
 class MyFrameWindow : FrameWindow {
 	this(char[] caption) {
 		super(caption);
 	}
 }
When I create instance of MyFrameWindow, I've got:
 register BaseWindowClass
 create
 register FrameWindowClass
 create test
I think I understand why, but I'm completely unhappy with it =( What I want is that the only constructor called were FrameWindow.this(char[]), to have *only* this:
 register FrameWindowClass
 create test
-- serg.
Aug 16 2006
next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Serg Kovrov wrote:
 Hello, everybody,
 
 As subject says I am really on the edge. This implicitly generated 
 parent constructor calls are... Evil.
 
 Here is my near-real class hierarchy:
 class BaseWindow {
   this() {
     writefln("register BaseWindowClass");
     writefln("create");
   }
 }
 class FrameWindow : BaseWindow {
   this(char[] caption) {
     writefln("register FrameWindowClass");
     writefln("create %s", caption);
   }
 }
 class MyFrameWindow : FrameWindow {
     this(char[] caption) {
         super(caption);
     }
 }
When I create instance of MyFrameWindow, I've got:
 register BaseWindowClass
 create
 register FrameWindowClass
 create test
I think I understand why, but I'm completely unhappy with it =( What I want is that the only constructor called were FrameWindow.this(char[]), to have *only* this:
 register FrameWindowClass
 create test
If you want to avoid the default constructor to be called in the base class, I guess you would have to create a overloaded dummy constructor in BaseWindow that you explicitly call in your subclass constructor. Or, you can reconsider your design. One option is to make a register() method that only the BaseWindow constructor calls. In you child classes, you can overload the register() method anyway you wish. /Oskar
Aug 16 2006
parent reply Serg Kovrov <kovrov no.spam> writes:
* Oskar Linde:
 Or, you can reconsider your design. One option is to make a register() 
 method that only the BaseWindow constructor calls. In you child classes, 
 you can overload the register() method anyway you wish.
Yes, I thought about reconsider design to workaround this issue, but I can't find clean solution yet. Let me clear my design. Obvious hierarchy - a base class for common any-window functionality, and subclasses for common window cases (such top frame, button, reac hedit, etc) which may have more narrow but still generic, and finally end-user-classes for particular application. All windows classes must register (once) os class with its window procedure, and create a os window *when instancing*. That is one thing I do not want to change. e.g, when I do `new Button("push me")` I have button object ready for action. no need/worry to init it, it should be inited in constructor. Ok, next. I want unique window procedure for each 'generic' window class. eg BaseWindow FrameWindow, Probably I need to explain more, but do not want to bloat this message. In few words it is for performance reasons. And that approach I'd like to keep too. My original idea was - each of constructors call register() and create() methods with different parameters - window procedure callback and window classname. Your idea to overload this methods in each subclass should work, but IMHO it is bad because of duplicating of code. There is lot of redundant code in register and create methods I can't avoid. I can write a wrappers for them and overload those wrappers, then. But isn't is silly to have such ugly workarounds instead of sane constructors? It is wery hard to follow such not clean code. To help others (and myself) to understand why there is done so, I'll need to comment code, but this still workaround, but not solution. This is main problem with C (atleast how I see it) - when people looking at code, they see lot of workarounds and quirks hiding actual domain logic. I see no way for C-code to avoid redundancy and code bloat. Thats why I looking forward at D.
Aug 16 2006
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 16 Aug 2006 15:15:49 +0300, Serg Kovrov <kovrov no.spam> wrote:
 * Oskar Linde:
 Or, you can reconsider your design. One option is to make a register()  
 method that only the BaseWindow constructor calls. In you child  
 classes, you can overload the register() method anyway you wish.
Yes, I thought about reconsider design to workaround this issue, but I can't find clean solution yet. Let me clear my design. Obvious hierarchy - a base class for common any-window functionality, and subclasses for common window cases (such top frame, button, reac hedit, etc) which may have more narrow but still generic, and finally end-user-classes for particular application. All windows classes must register (once) os class with its window procedure, and create a os window *when instancing*. That is one thing I do not want to change. e.g, when I do `new Button("push me")` I have button object ready for action. no need/worry to init it, it should be inited in constructor. Ok, next. I want unique window procedure for each 'generic' window class. eg BaseWindow FrameWindow, Probably I need to explain more, but do not want to bloat this message. In few words it is for performance reasons. And that approach I'd like to keep too. My original idea was - each of constructors call register() and create() methods with different parameters - window procedure callback and window classname.
How's this? import std.stdio; typedef int function(int i) WNDPROC; class BaseWindow { void register(char[] cname) { writefln("register(",cname,")"); } void create(int function(int) f) { writefln("create(",f(1),")"); } char[] classname() { return "BaseWindow"; } WNDPROC windproc() { return cast(WNDPROC)function int(int i){return i;}; } this() { register(classname()); create(windproc()); } } class FrameWindow : BaseWindow { override char[] classname() { return "FrameWindow"; } override WNDPROC windproc() { return cast(WNDPROC)function int(int i){return i*2;}; } } void main() { FrameWindow b = new FrameWindow(); BaseWindow a = new BaseWindow(); } Regan
Aug 16 2006
parent reply Serg Kovrov <kovrov no.spam> writes:
* Regan Heath:
 import std.stdio;
 
 typedef int function(int i) WNDPROC;
 
 class BaseWindow
 {
     void register(char[] cname) { writefln("register(",cname,")"); }
     void create(int function(int) f) { writefln("create(",f(1),")"); }
     
     char[] classname() { return "BaseWindow"; }
     WNDPROC windproc() { return cast(WNDPROC)function int(int i){return 
 i;}; }
     
     this() { register(classname()); create(windproc()); }
 }
 
 class FrameWindow : BaseWindow
 {
     override char[] classname() { return "FrameWindow"; }
     override WNDPROC windproc() { return cast(WNDPROC)function int(int 
 i){return i*2;}; }
 }
 
 void main()
 {
     FrameWindow b = new FrameWindow();
     BaseWindow a = new BaseWindow();
 }
Thank you, Regan. This is pretty good looking solution. Seems all solutions are about to avoid using constructors in subclasses at all. Sadly, constructors appears to be weak in D. I wish some day it will be changed. -- serg.
Aug 16 2006
parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 16 Aug 2006 16:43:16 +0300, Serg Kovrov <kovrov no.spam> wrote:
 * Regan Heath:
 import std.stdio;
  typedef int function(int i) WNDPROC;
  class BaseWindow
 {
     void register(char[] cname) { writefln("register(",cname,")"); }
     void create(int function(int) f) { writefln("create(",f(1),")"); }
         char[] classname() { return "BaseWindow"; }
     WNDPROC windproc() { return cast(WNDPROC)function int(int i){return  
 i;}; }
         this() { register(classname()); create(windproc()); }
 }
  class FrameWindow : BaseWindow
 {
     override char[] classname() { return "FrameWindow"; }
     override WNDPROC windproc() { return cast(WNDPROC)function int(int  
 i){return i*2;}; }
 }
  void main()
 {
     FrameWindow b = new FrameWindow();
     BaseWindow a = new BaseWindow();
 }
Thank you, Regan. This is pretty good looking solution. Seems all solutions are about to avoid using constructors in subclasses at all.
Or rather you should avoid trying to 'remove' initialisation behaviour from a base class, and instead place optional initialisation in seperate methods which can be overridden. Constructors are not like ordinary methods, they're always inherited and can only be overridden/removed by going to extremes. This is a much better situation than the alternative which allows you to easily 'forget' to initialise you base class, IMO.
 Sadly, constructors appears to be weak in D. I wish some day it will be  
 changed.
Actually, I disagree. I think the D behaviour is safer and more useful than the alternative. Especially given the fact that it can still handle/solve the problem you had, and does it in a more elegant fashion (IMO) than you were initially going to use, i.e. adding the same lines (with slightly different data) to every sub class constructor (something you might have forgotten to do at some stage) Regan
Aug 16 2006
prev sibling next sibling parent Serg Kovrov <kovrov no.spam> writes:
The real reason why I posted my original message, is that I do not 
understand purpose of such constructors behavior. I see no benefits of 
it at all. And can't understand technical limitations (if thats the 
case) too.

-- 
serg.
Aug 16 2006
prev sibling parent reply nobody <nobody mailinator.com> writes:
Serg Kovrov wrote:

 Here is my near-real class hierarchy:
 class BaseWindow {
uint registerID; // inits to 0
   this() {
if(registerID == 0) // nonzero registerID skips this step { registerID = register(...);
     writefln("register BaseWindowClass");
}
     writefln("create");
   }
 }
 class FrameWindow : BaseWindow {
   this(char[] caption) {
if(registerID == 0) // nonzero registerID skips this step { registerID = register(...);
     writefln("register FrameWindowClass");
}
     writefln("create %s", caption);
   }
 }
 class MyFrameWindow : FrameWindow {
     this(char[] caption) {
         super(caption);
     }
 }
 What I want is that the only constructor called were 
 FrameWindow.this(char[]), to have *only* this:
 register FrameWindowClass
 create test
I think caching some value which shows you have already registered should keep any parents from registering again since the child's ctor should always be called first.
Aug 16 2006
parent Serg Kovrov <kovrov no.spam> writes:
* nobody:
 I think caching some value which shows you have already registered 
 should keep any parents from registering again since the child's ctor 
 should always be called first.
Sure! This were just a simple example to illustrate hierarchy and purpose of constructors. -- serg.
Aug 16 2006