From: Vince Morgan on 8 Feb 2006 00:33 Hi, I've recently changed the way I call class members from callbacks, and although I've been very happy with the outcome, an issue raized it's head today. Say I have classes as below. class HoverButton { //members }; class HoverTextButton: public HoverButton { //other members }; The classes are used within an OS that implements callbacks. I place a pointer to the object instance within another object (term loosely applied here) and then extract the pointer and call the appropriate member function from that pointer when the callback fires. Say I create 4 objects; HoverButton BigButton; HoverButton SmallButton; HoverButton FunnyButton; HoverTextButton TextButton; void Callback() { switch (msg) { case MESSAGE: (reinterpret_cast<HoverTextButton*> (GetPtr(hwnd, INDEX)))->On_CallBack(); } etc; } Although most calls are objects of the base type, there are others of the derived type. So as you can see above I have cast the base type objects to the derived type in the callback. (correct cast?). This is working fine, but I have come to realize I hadn't planned for this, and that there may be a more appropriate method that deals with possible future changes such as additional parent classes. More experienced persons would see these issues long before I do, so. Is this a reasonable practice, or is there a better way perhaps? TIA -- Vince Morgan Remove UNSPAM vinhar(a)UNSPAMoptusnet.com.au
From: Heinz Ozwirk on 8 Feb 2006 04:07 "Vince Morgan" <vinhar(a)UNSPAMoptusnet.com.au> schrieb im Newsbeitrag news:43e982c9$0$19770$afc38c87(a)news.optusnet.com.au... > Hi, > I've recently changed the way I call class members from callbacks, > and although I've been very happy with the outcome, an issue raized it's > head today. > Say I have classes as below. > > class HoverButton > { > //members > }; > > class HoverTextButton: public HoverButton > { > //other members > }; > > The classes are used within an OS that implements callbacks. > I place a pointer to the object instance within another object (term > loosely > applied here) and then extract the pointer and call the appropriate member > function from that pointer when the callback fires. > > Say I create 4 objects; > > HoverButton BigButton; > HoverButton SmallButton; > HoverButton FunnyButton; > HoverTextButton TextButton; > > void Callback() > { > switch (msg) > { > case MESSAGE: > (reinterpret_cast<HoverTextButton*> > (GetPtr(hwnd, INDEX)))->On_CallBack(); > } > etc; > } > > Although most calls are objects of the base type, there are others of the > derived type. > So as you can see above I have cast the base type objects to the derived > type in the callback. (correct cast?). > This is working fine, but I have come to realize I hadn't planned for > this, > and that there may be a more appropriate method that deals with possible > future changes such as additional parent classes. > More experienced persons would see these issues long before I do, so. > Is this a reasonable practice, or is there a better way perhaps? The best solution would probably be, not to store a pointer as a long in a window's extra space. To get a clean solution you could derive all relevant classes from a common base and create a map, that assigns a pointer to your base class to a window handle, like std::map<HWND, MyWindowBase*>. If your callback function expects some other (derived) type there, it can do a dynamic_cast<> and detect the error, if the pointer does not point to an object of the expected type. Of cause, the windows registering entries in the map must remove their entries when they are destroyed, but at least you would get a type-safe solution. Actually that is basically what MFC does. If you insist to use a window's extra space, you should take care that you always write pointers to base classes into that space and that on reading you first cast to a base class pointer and then use dynamic_cast to get a pointer to the expected type: void SetObject(HWND window, MyWindowBase* object) { SetWindowLongPtr(window, INDEX, reinterpret_cast<DWORD_PTR>(object)); } MyWindowBase* GetObject(HWND window) { return reinterpret_cast<MyWindowBase*>(GetWindowLongPtr(window, INDEX)); } ... HoverTextButton* button = dynamic_cast<HoverTextButton*>(GetObject(hWnd)); if (button == 0) { // Oops, no object or wrong type. ... } When you use reinterpret_cast to cast a pointer to an unrelated type, take care that you later cast the unrelated type to the same type you had, when you did the first cast. Otherwise you might get problems when multiple inheritance is used, and even with single inheritance only, you have undefined behaviour. HTH Heinz
From: Vince Morgan on 8 Feb 2006 22:23 "Heinz Ozwirk" <hozwirk.SPAM(a)arcor.de> wrote in message news:43e9b4d2$0$342$9b4e6d93(a)newsread2.arcor-online.net... > The best solution would probably be, not to store a pointer as a long in a > window's extra space. To get a clean solution you could derive all relevant > classes from a common base and create a map, that assigns a pointer to your > base class to a window handle, like std::map<HWND, MyWindowBase*>. If your > callback function expects some other (derived) type there, it can do a > dynamic_cast<> and detect the error, if the pointer does not point to an > object of the expected type. Of cause, the windows registering entries in > the map must remove their entries when they are destroyed, but at least you > would get a type-safe solution. Actually that is basically what MFC does. > > If you insist to use a window's extra space, you should take care that you > always write pointers to base classes into that space and that on reading > you first cast to a base class pointer and then use dynamic_cast to get a > pointer to the expected type: > > void SetObject(HWND window, MyWindowBase* object) > { > SetWindowLongPtr(window, INDEX, > reinterpret_cast<DWORD_PTR>(object)); > } > MyWindowBase* GetObject(HWND window) > { > return reinterpret_cast<MyWindowBase*>(GetWindowLongPtr(window, > INDEX)); > } > ... > HoverTextButton* button = > dynamic_cast<HoverTextButton*>(GetObject(hWnd)); > if (button == 0) > { > // Oops, no object or wrong type. > ... > } > > When you use reinterpret_cast to cast a pointer to an unrelated type, take > care that you later cast the unrelated type to the same type you had, when > you did the first cast. Otherwise you might get problems when multiple > inheritance is used, and even with single inheritance only, you have > undefined behaviour. > > HTH > Heinz > > Thank you very much indeed Heinz. I tried to use a template to ensure the correct type was recast but I'm using VC6 and templates aren't well supported apparently, so that solution wasn't possible. Much appreciated. -- Thank you Vince Morgan Remove UNSPAM vinhar(a)UNSPAMoptusnet.com.au
From: Ulrich Eckhardt on 9 Feb 2006 01:14 Vince Morgan wrote: > class HoverButton > { > //members > }; > > class HoverTextButton: public HoverButton > { > //other members > }; [...] > void Callback() > { > switch (msg) > { > case MESSAGE: > (reinterpret_cast<HoverTextButton*> > (GetPtr(hwnd, INDEX)))->On_CallBack(); Strictly spoken, reinterpret_cast here requires that you used reinterpret_cast to store the pointer and that you used the cast on the exact same pointer type you are casting to. The reason is that reinterpret_cast is only guaranteed to yield the original value when cast back to the original type, the intermediate result is nothing you can work with. If you are using the implicit degeneration from HoverTextButton* to void* while setting the pointer, you need to use static_cast to reverse this process, not reinterpret_cast. > Although most calls are objects of the base type, there are others of the > derived type. > So as you can see above I have cast the base type objects to the derived > type in the callback. (correct cast?). > This is working fine, but I have come to realize I hadn't planned for > this, and that there may be a more appropriate method that deals with > possible future changes such as additional parent classes. This should be almost fine. You don't tell us about it, but On_Callback() is probably a virtual function defined in the baseclass. So, there is no need to cast to HoverTextButton, casting to the baseclass HoverButton should be enough, while the virtual dispatch does the rest. Uli -- FAQ: http://ma.rtij.nl/acllc-c++.FAQ.html
From: Vince Morgan on 9 Feb 2006 02:54
"Ulrich Eckhardt" <doomster(a)knuut.de> wrote in message news:450563F48kggU1(a)uni-berlin.de... > Vince Morgan wrote: > > class HoverButton > > { > > //members > > }; > > > > class HoverTextButton: public HoverButton > > { > > //other members > > }; > [...] > > void Callback() > > { > > switch (msg) > > { > > case MESSAGE: > > (reinterpret_cast<HoverTextButton*> > > (GetPtr(hwnd, INDEX)))->On_CallBack(); > > Strictly spoken, reinterpret_cast here requires that you used > reinterpret_cast to store the pointer and that you used the cast on the > exact same pointer type you are casting to. The reason is that > reinterpret_cast is only guaranteed to yield the original value when cast > back to the original type, the intermediate result is nothing you can work > with. Yes, this was how I cast to long. > > > Although most calls are objects of the base type, there are others of the > > derived type. > > So as you can see above I have cast the base type objects to the derived > > type in the callback. (correct cast?). > > This is working fine, but I have come to realize I hadn't planned for > > this, and that there may be a more appropriate method that deals with > > possible future changes such as additional parent classes. > > This should be almost fine. You don't tell us about it, but On_Callback() > is probably a virtual function defined in the baseclass. So, there is no > need to cast to HoverTextButton, casting to the baseclass HoverButton > should be enough, while the virtual dispatch does the rest. The callback is not a member of the class as it's an OS callback that doesn't understand the 'this' pointer, unfortunately. To be perfectly honest I dived into this with very little thought as to the consequences Ulrich. I haven't yet tackled polymorphism in any depth and I think I've been night flying with very little instrument training. The idea came from some code I saw quite a while ago, and although I couldn't remember the details the idea was very appealing as it cleaned up the callback procedures considerabley. I know nothing of virtual dispatch. I could, now that I think of it, implement a callback in the class and call that from the original, and then allow virtual dispatch look after it as you suggested. Time to start reading :) Your help is most gratefully accepted, Thank you very much, -- Vince Morgan Remove UNSPAM vinhar(a)UNSPAMoptusnet.com.au |