From: Vince Morgan on
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
"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
"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
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
"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