home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #16 / NN_1992_16.iso / spool / comp / lang / cplus / 11429 < prev    next >
Encoding:
Text File  |  1992-07-23  |  13.9 KB  |  465 lines

  1. Path: sparky!uunet!darwin.sura.net!mips!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!unixhub!ssrl01.slac.stanford.edu!tcox
  2. From: tcox@ssrl01.slac.stanford.edu (Tony Cox)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Member function pointers, (Base::*) -> (Derived::*)
  5. Message-ID: <1992Jul23.134107.1@ssrl01.slac.stanford.edu>
  6. Date: 23 Jul 92 21:41:07 GMT
  7. Sender: news@unixhub.SLAC.Stanford.EDU
  8. Organization: SSRL, Stanford, CA
  9. Lines: 453
  10. Nntp-Posting-Host: ssrl01.slac.stanford.edu
  11.  
  12.  
  13.     {long one this - approx. 450 lines}
  14.  
  15. In article <1992Jul17.164853.29520@ucc.su.oz>, maxtal@extro.ucc.su.OZ.AU writes:
  16. > In article <1992Jul16.151946.1@ssrl01.slac.stanford.edu> tcox@ssrl01.slac.stanford.edu writes:
  17. >>
  18. >>I have formed the belief that by careful coding techniques, one can avoid
  19. >>the use of explicit casts on C++ code, except where interface into other
  20. >>language modules is concerned (in this case, the Motif/Xt toolkit).
  21. >     Good.
  22. >>
  23. >>However, there is one stubbon problem which I cannot for the life of me see
  24. >>an elegant solution for, so I am posting this in the hope that someone can
  25. >>enlighten me.
  26. >>
  27. >>Now, as Motif programmers will know, PushButton callbacks, and indeed
  28. >>pretty much all callbacks within Motif, end up calling a "C" routine with
  29. >>the following declaration:-
  30. >>
  31. >>        actionRoutine( Widget, XtPointer clientData, XtPointer)
  32. >>
  33. >>The first arg is the widget wherefrom the callback originated, the second
  34. >>is any (equiv to void*) user provided pointer, and the third is the
  35. >>address of an Xevent structure which further defines the operation.
  36. >     Unless I misunderstand :
  37. > Provide ONE only callback routine in your entire progam.
  38. >     actionRoutine(Widget W, void *object, XtPointer X)
  39. >     {
  40. >         WidgetObject* w=object;
  41. >         w->execute(W,X);
  42. >     }
  43. > Where:
  44. >     class WidgetObject { 
  45. >     public: 
  46. >         virtual execute(Widget,XtPointer)=0;
  47. >     };
  48. > To define the behaviour, derive a class from WidgetObject.
  49. > You must supply the object somehow to Motiff to
  50. > call you back with.
  51. > [PS: I'm not a Motif or even X user, I use Windows. The above
  52. > type of solution is what I do there. It might need tuning.
  53. > But the key is ONE only callback (of each type in Windows).]
  54. > -- 
  55. > ;----------------------------------------------------------------------
  56. >         JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
  57. >     Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
  58. > ;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
  59.  
  60. Max,
  61.     I guess you dont fully understand my problem, so I am glad to
  62. elaborate. I do this because the results of my discussions with several
  63. folks on this newsgroup have brought out some interesting (to me non-obvious)
  64. facts which have general validity. There are a number of `gotcha's for
  65. the unwary.
  66.  
  67.     Your solution works fine if you only want your callbacks to vector
  68. into a single (non-static) member function. The problem I have, is that I
  69. would like to vector back into SEVERAL DIFFERENT (non-static) member functions,
  70. and would like to avoid the confusion of adding (static) member functions for
  71. each one to do the revectoring.
  72.  
  73.     My first approach (and to me what appeared to be the obvious one)
  74. was to typedef the following:-
  75.  
  76.         typedef    void (BASE::*memberFunction)( BASE*);
  77.  
  78. and then hide the revectoring for derived classes within the base class
  79. with a protected member function like this:-
  80.  
  81.     class BASE {
  82.         ...
  83.         protected:
  84.         void registerCallback( memberFunction, BASE*);
  85.         ...
  86.     };
  87.  
  88. The idea being that one could then do something like this:-
  89.  
  90.     Derived::someMethod()
  91.     {
  92.         ....
  93.         registerCallback( myRevectoredMethod, this);
  94.         ....
  95.     }
  96.  
  97.     Derived::myRevectoredMethod()
  98.     {
  99.         ....
  100.     }
  101.  
  102.  
  103. The PROBLEM is that myRevectoredMethod is of class (Derived::*)(Derived*);
  104. One has no problem with the cast of Derived* -> Base* of course - the problem
  105. is with (Derived::*) -> (Base::*).
  106.  
  107. Although this works (GCC V2.0.0, VAX/VMS V5.4), it is NOT TYPE SAFE, and
  108. you can run into problems with revectoring into virtual routines, and probably
  109. with some compiler implementations, elsewhere also. The ARM has some
  110. implementation notes concerning pointers to member functions which I wont
  111. go into here.
  112.  
  113. Doug Young, and others, referred me to a typesafe method of doing this in
  114. his CALLBACK class. It is elegant, but not particularly straightforward.
  115.  
  116. I have implemented my own CALLBACK class hierarchy along the same lines (which
  117. I append to this message to avoid a potential flood of requests), as I need to
  118. be able to revector with arbitrary parameters. The 'Trick' is to define various 
  119. general functions in a CALLBACK base class, and then to define SPECIFIC derived
  120. classes which know about the particular class you are trying to revector into.
  121. This is done in a macro within the (revectoring-into) class.
  122.  
  123. Finally, by overloading a global CALLBACK function with various parameters,
  124. I can get to what I want to do, which is to provide a generic callback 
  125. mechanism which is both type safe and easy to understand in the code.
  126.  
  127. For example (now I'm getting Motif specific, and refer interested readers
  128. back to my original post), I can now do this:-
  129.  
  130. file ScaleInterface.cc
  131.     
  132.         #include "ScaleInterface.h"
  133.         #include "Callback.h"
  134.  
  135.         ....
  136.  
  137.         defineCallback( ScaleInterface);
  138.  
  139.         ...
  140.  
  141.  
  142.         ScaleInterface::someMethod()
  143.         {
  144.         ...
  145.             PushButtonWidget* pb = new PushButtonWidget("Pushme");
  146.             pb->addActivateCallback( CALLBACK( this, iAmPushed));
  147.         ...
  148.         }
  149.  
  150.         ScaleInterface::iAmPushed( PushButtonWidget* pb)
  151.         {
  152.         }    
  153.  
  154.  
  155. For those interested, here is my callback class, modelled on Doug's and
  156. extended as I mentioned. It is a prototype, just developed, so you will
  157. most likely need to extend and correct it.
  158.  
  159. Note in particular the LONG macro 'defineCallback'. I point out that this
  160. can be a real bugger to debug - the macro expansion typically is 3000+
  161. characters, and tends to overflow the VMS editors EDT, EVE and LSE.
  162.  
  163. My CALLBACK class, and the derived classes, revector four types of 
  164. Motif callbacks - simple Widget callback, Event handlers, Work Procedures
  165. and Timeout events. The CALLBACK global function generates the appropriate
  166. derived class depending on the presence of AppWidget, XEvent, XtTimeoutId,
  167. XmAnyCallbackStruct as parameters in the call. At present, only the simple
  168. Widget Callback derived classes allow for additional parameters to be
  169. passed, but it should be obvious from the code how to extend this to
  170. permit additional parameters to be added.
  171.  
  172. ------- Callback.h -------------
  173. // Not the Doug Young callback. This by AD Cox, SSRL, 15-Jul-1992
  174.  
  175. #ifndef    CALLBACK_H
  176. #define    CALLBACK_H
  177.  
  178. #include    "VMSObject.h"
  179. #include    <Xm/Xm.h>
  180. #include    <X11/Xlib.h>
  181.  
  182. extern "C" {
  183. void printf(...);
  184. };
  185.  
  186. class UIComponent;
  187. class AppWidget;
  188.  
  189. class Callback    :   public virtual VMSObject {
  190.     protected:
  191.     Callback();
  192.     Boolean        _isInUse;
  193. };
  194.  
  195. // Simple widget callbacks
  196. class CallbackW    :   public Callback {
  197.     public:
  198.     void    addCallback( AppWidget*, char*);
  199.     protected:
  200.     virtual    void    dispatch() = 0;
  201.     static    void    _callbackCleanup( Widget, XtPointer, XtPointer);
  202.     XmAnyCallbackStruct*    _xmAnyCallbackStruct;
  203.     AppWidget*    _forWidget;
  204.     private:
  205.     static    void    _callbackWvector( Widget, XtPointer, XtPointer);
  206. };
  207.  
  208. // Events registered with XtAddEventHandler
  209. class CallbackEH :  public Callback {
  210.     public:
  211.     void addEventHandler( AppWidget*, EventMask, Boolean);
  212.     protected:
  213.     static    void    _callbackCleanup( Widget, XtPointer, XtPointer);
  214.     virtual void    dispatch() = 0;
  215.     XEvent*        _xevent;
  216.     AppWidget*    _forWidget;
  217.     private:
  218.     static    void    _callbackEHvector( Widget, XtPointer, XEvent*,
  219.                         Boolean*);
  220. };
  221.  
  222. // Work procedures.
  223. class CallbackWP : public Callback {
  224.     public:
  225.     void addWorkProc();
  226.     protected:
  227.     virtual Boolean    dispatch() = 0;
  228.     private:
  229.     static    Boolean    _callbackWPvector( caddr_t);
  230. };
  231.  
  232. // Timeout events
  233. class CallbackT : public Callback {
  234.     public:
  235.     XtIntervalId    addTimeOut( unsigned long);
  236.     protected:
  237.     virtual void dispatch() = 0;
  238.     XtIntervalId    _xtIntervalId;
  239.     private:
  240.     static    void    _callbackTvector( caddr_t, XtIntervalId*);
  241. };
  242.  
  243. #define    defineCallback(CLASS)\
  244. class CLASS;\
  245. class CLASS##CallbackW : public CallbackW {\
  246.     public:\
  247.     CLASS##CallbackW( CLASS* obj) {_obj = obj;}\
  248.     protected:\
  249.     virtual void    dispatch() = 0;\
  250.     CLASS* _obj;};\
  251. class CLASS##CallbackEH : public CallbackEH {\
  252.     public:\
  253.     CLASS##CallbackEH( CLASS* obj) {_obj = obj;}\
  254.     protected:\
  255.     virtual void    dispatch() = 0;\
  256.     CLASS* _obj;};\
  257. class CLASS##CallbackWP : public CallbackWP {\
  258.     public:\
  259.     CLASS##CallbackWP( CLASS* obj) {_obj = obj;}\
  260.     protected:\
  261.     virtual Boolean    dispatch() = 0;\
  262.     CLASS* _obj;};\
  263. class CLASS##CallbackT : public CallbackT {\
  264.     public:\
  265.     CLASS##CallbackT( CLASS* obj) {_obj = obj;}\
  266.     protected:\
  267.     virtual void    dispatch() = 0;\
  268.     CLASS* _obj;};\
  269. typedef    void (CLASS::*__noParamWdispatch)( AppWidget*);\
  270. class CLASS##NoParamCallbackW : public CLASS##CallbackW {\
  271.     public:\
  272.     CLASS##NoParamCallbackW( CLASS* obj, __noParamWdispatch npd) :\
  273.         CLASS##CallbackW( obj)  { _npd = npd;}\
  274.     protected:\
  275.     virtual void    dispatch() {(_obj->*_npd)( _forWidget);}\
  276.     private:\
  277.     __noParamWdispatch    _npd;};\
  278. typedef void (CLASS::*__floatWdispatch)( AppWidget*, float);\
  279. class CLASS##FloatCallbackW : public CLASS##CallbackW {\
  280.     public:\
  281.     CLASS##FloatCallbackW( CLASS* obj, __floatWdispatch fd, float f) :\
  282.         CLASS##CallbackW( obj)  { _fd = fd; _f = f;}\
  283.     protected:\
  284.     virtual void    dispatch() {(_obj->*_fd)( _forWidget, _f);}\
  285.     private:\
  286.     __floatWdispatch    _fd;    float _f;};\
  287. typedef    void (CLASS::*__noParamEHdispatch)( AppWidget*, XEvent*);\
  288. class CLASS##NoParamCallbackEH : public CLASS##CallbackEH {\
  289.     public:\
  290.     CLASS##NoParamCallbackEH( CLASS* obj, __noParamEHdispatch ed) :\
  291.         CLASS##CallbackEH( obj) {_ed = ed;}\
  292.     protected:\
  293.     virtual void    dispatch() {(_obj->*_ed)( _forWidget, _xevent);}\
  294.     private:\
  295.     __noParamEHdispatch    _ed;};\
  296. typedef    Boolean    (CLASS::*__noParamWPdispatch)();\
  297. class CLASS##NoParamCallbackWP : public CLASS##CallbackWP {\
  298.     public:\
  299.     CLASS##NoParamCallbackWP( CLASS* obj, __noParamWPdispatch wp) :\
  300.         CLASS##CallbackWP( obj) {_wp = wp;}\
  301.     protected:\
  302.     virtual Boolean    dispatch() {(_obj->*_wp)();}\
  303.     private:\
  304.     __noParamWPdispatch _wp;};\
  305. typedef    void (CLASS::*__noParamTdispatch)( XtIntervalId);\
  306. class CLASS##NoParamCallbackT : public CLASS##CallbackT {\
  307.     public:\
  308.     CLASS##NoParamCallbackT( CLASS* obj, __noParamTdispatch t) :\
  309.         CLASS##CallbackT( obj) {_t = t;}\
  310.     protected:\
  311.     virtual void    dispatch() {(_obj->*_t)(_xtIntervalId);}\
  312.     private:\
  313.     __noParamTdispatch _t;};\
  314. inline CallbackW* CALLBACK( CLASS* obj, __floatWdispatch fd, float f) {\
  315.     return new CLASS##FloatCallbackW( obj, fd, f);}\
  316. inline CallbackW* CALLBACK( CLASS* obj, __noParamWdispatch npd) {\
  317.     return new CLASS##NoParamCallbackW( obj, npd);}\
  318. inline CallbackEH* CALLBACK( CLASS* obj, __noParamEHdispatch ed) {\
  319.     return new CLASS##NoParamCallbackEH( obj, ed);}\
  320. inline CallbackWP* CALLBACK( CLASS* obj, __noParamWPdispatch wp) {\
  321.     return new CLASS##NoParamCallbackWP( obj, wp);}\
  322. inline CallbackT* CALLBACK( CLASS* obj, __noParamTdispatch t) {\
  323.     return new CLASS##NoParamCallbackT( obj, t);}
  324. #endif
  325.  
  326. ----- Callback.cc
  327. // Not the Doug Young callback. This by AD Cox, SSRL, 15-Jul-1992
  328.  
  329. #include    "Application.h"
  330. #include    "AppWidget.h"
  331. #include    "Callback.h"
  332.  
  333. extern "C" {
  334. void printf(...);
  335. };
  336.  
  337. Callback::Callback()
  338. {
  339.     _isInUse = False;
  340. }
  341.  
  342.  
  343. void CallbackW::addCallback( AppWidget* caller, char* callbackName)
  344. {
  345.     motifapp_assert( (_isInUse == False, _isInUse = True));
  346.                         //To prevent multiple
  347.                         // assignments to same callback
  348.     printf("\nCallbackW::addCallback...");
  349.     _forWidget = caller;
  350.     XtAddCallback( caller->baseWidget(),
  351.            callbackName,
  352.            _callbackWvector,
  353.            this);
  354.     XtAddCallback( caller->baseWidget(),
  355.            XmNdestroyCallback,
  356.            _callbackCleanup,
  357.            this);
  358. }
  359.  
  360. void CallbackW::_callbackWvector( Widget, XtPointer clientData,
  361.                       XtPointer callData)
  362. {
  363.     CallbackW* c = (CallbackW*)clientData;
  364.     c->_xmAnyCallbackStruct = (XmAnyCallbackStruct*) callData;
  365.     c->dispatch();
  366. }
  367.  
  368. void CallbackW::_callbackCleanup( Widget w, XtPointer clientData,
  369.                     XtPointer callData)
  370. {
  371.     CallbackW* c = (CallbackW*)clientData;
  372.     printf("\nDeleting callback structure %s", XtName( w));
  373.     delete c;
  374. }
  375.  
  376. void CallbackEH::addEventHandler( AppWidget* caller, EventMask em,
  377.                 Boolean nonmaskable)
  378. {
  379.     motifapp_assert( (_isInUse == False, _isInUse = True));
  380.     _forWidget = caller;
  381.     printf("\nCallbackEH::addEventHandler...");
  382.     XtAddEventHandler( caller->baseWidget(),
  383.                em,
  384.                nonmaskable,
  385.                _callbackEHvector,
  386.                this);
  387.     XtAddCallback( caller->baseWidget(),
  388.            XmNdestroyCallback,
  389.            _callbackCleanup,
  390.            this);
  391. }
  392.  
  393. void CallbackEH::_callbackEHvector( Widget,
  394.                    XtPointer clientData,
  395.                    XEvent* event,
  396.                    Boolean*   continue_to_dispatch
  397.                  )
  398. {
  399.     CallbackEH* c = (CallbackEH*)clientData;
  400.     c->_xevent = (XEvent*) event;
  401.     c->dispatch();
  402. }
  403.  
  404. void CallbackEH::_callbackCleanup( Widget w, XtPointer clientData,
  405.                     XtPointer callData)
  406. {
  407.     CallbackEH* c = (CallbackEH*)clientData;
  408.     printf("\nDeleting (eventhandler) callback structure %s", XtName( w));
  409.     delete c;
  410. }
  411.  
  412. void CallbackWP::addWorkProc()
  413. {
  414.     printf("\nCallbackWP::addWorkProc...");
  415.     theApplication->addWorkProc( _callbackWPvector, this);
  416. }
  417.  
  418. Boolean CallbackWP::_callbackWPvector( caddr_t clientData)
  419. {
  420.     printf("\nCallbackWP::_callbackWPvector");
  421.     CallbackWP* c = (CallbackWP*) clientData;
  422.     Boolean done = c->dispatch();
  423.     if (done) {
  424.     printf("\nWP complete. Deleting container object...");
  425.     delete c;
  426.     }
  427.     return done;
  428. }
  429.  
  430. XtIntervalId CallbackT::addTimeOut( unsigned long interval)
  431. {
  432.     printf("\nCallbackT::addTimeOut...");
  433.     theApplication->addTimeOut( interval, _callbackTvector, this);
  434. }
  435.  
  436. void CallbackT::_callbackTvector( caddr_t clientData,
  437.                   XtIntervalId* xtIntervalId)
  438. {
  439.     printf("\nCallbackT::_callbackTvector...dispatching timeout");
  440.     CallbackT*    c = (CallbackT*) clientData;
  441.     c->_xtIntervalId = *xtIntervalId;
  442.     c->dispatch();
  443.     delete c;
  444. }
  445.  
  446. ------------------------------
  447.  
  448. Hope this is of interest. If not, sorry for the bandwidth.....
  449. regards,
  450. Tony Cox, Stanford, CA
  451.