From: tcox@ssrl01.slac.stanford.edu (Tony Cox)
Newsgroups: comp.lang.c++
Subject: Re: Member function pointers, (Base::*) -> (Derived::*)
Date: 23 Jul 92 21:41:07 GMT
Organization: SSRL, Stanford, CA


	{long one this - approx. 450 lines}

In article <1992Jul17.164853.29520@ucc.su.oz>, maxtal@extro.ucc.su.OZ.AU writes:
> 
> In article <1992Jul16.151946.1@ssrl01.slac.stanford.edu> tcox@ssrl01.slac.stanford.edu writes:
>>
>>I have formed the belief that by careful coding techniques, one can avoid
>>the use of explicit casts on C++ code, except where interface into other
>>language modules is concerned (in this case, the Motif/Xt toolkit).
> 
> 	Good.
>>
>>However, there is one stubbon problem which I cannot for the life of me see
>>an elegant solution for, so I am posting this in the hope that someone can
>>enlighten me.
>>
>>Now, as Motif programmers will know, PushButton callbacks, and indeed
>>pretty much all callbacks within Motif, end up calling a "C" routine with
>>the following declaration:-
>>
>>		actionRoutine( Widget, XtPointer clientData, XtPointer)
>>
>>The first arg is the widget wherefrom the callback originated, the second
>>is any (equiv to void*) user provided pointer, and the third is the
>>address of an Xevent structure which further defines the operation.
> 
> 
> 	Unless I misunderstand :
> 
> Provide ONE only callback routine in your entire progam.
> 
> 	actionRoutine(Widget W, void *object, XtPointer X)
> 	{
> 		WidgetObject* w=object;
> 		w->execute(W,X);
> 	}
> 
> 
> Where:
> 	class WidgetObject { 
> 	public: 
> 		virtual execute(Widget,XtPointer)=0;
> 	};
> 
> 
> To define the behaviour, derive a class from WidgetObject.
> You must supply the object somehow to Motiff to
> call you back with.
> 
> 
> [PS: I'm not a Motif or even X user, I use Windows. The above
> type of solution is what I do there. It might need tuning.
> But the key is ONE only callback (of each type in Windows).]
> 
> 
> -- 
> ;----------------------------------------------------------------------
>         JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
> 	Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
> ;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------

Max,
	I guess you dont fully understand my problem, so I am glad to
