www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to use base class & child class as parameter in one function ?

reply Vinod K Chandran <kcvinu82 gmail.com> writes:
Hi all,
I have a windows gui setup like this;

class EventArgs {} \\ Base class for all messages
class MouseEventArgs : EventArgs {	// child class for handling 
mouse messages
     ...
     int x;
     int y;
     this(WPARAM wpm, LPARAM lpm){
	this.x = xFromLparam(lpm);
	this.y = yFromLparam(lpm);
     }
}

alias EvtFuncPtr = void function(EventArgs); // function pointer

struct MsgHandler {
     uint message ;
     HWND handle ;
     bool isActive ;
     EvtFuncPtr fnPtr;
}
// Then in my window class...
void addHandler(uint we, EvtFuncPtr fnp){ // This is error point.
	auto mh = MsgHandler();
	mh.handle = this.mHandle;
	mh.message = we;
	mh.fnPtr = fnp;
	mh.isActive = true;
	this.msgHandlerList ~= mh;  // This is a list<MsgHandler> in 
window class.	
}

// And in the WndProc...
auto thisWin = findWindowClass(hWnd);  // get the window class 
with hWnd.
auto mh = thisWin.findHandler(hWnd, message);    // get the event 
handler for this message & hWnd
if(mh.isActive) { // if there is an event handler fixed,
	switch (message){
	   case 512 : .. case 526 :  // if it's a Mouse messages
		auto ea = new MouseEventArgs(wParam, lParam);
		mh.fnPtr(ea);   // execute that event handler function
		break;
	   default : break;
	}
}

// And this is the window creation site...
auto app = new Application() ;
auto frm = new Window(app) ;
frm.createWindow() ;

frm.addHandler(frm.load, &onLoad);
frm.addHandler(frm.Click, &onClick);

void onLoad(EventArgs e){
	log("form is loaded...");
}
void onClick(MouseEventArgs e){	 // Compiler wants to change the 
MouseEventArgs with EventArgs.
	log("form clicked on x = ", e.x, ", and y = ", e.y);
}

I wrote  EventArgs as the parameter type in function pointer but 
i want to use it's child classes also. But i can't.
May 22 2020
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
if (Child child = cast(Child)parent) {
	assert(child !is null);
}
May 22 2020
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 22 May 2020 at 12:21:25 UTC, rikki cattermole wrote:
 if (Child child = cast(Child)parent) {
 	assert(child !is null);
 }
Actually, problem occurs in addHandler function. It expects an argument of type "EventArgs", not MouseEventArgs.
May 22 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/22/20 9:10 AM, Vinod K Chandran wrote:
 On Friday, 22 May 2020 at 12:21:25 UTC, rikki cattermole wrote:
 if (Child child = cast(Child)parent) {
     assert(child !is null);
 }
Actually, problem occurs in addHandler function. It expects an argument of type "EventArgs", not MouseEventArgs.
Yes, because what if you did this with your function: fnp(new EventArgs(...)); It would be called with the type being implicitly cast to the child type without that being true. What Rikki was recommending is that you write your handler like this: void onClick(EventArgs e){ if(auto me = cast(MouseEventArgs)e) { log("form clicked on x = ", me.x, ", and y = ", me.y); } } Actually, if you are certain it's a programming error for onClick to be called with a different type of event args, I'd do: void onClick(EventArgs e){ auto me = cast(MouseEventArgs)e; assert(me !is null, "Error, onClick called with invalid event type"); log("form clicked on x = ", me.x, ", and y = ", me.y); }
May 22 2020
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 22 May 2020 at 16:12:12 UTC, Steven Schveighoffer 
wrote:
 On 5/22/20 9:10 AM, Vinod K Chandran wrote:
 On Friday, 22 May 2020 at 12:21:25 UTC, rikki cattermole wrote:
 if (Child child = cast(Child)parent) {
     assert(child !is null);
 }
