home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!darwin.sura.net!mips!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!unixhub!ssrl01.slac.stanford.edu!tcox
- From: tcox@ssrl01.slac.stanford.edu (Tony Cox)
- Newsgroups: comp.lang.c++
- Subject: Re: Member function pointers, (Base::*) -> (Derived::*)
- Message-ID: <1992Jul23.134107.1@ssrl01.slac.stanford.edu>
- Date: 23 Jul 92 21:41:07 GMT
- Sender: news@unixhub.SLAC.Stanford.EDU
- Organization: SSRL, Stanford, CA
- Lines: 453
- Nntp-Posting-Host: ssrl01.slac.stanford.edu
-
-
- {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
-