elaborate. I do this because the results of my discussions with several
folks on this newsgroup have brought out some interesting (to me non-obvious)
facts which have general validity. There are a number of `gotcha's for
the unwary.

	Your solution works fine if you only want your callbacks to vector
into a single (non-static) member function. The problem I have, is that I
would like to vector back into SEVERAL DIFFERENT (non-static) member functions,
and would like to avoid the confusion of adding (static) member functions for
each one to do the revectoring.

	My first approach (and to me what appeared to be the obvious one)
was to typedef the following:-

		typedef	void (BASE::*memberFunction)( BASE*);

and then hide the revectoring for derived classes within the base class
with a protected member function like this:-

	class BASE {
		...
	    protected:
		void registerCallback( memberFunction, BASE*);
		...
	};

The idea being that one could then do something like this:-

	Derived::someMethod()
	{
		....
		registerCallback( myRevectoredMethod, this);
		....
	}

	Derived::myRevectoredMethod()
	{
		....
	}


The PROBLEM is that myRevectoredMethod is of class (Derived::*)(Derived*);
One has no problem with the cast of Derived* -> Base* of course - the problem
is with (Derived::*) -> (Base::*).

Although this works (GCC V2.0.0, VAX/VMS V5.4), it is NOT TYPE SAFE, and
you can run into problems with revectoring into virtual routines, and probably
with some compiler implementations, elsewhere also. The ARM has some
implementation notes concerning pointers to member functions which I wont
go into here.

Doug Young, and others, referred me to a typesafe method of doing this in
his CALLBACK class. It is elegant, but not particularly straightforward.

I have implemented my own CALLBACK class hierarchy along the same lines (which
I append to this message to avoid a potential flood of requests), as I need to
be able to revector with arbitrary parameters. The 'Trick' is to define various 
general functions in a CALLBACK base class, and then to define SPECIFIC derived
classes which know about the particular class you are trying to revector into.
This is done in a macro within the (revectoring-into) class.

Finally, by overloading a global CALLBACK function with various parameters,
I can get to what I want to do, which is to provide a generic callback 
mechanism which is both type safe and easy to understand in the code.

For example (now I'm getting Motif specific, and refer interested readers
back to my original post), I can now do this:-

file ScaleInterface.cc
	
		#include "ScaleInterface.h"
		#include "Callback.h"

		....

		defineCallback( ScaleInterface);

		...


		ScaleInterface::someMethod()
		{
		...
			PushButtonWidget* pb = new PushButtonWidget("Pushme");
			pb->addActivateCallback( CALLBACK( this, iAmPushed));
		...
		}

		ScaleInterface::iAmPushed( PushButtonWidget* pb)
		{
		}	


For those interested, here is my callback class, modelled on Doug's and
extended as I mentioned. It is a prototype, just developed, so you will
most likely need to extend and correct it.

Note in particular the LONG macro 'defineCallback'. I point out that this
can be a real bugger to debug - the macro expansion typically is 3000+
characters, and tends to overflow the VMS editors EDT, EVE and LSE.

My CALLBACK class, and the derived classes, revector four types of 
Motif callbacks - simple Widget callback, Event handlers, Work Procedures
and Timeout events. The CALLBACK global function generates the appropriate
derived class depending on the presence of AppWidget, XEvent, XtTimeoutId,
XmAnyCallbackStruct as parameters in the call. At present, only the simple
Widget Callback derived classes allow for additional parameters to be
passed, but it should be obvious from the code how to extend this to
permit additional parameters to be added.

------- Callback.h -------------
// Not the Doug Young callback. This by AD Cox, SSRL, 15-Jul-1992

#ifndef	CALLBACK_H
#define	CALLBACK_H

#include    "VMSObject.h"
#include    <Xm/Xm.h>
#include    <X11/Xlib.h>

extern "C" {
void printf(...);
};

class UIComponent;
class AppWidget;

class Callback	:   public virtual VMSObject {
    protected:
	Callback();
	Boolean		_isInUse;
};

// Simple widget callbacks
class CallbackW	:   public Callback {
    public:
	void	addCallback( AppWidget*, char*);
    protected:
	virtual	void	dispatch() = 0;
	static	void	_callbackCleanup( Widget, XtPointer, XtPointer);
	XmAnyCallbackStruct*	_xmAnyCallbackStruct;
	AppWidget*	_forWidget;
    private:
	static	void	_callbackWvector( Widget, XtPointer, XtPointer);
};

// Events registered with XtAddEventHandler
class CallbackEH :  public Callback {
    public:
	void addEventHandler( AppWidget*, EventMask, Boolean);
    protected:
	static	void	_callbackCleanup( Widget, XtPointer, XtPointer);
	virtual void	dispatch() = 0;
	XEvent*		_xevent;
	AppWidget*	_forWidget;
    private:
	static	void	_callbackEHvector( Widget, XtPointer, XEvent*,
						Boolean*);
};

// Work procedures.
class CallbackWP : public Callback {
    public:
	void addWorkProc();
    protected:
	virtual Boolean	dispatch() = 0;
    private:
	static	Boolean	_callbackWPvector( caddr_t);
};

// Timeout events
class CallbackT : public Callback {
    public:
	XtIntervalId	addTimeOut( unsigned long);
    protected:
	virtual void dispatch() = 0;
	XtIntervalId	_xtIntervalId;
    private:
	static	void	_callbackTvector( caddr_t, XtIntervalId*);
};

#define	defineCallback(CLASS)\
class CLASS;\
class CLASS##CallbackW : public CallbackW {\
    public:\
	CLASS##CallbackW( CLASS* obj) {_obj = obj;}\
    protected:\
	virtual void	dispatch() = 0;\
	CLASS* _obj;};\
class CLASS##CallbackEH : public CallbackEH {\
    public:\
	CLASS##CallbackEH( CLASS* obj) {_obj = obj;}\
    protected:\
	virtual void	dispatch() = 0;\
	CLASS* _obj;};\
class CLASS##CallbackWP : public CallbackWP {\
    public:\
	CLASS##CallbackWP( CLASS* obj) {_obj = obj;}\
    protected:\
	virtual Boolean	dispatch() = 0;\
	CLASS* _obj;};\
class CLASS##CallbackT : public CallbackT {\
    public:\
	CLASS##CallbackT( CLASS* obj) {_obj = obj;}\
    protected:\
	virtual void	dispatch() = 0;\
	CLASS* _obj;};\
typedef	void (CLASS::*__noParamWdispatch)( AppWidget*);\
class CLASS##NoParamCallbackW : public CLASS##CallbackW {\
    public:\
	CLASS##NoParamCallbackW( CLASS* obj, __noParamWdispatch npd) :\
		CLASS##CallbackW( obj)  { _npd = npd;}\
    protected:\
	virtual void	dispatch() {(_obj->*_npd)( _forWidget);}\
    private:\
	__noParamWdispatch	_npd;};\
typedef void (CLASS::*__floatWdispatch)( AppWidget*, float);\
class CLASS##FloatCallbackW : public CLASS##CallbackW {\
    public:\
	CLASS##FloatCallbackW( CLASS* obj, __floatWdispatch fd, float f) :\
		CLASS##CallbackW( obj)  { _fd = fd; _f = f;}\
    protected:\
	virtual void	dispatch() {(_obj->*_fd)( _forWidget, _f);}\
    private:\
	__floatWdispatch	_fd;	float _f;};\
typedef	void (CLASS::*__noParamEHdispatch)( AppWidget*, XEvent*);\
class CLASS##NoParamCallbackEH : public CLASS##CallbackEH {\
    public:\
	CLASS##NoParamCallbackEH( CLASS* obj, __noParamEHdispatch ed) :\
		CLASS##CallbackEH( obj) {_ed = ed;}\
    protected:\
	virtual void	dispatch() {(_obj->*_ed)( _forWidget, _xevent);}\
    private:\
	__noParamEHdispatch	_ed;};\
typedef	Boolean	(CLASS::*__noParamWPdispatch)();\
class CLASS##NoParamCallbackWP : public CLASS##CallbackWP {\
    public:\
	CLASS##NoParamCallbackWP( CLASS* obj, __noParamWPdispatch wp) :\
		CLASS##CallbackWP( obj) {_wp = wp;}\
    protected:\
	virtual Boolean	dispatch() {(_obj->*_wp)();}\
    private:\
	__noParamWPdispatch _wp;};\
typedef	void (CLASS::*__noParamTdispatch)( XtIntervalId);\
class CLASS##NoParamCallbackT : public CLASS##CallbackT {\
    public:\
	CLASS##NoParamCallbackT( CLASS* obj, __noParamTdispatch t) :\
		CLASS##CallbackT( obj) {_t = t;}\
    protected:\
	virtual void	dispatch() {(_obj->*_t)(_xtIntervalId);}\
    private:\
	__noParamTdispatch _t;};\
inline CallbackW* CALLBACK( CLASS* obj, __floatWdispatch fd, float f) {\
    return new CLASS##FloatCallbackW( obj, fd, f);}\
inline CallbackW* CALLBACK( CLASS* obj, __noParamWdispatch npd) {\
    return new CLASS##NoParamCallbackW( obj, npd);}\
inline CallbackEH* CALLBACK( CLASS* obj, __noParamEHdispatch ed) {\
    return new CLASS##NoParamCallbackEH( obj, ed);}\
inline CallbackWP* CALLBACK( CLASS* obj, __noParamWPdispatch wp) {\
    return new CLASS##NoParamCallbackWP( obj, wp);}\
inline CallbackT* CALLBACK( CLASS* obj, __noParamTdispatch t) {\
    return new CLASS##NoParamCallbackT( obj, t);}
#endif

----- Callback.cc
// Not the Doug Young callback. This by AD Cox, SSRL, 15-Jul-1992

#include    "Application.h"
#include    "AppWidget.h"
#include    "Callback.h"

extern "C" {
void printf(...);
};

Callback::Callback()
{
    _isInUse = False;
}


void CallbackW::addCallback( AppWidget* caller, char* callbackName)
{
    motifapp_assert( (_isInUse == False, _isInUse = True));
						//To prevent multiple
						// assignments to same callback
    printf("\nCallbackW::addCallback...");
    _forWidget = caller;
    XtAddCallback( caller->baseWidget(),
		   callbackName,
		   _callbackWvector,
		   this);
    XtAddCallback( caller->baseWidget(),
		   XmNdestroyCallback,
		   _callbackCleanup,
		   this);
}

void CallbackW::_callbackWvector( Widget, XtPointer clientData,
					  XtPointer callData)
{
    CallbackW* c = (CallbackW*)clientData;
    c->_xmAnyCallbackStruct = (XmAnyCallbackStruct*) callData;
    c->dispatch();
}

void CallbackW::_callbackCleanup( Widget w, XtPointer clientData,
					XtPointer callData)
{
    CallbackW* c = (CallbackW*)clientData;
    printf("\nDeleting callback structure %s", XtName( w));
    delete c;
}

void CallbackEH::addEventHandler( AppWidget* caller, EventMask em,
				Boolean nonmaskable)
{
    motifapp_assert( (_isInUse == False, _isInUse = True));
    _forWidget = caller;
    printf("\nCallbackEH::addEventHandler...");
    XtAddEventHandler( caller->baseWidget(),
		       em,
		       nonmaskable,
		       _callbackEHvector,
		       this);
    XtAddCallback( caller->baseWidget(),
		   XmNdestroyCallback,
		   _callbackCleanup,
		   this);
}

void CallbackEH::_callbackEHvector( Widget,
				   XtPointer clientData,
				   XEvent* event,
				   Boolean*   continue_to_dispatch
				 )
{
    CallbackEH* c = (CallbackEH*)clientData;
    c->_xevent = (XEvent*) event;
    c->dispatch();
}

void CallbackEH::_callbackCleanup( Widget w, XtPointer clientData,
					XtPointer callData)
{
    CallbackEH* c = (CallbackEH*)clientData;
    printf("\nDeleting (eventhandler) callback structure %s", XtName( w));
    delete c;
}

void CallbackWP::addWorkProc()
{
    printf("\nCallbackWP::addWorkProc...");
    theApplication->addWorkProc( _callbackWPvector, this);
}

Boolean CallbackWP::_callbackWPvector( caddr_t clientData)
{
    printf("\nCallbackWP::_callbackWPvector");
    CallbackWP* c = (CallbackWP*) clientData;
    Boolean done = c->dispatch();
    if (done) {
	printf("\nWP complete. Deleting container object...");
	delete c;
    }
    return done;
}

XtIntervalId CallbackT::addTimeOut( unsigned long interval)
{
    printf("\nCallbackT::addTimeOut...");
    theApplication->addTimeOut( interval, _callbackTvector, this);
}

void CallbackT::_callbackTvector( caddr_t clientData,
				  XtIntervalId* xtIntervalId)
{
    printf("\nCallbackT::_callbackTvector...dispatching timeout");
    CallbackT*	c = (CallbackT*) clientData;
    c->_xtIntervalId = *xtIntervalId;
    c->dispatch();
    delete c;
}

------------------------------

Hope this is of interest. If not, sorry for the bandwidth.....
regards,
Tony Cox, Stanford, CA
Newsgroups: comp.lang.c++