Actually, problem occurs in addHandler function. It expects an argument of type "EventArgs", not MouseEventArgs.
Yes, because what if you did this with your function: fnp(new EventArgs(...)); It would be called with the type being implicitly cast to the child type without that being true. What Rikki was recommending is that you write your handler like this: void onClick(EventArgs e){ if(auto me = cast(MouseEventArgs)e) { log("form clicked on x = ", me.x, ", and y = ", me.y); } } Actually, if you are certain it's a programming error for onClick to be called with a different type of event args, I'd do: void onClick(EventArgs e){ auto me = cast(MouseEventArgs)e; assert(me !is null, "Error, onClick called with invalid event type"); log("form clicked on x = ", me.x, ", and y = ", me.y); }
Thanks for the answer. I understand that, in D, derived class and base class are not the same as in vb.net or any other language. (Please correct me if i am wrong). In vb.net, assume that i have a class setup like this-- Public Class Base Public Property SampleInt As Integer End Class Public Class Child : Inherits Base Public Property SampleDouble As Double End Class //Assume that i have a list of Base class like this-- Dim sampleList As New List(Of Base) // Now, i can use this list like this-- sampleList.Add(New Child(10.5)) Is this possible in D without casting ?
May 22 2020
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 22 May 2020 at 20:04:24 UTC, Vinod K Chandran wrote:
 sampleList.Add(New Child(10.5)) Is this possible in D without 
 casting ?
Direct translation of this code works just fine in D.
May 22 2020
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 22 May 2020 at 20:06:20 UTC, Adam D. Ruppe wrote:
 On Friday, 22 May 2020 at 20:04:24 UTC, Vinod K Chandran wrote:
 sampleList.Add(New Child(10.5)) Is this possible in D without 
 casting ?
Direct translation of this code works just fine in D.
Yeah, my bad. I just checked in D. But think inherited type difference is a problem in function pointer's parameters only. alias EvtFuncPtr = void function(EventArgs); Now, this EvtFuncPtr won't allow any derived classes of EventArgs as parameter. That's the problem i am facing. What about a template ? alias EvtFuncPtr = void function(T)(T = EventArgs); This is not compiled.
May 22 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, May 22, 2020 at 08:55:45PM +0000, Vinod K Chandran via
Digitalmars-d-learn wrote:
 On Friday, 22 May 2020 at 20:06:20 UTC, Adam D. Ruppe wrote:
 On Friday, 22 May 2020 at 20:04:24 UTC, Vinod K Chandran wrote:
 sampleList.Add(New Child(10.5)) Is this possible in D without
 casting ?
Direct translation of this code works just fine in D.
Yeah, my bad. I just checked in D. But think inherited type difference is a problem in function pointer's parameters only. alias EvtFuncPtr = void function(EventArgs); Now, this EvtFuncPtr won't allow any derived classes of EventArgs as parameter. That's the problem i am facing.
[...] So you're basically saying: void function(DerivedClass) cannot implicitly convert to: void function(BaseClass) right? This is as it should be: class Base { ... } class Derived : Base { ... } class Another : Base { ... } void baseFunc(Base) { ... } void derivedFunc(Derived) { ... } void function(Base) funcPtr; funcPtr = baseFunc; // OK funcPtr = derivedFunc; // Not allowed Why is it bad to assign derivedFunc to funcPtr? Consider this: Base obj = new Another; funcPtr = baseFunc; funcPtr(obj); // OK, because Another is a subtype of Base funcPtr = derivedFunc; // suppose this was allowed funcPtr(obj); // Uh oh, derivedFunc receives an argument of // type Another which is not a subtype of // Derived So, it's not permissible to allow assigning derivedFunc to funcPtr without a cast. T -- Indifference will certainly be the downfall of mankind, but who cares? -- Miquel van Smoorenburg
May 22 2020
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/22/20 4:04 PM, Vinod K Chandran wrote:
 On Friday, 22 May 2020 at 16:12:12 UTC, Steven Schveighoffer wrote:
 On 5/22/20 9:10 AM, Vinod K Chandran wrote:
 On Friday, 22 May 2020 at 12:21:25 UTC, rikki cattermole wrote:
 if (Child child = cast(Child)parent) {
     assert(child !is null);
 }
Actually, problem occurs in addHandler function. It expects an argument of type "EventArgs", not MouseEventArgs.
Yes, because what if you did this with your function: fnp(new EventArgs(...)); It would be called with the type being implicitly cast to the child type without that being true. What Rikki was recommending is that you write your handler like this: void onClick(EventArgs e){     if(auto me = cast(MouseEventArgs)e) {     log("form clicked on x = ", me.x, ", and y = ", me.y);     } } Actually, if you are certain it's a programming error for onClick to be called with a different type of event args, I'd do: void onClick(EventArgs e){     auto me = cast(MouseEventArgs)e;     assert(me !is null, "Error, onClick called with invalid event type");     log("form clicked on x = ", me.x, ", and y = ", me.y); }
Thanks for the answer. I understand that, in D, derived class and base class are not the same as in vb.net or any other language. (Please correct me if i am wrong). In vb.net, assume that i have a class setup like this-- Public Class Base     Public Property SampleInt As Integer End Class Public Class Child : Inherits Base     Public Property SampleDouble As Double End Class //Assume that i have a list of Base class like this-- Dim sampleList As New List(Of Base) // Now, i can use this list like this-- sampleList.Add(New Child(10.5)) Is this possible in D without casting ?
Yes. What you cannot do is this (which I hope doesn't compile in VB.net, but I wouldn't be surprised): Dim sampleList As New List(Of Child) sampleList.Add(New Base(10)) Which is the equivalent of what you were requesting. -Steve
May 22 2020
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 22 May 2020 at 20:51:20 UTC, Steven Schveighoffer 
wrote:
 On 5/22/20 4:04 PM, Vinod K Chandran wrote:
 [...]
