From: soloway@midniteoil.Eng.Sun.COM (Mark Soloway)
Newsgroups: comp.lang.c++,comp.windows.open-look,alt.toolkits.xview
Subject: C++ and callback problems (VERY LONG)
Date: 10 Sep 1992 22:28:52 GMT
Organization: Sun Microsystems, Inc.
Distribution: world
Reply-To: soloway@midniteoil.Eng.Sun.COM
NNTP-Posting-Host: midniteoil

From <name kept anonymous to protect the innocent>:

> This letter concerns the your article in alt.toolkits.xview.  I have
> tried to implement your method of encapsulating callbacks thru a
> static wrapper function and storing the 'this' pointer as xv_data.
> Two problems have arisen: the first was due to the fact that i ignored
> your short and somewhat cryptic note:
> ->        // If a notify handler has been specified, via
> ->        // "void Button::setNotifyHandler (void (*)(Button *, Event *))",
> ->        // then call it.
> ->            if (object->notifyHandler)
> ->              (*object->notifyHandler)(object, event);
> 
> I called directly object->notifyhandler(), but then calls in derived classes
> were made to the ancestor callback, as the value of
> object->notifyhandler() is known at compile-time.  I then realized what
> u did, and added a private variable which points to the callback, thus
> sort of explicitly writing the late-binding code.  What turns out
> (using g++ 2.1), is that the code is unusable, because the
> private handler receives a hidden this pointer as its first parameter,
> but no such parameter is pushed to the stack in:
> ->        (*object->notifyHandler)(event);
> thus, stepping into a call of the form
>         (*object->notifyhandler(menu, menuitem)
> where menu=x and menuitem=y, and notifyhandler pointing at notifyhandlerproc
> yields
>         notifyhandlerproc(this=x, menu=y, menuitem=junk)
> 
> i could explicitly define the variable notifyhandlerproc to be a
> pointer to a function which receives (obj *, Menu, Menu_item), but how
> portable is this?  and since you stated in your article that this
> method was used in uit, can you please let me know what the portable,
> arm-compliant, overall stable workaround is?

After re-reading what I wrote, I can see why my original article may have
been confusing.  I left out a class declaration that would have made example
#1 more clear and I made a mistake in the "notifyProc" part of example
#2 that could make the example very confusing.  In order to correct these
mistakes, I've included portions of the original posting along with commentary
that will hopefully clear up the confusion.

-> This article describes a method I use to provide a convenient C++
-> callback interface for XView and provide the ability to encapsulate
-> callback behavior in a subclass.  The method described is used in
-> UIT V2.

[text deleted]

EXAMPLE 1:

-> A basic notifyProc member function that calls a user spcified callback
-> handler might look like this:
-> 
-> 	void Button::notifyProc (Panel_item item, Event *event)
-> 	{
-> 	  Button *object = (Button *)xv_get(item,
-> 	                                    XV_KEY_DATA,
-> 					    THIS_POINTER_KEY);
-> 
-> 	  // If a notify handler has been specified, via
-> 	  // "void Button::setNotifyHandler (void (*)(Button *, Event *))",
-> 	  // then call it.
->           if (object->notifyHandler)
->             (*object->notifyHandler)(object, event);
-> 	}

The "notifyHandler" member used here is actually a data member.  The class
might look something like this:

	class Button {
	public:
	  ...
	  void setNotifyHandler (void (*handler)(Button *, Event *))
		{
		  notifyHandler = handler;
		}

	protected:
	  Panel_item xviewButton;

	private:
	  static void   notifyProc(Panel_item, Event *);
	  void        (*notifyHandler)(Button *, Event *);
	}

The notifyHandler data member is just a holder for a user specified
callback function.  The user specified callback function should be
a regular external function (not a member function of another or this
class).

The function that XView actually calls for the PANEL_NOTIFY_PROC is
"notifyProc".  "notifyProc" does not have an implicit "this" parameter
because it is a static member function.  "notifyProc" needs to be
declared public for use with g++ because it is illegal to use the
address of a private or protected member function (static or non-static)
in g++.  This is not the case for AT&T or ANSI C++.

-> This provides a "convenient" callback interface, but it does not provide
-> for encapsulation of callback behavior in the class.  To provide the ability
-> to encapsulate the behavior, you need to provde a virtual function that
-> will be overridden in subclasses that are encapsulating the callback
-> behavior:

EXAMPLE 2:

-> 	class Button {
-> 	public:
-> 	  ...
-> 
-> 	protected:
-> 	  // Make xviewButton protected so that derived classes can get hold
-> 	  // of the XView button object when inside of their personalized
-> 	  // handlers.
->           Panel_item xviewButton;
-> 
-> 	private:
-> 	  static  void notifyProc(Panel_item, Event *);
-> 	  virtual void notifyHandler(Event *);
-> 	}
-> 
-> 	void Button::notifyProc (Panel_item item, Event *event)
-> 	{
-> 	  Button *object = (Button *)xv_get(item,
-> 	                                    XV_KEY_DATA,
-> 					    THIS_POINTER_KEY);
-> 
-> 	  (*object->notifyHandler)(event);
-> 	}

The "notifyHandler" in this example is an actual member function of the
class.  It should be called as such.  Since I made a mistake in this
example, this is where most of the confusion probably is.  The above
"notifyProc" should be re-written as follows:

	void Button::notifyProc (Panel_item item, Event *event)
 	{
 	  Button *object = (Button *)xv_get(item,
 	                                    XV_KEY_DATA,
 					    THIS_POINTER_KEY);
 
 	  object->notifyHandler(event);
 	}

-> You can then define the behavior of notifyHandler in a class derived
-> from Button:

[text deleted]

 					- Mark
 __________________________________________
 \_Mark Soloway (mark.soloway@Eng.Sun.COM)_\
 /_Distributed Systems Services (ToolTalk)_/
 \__SunSoft (A Sun Microsystems Company)___\
 


Newsgroups: comp.lang.c++