home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #1 / NN_1993_1.iso / spool / comp / lang / cplus / 18859 < prev    next >
Encoding:
Text File  |  1993-01-08  |  6.7 KB  |  207 lines

  1. Path: sparky!uunet!zaphod.mps.ohio-state.edu!cs.utexas.edu!sun-barr!news2me.EBay.Sun.COM!exodus.Eng.Sun.COM!appserv.Eng.Sun.COM!midniteoil!soloway
  2. From: soloway@midniteoil.Eng.Sun.COM (Mark Soloway)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Callbacks in C++
  5. Date: 8 Jan 1993 22:03:03 GMT
  6. Organization: Sun Microsystems, Inc.
  7. Lines: 193
  8. Distribution: world
  9. Message-ID: <lkrugnINNjh5@appserv.Eng.Sun.COM>
  10. References: <1iikcuINN6fr@news.cerf.net>
  11. Reply-To: soloway@midniteoil.Eng.Sun.COM
  12. NNTP-Posting-Host: midniteoil
  13.  
  14. In article 1iikcuINN6fr@news.cerf.net, hlf@nic.cerf.net (Howard Ferguson) writes:
  15. >The FAQ for this group describes how to put wrapper functions around 
  16. >member functions so they can be called as callbacks from X libraries
  17. >and the like. But if I am writting a c++ class library, surley I should
  18. >be able to provide a cleaner mechanism to my customers. Is there
  19. >a standard way of doing this???
  20.  
  21. This question comes up often enough that it should be added to the FAQ...
  22.  
  23. Anyway, I sent out the article that follows several months ago.  It describes
  24. a method I used for creating C++ wrappers around XView packages.  It can be
  25. generalized to other toolkits fairly easily...
  26.  
  27.                             - Mark
  28. --------------------------------------------------------------------
  29. This article describes a method I use to provide a convenient C++
  30. callback interface for XView and provide the ability to encapsulate
  31. callback behavior in a subclass.  The method described is used in
  32. UIT V2.
  33.  
  34. In order to provide a convenient C++ callback interface for XView
  35. you want the callback to call a member function of the class that
  36. is encapsulating the XView package.  Ordinary (non-static) member
  37. functions provide an implicit "this" pointer for access to other members
  38. of the object.  Many compilers pass the "this" pointer as an invisible
  39. first argument in the member function.  This means that the following
  40. member function:
  41.  
  42.     ClassName::memberFunction (Panel_item item, Event *event)
  43.  
  44. can look like the following in the C code generated by cfront:
  45.  
  46.     memberFunction__9ClassNameF10Panel_itemP5Event (ClassName  *this,
  47.                                        Panel_item  item,
  48.                                     Event      *event)
  49.  
  50.     (See C++ Annotated Refernce Manual (ARM), section 7.2 for
  51.      more information regarding funtion name encoding).
  52.  
  53. This causes a problem with callbacks becuase of the "this" argument.
  54. The way to get around this is by using a static member function in
  55. callbacks.  Static member functions do not provide a "this" pointer
  56. to access ordinary class members and do not have a "this" argument in
  57. the C code form of the member function.  If you have the following static
  58. member function defined for a Button class (encapsulating the XView
  59. PANEL_BUTTON package):
  60.  
  61.     void Button::notifyProc (Panel_item item, Event *event)
  62.  
  63. You can specify it as a callback in the following way:
  64.  
  65.     // Create the XView button and store it in a data mwember
  66.     // of the Button class (Button::xviewButton).
  67.     xviewButton = xv_create (owner, PANEL_BUTTON,
  68.                      PANEL_NOTIFY_PROC, &Button::notifyProc,
  69.                  NULL);
  70.  
  71. With some compilers (g++, for example) the static member function must
  72. be public if you want to get the address of it.  This is not true for
  73. most AT&T derived compilers.
  74.  
  75. In order to actually provide a convenient callback interface you really
  76. need to get hold of a pointer to the Button object when the callback is
  77. called.  This can be done by attaching data to the XView object that
  78. calls the callback.  XV_KEY_DATA provides an easy way to do this.  First
  79. a constant key value must be created.  Here are 2 ways of doing this:
  80.  
  81.     1) Use a "const int" or a "#define" to define a key value in
  82.        a header file that will be used for this purpose:
  83.  
  84.         const int THIS_POINTER_KEY = 100;
  85.  
  86.        or:
  87.  
  88.         #define THIS_POINTER_KEY 100
  89.  
  90.        Of course, you must make sure the key value you choose is unique
  91.        for your application.  Or,
  92.  
  93.     2) Create a global variable and use xv_unique_key to initialize
  94.        the value:
  95.  
  96.         int this_pointer_key_g;
  97.  
  98.         void main (int argc, char **argv)
  99.         {
  100.           this_pointer_key_g = xv_unique_key();
  101.  
  102.                   ...
  103.         }
  104.  
  105. After creating the unique key, use the key to store the encapsulating object
  106. "this" pointer in the XView button object:
  107.  
  108.         xv_set (xviewButton,
  109.             XV_KEY_DATA, THIS_POINTER_KEY, this,
  110.             NULL);
  111.  
  112. A basic notifyProc member function that calls a user specified callback
  113. handler might look like this:
  114.  
  115.     void Button::notifyProc (Panel_item item, Event *event)
  116.     {
  117.       Button *object = (Button *)xv_get(item,
  118.                                         XV_KEY_DATA,
  119.                         THIS_POINTER_KEY);
  120.  
  121.       // If a notify handler has been specified, via
  122.       // "void Button::setNotifyHandler (void (*)(Button *, Event *))",
  123.       // then call it.
  124.           if (object->notifyHandler)
  125.             (*object->notifyHandler)(object, event);
  126.     }
  127.  
  128. The Button class might look like this:
  129.  
  130.         class Button {
  131.         public:
  132.           ... 
  133.       void setNotifyHandler(void (*handler)(Button *, Event *))
  134.         {
  135.           notifyHandler = handler;
  136.         }
  137.  
  138.         protected:
  139.           // Make xviewButton protected so that derived classes can get hold
  140.           // of the XView button object when inside of their personalized
  141.           // handlers.
  142.           Panel_item xviewButton;
  143.  
  144.         private:
  145.           static void   notifyProc(Panel_item, Event *);
  146.           void        (*notifyHandler)(Button *, Event *);
  147.         }
  148.  
  149. This provides a "convenient" callback interface, but it does not provide
  150. for encapsulation of callback behavior in the class.  To provide the ability
  151. to encapsulate the behavior, you need to provde a virtual function that
  152. will be overridden in subclasses that are encapsulating the callback
  153. behavior:
  154.  
  155.     class Button {
  156.     public:
  157.       ...
  158.  
  159.     protected:
  160.       // Make xviewButton protected so that derived classes can get hold
  161.       // of the XView button object when inside of their personalized
  162.       // handlers.
  163.           Panel_item xviewButton;
  164.  
  165.     private:
  166.       static  void notifyProc(Panel_item, Event *);
  167.       virtual void notifyHandler(Event *);
  168.     }
  169.  
  170.     void Button::notifyProc (Panel_item item, Event *event)
  171.     {
  172.       Button *object = (Button *)xv_get(item,
  173.                                         XV_KEY_DATA,
  174.                         THIS_POINTER_KEY);
  175.  
  176.       object->notifyHandler(event);
  177.     }
  178.     
  179. You can then define the behavior of notifyHandler in a class derived
  180. from Button:
  181.  
  182.     class FileButton : public Button {
  183.     public:
  184.       ...
  185.  
  186.     private:
  187.       virtual notifyHandler(Button *, Event *);
  188.     }
  189.  
  190.     void FileButton::notifyHandler (Event *event)
  191.     {
  192.       // Do whatever I want...
  193.     }
  194.  
  195. Because of virtual functions, FileButton::notifyHandler will have
  196. an implicit "this" pointer to the FileButton object that is encapsulating
  197. the XView object the callback occurred on.
  198.  
  199.                     - Mark
  200. __________________________________________
  201. \_Mark Soloway (mark.soloway@Eng.Sun.COM)_\
  202. /_Distributed Systems Services (ToolTalk)_/
  203. \__SunSoft (A Sun Microsystems Company)___\
  204.  
  205.  
  206.  
  207.