Yes. What you cannot do is this (which I hope doesn't compile in VB.net, but I wouldn't be surprised): Dim sampleList As New List(Of Child) sampleList.Add(New Base(10)) Which is the equivalent of what you were requesting. -Steve
Nope-- List(Of Base) will contain an instance of a Child. So in the same manner, i want void function(Base) = fnPtr wiil work with void function(Child)
May 22 2020
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/22/20 5:39 PM, Vinod K Chandran wrote:
 On Friday, 22 May 2020 at 20:51:20 UTC, Steven Schveighoffer wrote:
 On 5/22/20 4:04 PM, Vinod K Chandran wrote:
 [...]
Yes. What you cannot do is this (which I hope doesn't compile in VB.net, but I wouldn't be surprised): Dim sampleList As New List(Of Child) sampleList.Add(New Base(10)) Which is the equivalent of what you were requesting.
Nope-- List(Of Base) will contain an instance of a Child. So in the same manner, i want void function(Base) = fnPtr wiil work with void function(Child)
That is the opposite of what you are thinking. A function pointer has to be valid based on its parameter types. Covariant functions are allowed. This is OK: void function(Child) fptr; void foo(Base) {} fptr = &foo; // OK! it's fine to call fptr with a Child, because it is a Base as well void function(Base) fptr2; void foo2(Child) {} fptr2 = &foo2; // Error! if you called fptr2 with a Base that is NOT a Child, bad things will happen. This is more clear if you actually try calling them: fptr2(new Base); // the compiler should allow this foo2(new Base); // but would not allow this So why should fptr2 be allowed to point at foo2? -Steve
May 22 2020
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 22 May 2020 at 22:40:50 UTC, Steven Schveighoffer 
wrote:
 On 5/22/20 5:39 PM, Vinod K Chandran wrote:
 [...]
That is the opposite of what you are thinking. A function pointer has to be valid based on its parameter types. Covariant functions are allowed. This is OK: void function(Child) fptr; void foo(Base) {} fptr = &foo; // OK! it's fine to call fptr with a Child, because it is a Base as well void function(Base) fptr2; void foo2(Child) {} fptr2 = &foo2; // Error! if you called fptr2 with a Base that is NOT a Child, bad things will happen. This is more clear if you actually try calling them: fptr2(new Base); // the compiler should allow this foo2(new Base); // but would not allow this So why should fptr2 be allowed to point at foo2? -Steve
Thank you for the guidance. I got the point. :)
May 23 2020
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, May 22, 2020 at 09:39:16PM +0000, Vinod K Chandran via
Digitalmars-d-learn wrote:
[...]
 So in the same manner, i want
 void function(Base) = fnPtr wiil work with
 void function(Child)
You cannot, because that's type unsafe: class Base {} class Derived : Base { void derivedFunc() {} } class Another : Base {} void derivedFunc(Derived d) { d.derivedFunc(); } void function(Base) funPtr; funPtr = derivedFunc; // suppose this was allowed Base obj = new Another; funPtr(obj); // crash: obj does not have derivedFunc() T -- Век живи - век учись. А дураком помрёшь.
May 22 2020
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 22 May 2020 at 22:44:17 UTC, H. S. Teoh wrote:
 On Fri, May 22, 2020 at 09:39:16PM +0000, Vinod K Chandran via 
 Digitalmars-d-learn wrote: [...]
 So in the same manner, i want
 void function(Base) = fnPtr wiil work with
 void function(Child)
You cannot, because that's type unsafe: class Base {} class Derived : Base { void derivedFunc() {} } class Another : Base {} void derivedFunc(Derived d) { d.derivedFunc(); } void function(Base) funPtr; funPtr = derivedFunc; // suppose this was allowed Base obj = new Another; funPtr(obj); // crash: obj does not have derivedFunc() T
Yeah, I understand that. And i just changed my code. Thanks for the guidance. :)
May 23 2020