rink@tramp.colorado.edu (Jeff) (03/09/91)
Howdy. Help requested from C++ and X gurus: I am trying to set up a callback for an X widget. I would LIKE to have the callback be a member-function of a C++ class. An example: struct blah { void foo(); } .... blah instance; XtAddCallback( ---- , ---- , instance.foo, ---- ); The problem(s): When I try it (as in the example), foo() is not called with the proper, hidden, THIS* argument. THIS, in fact, points to a mess. I suspected that X is packing the normal callback arguments where they shouldn't be (w, client_data, and call_data), so I tried explicitly specifying the arguments: struct blah { void foo(Widget, caddr_t, caddr_t); } But it still doesn't work. THIS still points somewhere else. Any ideas? I'm sorry if this isn't more clear. I'm rather new to both X and C++. Jeff Rink (rink@tramp.colorado.EDU)
bob@odi.COM (Bob Miner) (03/10/91)
Howdy. Help requested from C++ and X gurus: I am trying to set up a callback for an X widget. I would LIKE to have the callback be a member-function of a C++ class. An example: struct blah { void foo(); } .... blah instance; XtAddCallback( ---- , ---- , instance.foo, ---- ); The problem(s): When I try it (as in the example), foo() is not called with the proper, hidden, THIS* argument. THIS, in fact, points to a mess. I suspected that X is packing the normal callback arguments where they shouldn't be (w, client_data, and call_data), so I tried explicitly specifying the arguments: struct blah { void foo(Widget, caddr_t, caddr_t); } But it still doesn't work. THIS still points somewhere else. Any ideas? I'm sorry if this isn't more clear. I'm rather new to both X and C++. Jeff Rink (rink@tramp.colorado.EDU) To the best of my knowledge, there's no way to make Xt directly call a C++ member function. If there is, I'd love to hear it. The way to make Xt and C++ work together is to create a static C++ function as the callback function for each callback. This function receives events, determines which C++ object should handle the event and then passes the event to that C++ object. Your callback function can determine which C++ object to call in at least two ways. One way is to, when you add the callback to the widget, set the clientData (the second arg) to be a pointer to your C++ object. Then, when you get the callback, cast the clientData to a pointer to the appropriate C++ type and call the appropriate event handling function on the C++ object. Another way which can be used for some toolkits (OSF/Motif at least) is to, when creating the widget, set the userData resource on the widget to be a pointer to the corresponding C++ object. Then, when you get the callback, get the userData resource from the given widget and cast the userData resource value to the appropriate C++ type and call the appropriate event handling function on that C++ object. I've also heard of people using hash tables to map widgets to C++ objects, but I'm not sure what advantage that has over the above two alternatives. Bob Miner ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Object Design, Inc. ~ OOOOOO 1 New England Executive Park ~ OOOO OOOO Burlington, MA 01803-5005 USA ~ OOOOO OOOOO bob@odi.com -or- uunet!odi!bob ~ OOOO OOOO voice: (617) 270-9797 FAX: (617) 270-3509 ~ OOOOOO bject Design Inc. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "From there to here, from here to there, funny things are everywhere." - Dr. Seuss ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nazgul@alphalpha.com (Kee Hinckley) (03/10/91)
In article <1991Mar9.123358.14352@csn.org> rink@tramp.colorado.edu (Jeff) writes: > > >Howdy. >Help requested from C++ and X gurus: > > > I am trying to set up a callback for an X widget. I would LIKE > to have the callback be a member-function of a C++ class. > An example: The only way you can make this work is to declare the member function static. Then it's up to you to figure out how to pass "this". The easiest way is in the the callback data, the second easiest is in the widgets userData resource. It would be nice if C++ allowed you to then assign this and proceed normally, but.... If you want to do virtual functions, declare two callbacks, one static and one virtual. All the static one does is call the virtual one. -kee -- Alfalfa Software, Inc. | Poste: The EMail for Unix nazgul@alfalfa.com | Send Anything... Anywhere 617/646-7703 (voice/fax) | info@alfalfa.com I'm not sure which upsets me more: that people are so unwilling to accept responsibility for their own actions, or that they are so eager to regulate everyone else's.
kittu@shakti.wpd.sgi.com (Krishna Swaroop Kolluri) (03/10/91)
What you can do is to declare the callback as a friend function of the class. This fried function should take a pointer to the object of the class as argument which you can pass as client_data. Hope that helps! kittu.. -- ------- Krishna Swaroop Kolluri 9U-530 kittu@sgi.com Silicon Graphics, Inc. Off:(415)335-1859 Fax:(415)969-2314
XITIJSCH@DDATHD21.BITNET (03/11/91)
Try the following (I assume you use an ANSI C preprocessor): --- This goes into a general header file: #define use_as_callback(Class, foo) \ void foo##_dispatch(Widget w, Class *object, caddr_t call_data) \ { \ object->foo(w, 0, call_data); \ } #define Cpp_callback(foo) foo##_dispatch --- This is your C++ code: class Name_of_class { ... void do_it(Widget w, caddr_t client_data, caddr_t call_data); ... }; use_as_callback(Name_of_class, do_it); .. Name_of_class object; ... XtAddCallback(w, Cpp_callback(do_it), &object); or, if you use Motif, you will register Cpp_callback(do_it) and pass &object via an UIL identifier, etc. There remains a problem with this approach: You cannot pass client_data -- but perhaps you may attach it as userdata to the widget. -- Joachim Schrod <xitijsch@ddathd21.bitnet> Technical University of Darmstadt
baecker@gmdzi.gmd.de (Andreas Baecker) (03/13/91)
We have made the approach described below. It allows all kinds of functions, including member functions, to be used as callbacks. It also provides a means for using virtual functions as callbacks. Consider the following definitions: class CallbackTest { public: CallbackTest(Gcomposite *parent); protected: // These functions should be called as callbacks // All callbacks MUST have their last argument of type caddr_t // This is one which doen't have a client_data void CallbackWithoutClientData(caddr_t call_data); // This one has one client_data argument void CallbackWithOneClientData(void *client_data, caddr_t call_data); // This one has two client_data arguments void CallbackWithTwoClientData(void *client_data_1, void *client_data_2, caddr_t call_data); // This one is a "wrapper" for a virtual function with one client_data void VirtualWrapper(void *client_data, caddr_t call_data); // This is the virtual function which is wrapped by the above function virtual void Wrapped(void *client_data, caddr_t call_data); private: // This is a C++ PushButton object which has the above functions in it's // activateCallback list. GpushButton button; } And here is how we attach the member functions as callbacks to the button widget. We do this in the constructor: CallbackTest::CallbackTest(Gcomposite *parent) { button.create(parent, "someButton", /* managed = */ True ); // Create a callback object for a callback without client_data // Save a pointer to the callback object on a local variable Gcallback * cb_1 = CALLBACK(CallbackTest, CallbackWithoutClientData, this); button.add_activate_callback(cb_1); // Create a callback object with one client_data (the button's address) // This one uses an anonymous callback object button.add_activate_callback(CALLBACK1(CallbackTest, CallbackWithOneClientData, this, GpushButton *, &button)); // Create a callback object with two client_data (button & cb_1) button.add_activate_callback(CALLBACK2(CallbackTest, CallbackWithTwoClientData, this, GpushButton *, &button, Gcallback *, cb_1)); // This one attaches the virtual wrapper as a callback button.add_activate_callback(CALLBACK1(CallbackTest, VirtualWrapper, this GpushButton *, &button)); } CallbackTest::VirtualWrapper(void *client_data, caddr_t call_data) { Wrapped(client_data, call_data); } The idea is to introduce so-called callback objects (class Gcallback). Complete listings of Gcallback.h and Gcallback.C are appended to this message. In the case of callbacks without client_data, these objects contais a pointer to the function to be called and a "this" pointer. Classes derived from Gcallback have additional members which hold the client_data arguments. In our current implementation, we have two derived classes GcallbackOne and GcallbackTwo which hold one or two client_data arguments, respectively. Callback objects may be used as ordinary callbacks, as event handlers, as protocol callback handlers and as timeout handlers (XtAppAddTimout). The macros CALLBACK, CALLBACK1 and CALLBACK2 create "new" callback objects of classes Gcallback, GcallbackOne and GcallbackTwo, respectively. These macros provide all necessary type checking and therefor increase security and reduce the amount of code to be written. Here is the definition of CALLBACK1: #define CALLBACK1(class, function, object, client_data_class_one, client_data_1)\ (new GcallbackOne((GinaClassCallbackProcOne)(class :: function), \ (void *)(class *)(object), \ (void *)(client_data_class_one)(client_data_1))) The macro guarantees that "function" is a member function of class "class", and that "object" could be casted to a pointer to an instance of "class". The class Gcallback has a general static callback handler which is an XtCallbackProc: static Gcallback::widget_handler(Widget w, client_data cl, caddr_t call_data) { Gcallback *cb = (Gcallback *)client_data; cb->evaluate(call_data); } The callback handler is the function which is really added to a widget's callback list when add_activate_callback() is called. The callback handler gets the callback object as it's client_data and a widget-dependent call_data. Note that evaluate() is a public function. It is therefor possible to call it from everywhere in a program. This provides a means for implementing callback in other contexts, which have nothing to do with widget. It is also possible to implement additional callback lists in classes derived from the classes which wrap the Motif widget classes, especially in any kind of dialogs. We can provide an example if it is desired. The callback handler calls the virtual function Gcallback::evaluate(). Gcallback::evaluate() then calls the C++ function with it's arguments. We show the implementation of Gcallback::evaluate() and GcallbackOne::evaluate(): // We include this-> for documentation. void Gcallback::evaluate(caddr_t call_data) { (this->function_name)(this->object, this->call_data); } void GcallbackOne::evaluate(caddr_t call_data) { (this->function_name)(this->object, this->client_data_one, this->call_data); } The PushButton's member function add_activate_callback() calls Gcallback::attach_to_widget() and provides all necessary arguments for attaching a callback: void GpushButton::add_activate_callback(Gcallback *cb) { cb->attach_to_widget(this->get_widget_id(), XmNactivateCallback); } Finally, Gcallback::attach_to_widget() calls XtAddCallback to add the callback: void Gcallback:: attach_to_widget(Widget widget, char *name) { Require( cb_type == CB_NOT_ATTACHED ); cb_type = CB_WIDGET; cb_info.callback.widget = widget; cb_info.callback.name = name; XtAddCallback(widget, name, (XtCallbackProc)Gcallback::widget_handler, (XtPointer)this); } Using non-member functions as callbacks is similar. You may read through the source code and see how it is done. Andreas ============================================================================== Mail: Andreas Baecker GMD Gesellschaft fuer Mathemathik und Datenverarbeitung mbH (The German National Research Center for Computer Science) Schloss Birlinghoven D-5205 Sankt Augustin 1 Germany email: baecker@gmdzi.gmd.de PHONE: +49-2241-142078 FAX : +49-2241-142618 ============================================================================== Listing of Gcallback.h ====================== /* @(#)Gcallback.h 1.14 2/25/91 */ #ifndef Gina_Callback_H #define Gina_Callback_H #include <Gina/Gobject.h> #ifdef USE_NIHCL # define Object XObject # define String XString #endif USE_NIHCL #include <Xm/AtomMgr.h> #ifdef USE_NIHCL # undef Object # undef String #endif USE_NIHCL class Gtimer; typedef void (*GinaSimpleCallbackProc) (caddr_t); class Gcallback : public Gobject { friend class Gtimer; public: typedef enum GinaCallbackType { CB_NOT_ATTACHED, CB_WIDGET, CB_EVENT, CB_PROTOCOL, CB_TIMER}; public: Gcallback(GinaSimpleCallbackProc); ~Gcallback(); virtual void evaluate(caddr_t call_data); void attach_to_widget(Widget, char *); void attach_to_widget(Widget, EventMask mask, Boolean non_maskable); void attach_to_widget(Widget, Atom, Atom); void attach_to_timer(Gtimer *); void remove(); protected: // Callback handlers (called from "C" only) static void widget_handler(Widget w, Gcallback* client_data, caddr_t call_data); static void timer_handler(Gtimer *, XtIntervalId *); GinaSimpleCallbackProc function_name; GinaCallbackType cb_type; union { struct {Widget widget; char *name; } callback; struct {Widget widget; EventMask mask; Boolean non_maskable;} event; struct {Widget widget; Atom property, protocol; } atom; struct {Gtimer *timer; } timer; } cb_info; }; DefGenericList(GcallbackList, Gcallback); typedef void (*GinaClassCallbackProc) (void *, caddr_t); class GclassCallback : public Gcallback { public: GclassCallback(GinaClassCallbackProc proc, void* object); virtual void evaluate(caddr_t call_data); protected: void *object; }; typedef void (*GinaClassCallbackProcOne) (void *, void *, caddr_t); class GcallbackOne : public GclassCallback { public: GcallbackOne(GinaClassCallbackProcOne proc, void* object, void *); virtual void evaluate(caddr_t call_data); protected: void *client_data_one; }; typedef void (*GinaClassCallbackProcTwo) (void *, void *, void *, caddr_t); class GcallbackTwo : public GcallbackOne { public: GcallbackTwo(GinaClassCallbackProcTwo proc, void*, void *, void *); virtual void evaluate(caddr_t call_data); protected: void *client_data_two; }; #define SIMPLE_CALLBACK(function) (new Gcallback((function))) #define SIMPLE_CALLBACK1(function, client_data_class, client_data) \ (new GclassCallback((function), \ (void *)(client_data_class)(client_data))) #define SIMPLE_CALLBACK2(function, client_data_class_one, client_data_one, client_data_class_two, client_data_two) \ (new GcallbackOne((function), \ (void *)(client_data_class_one)(client_data_one),\ (void *)(client_data_class_two)(client_data_two))) #define CALLBACK(class, function, object) \ (new GclassCallback((GinaClassCallbackProc)(class :: function), \ (void *)(class *)(object))) #define CALLBACK1(class, function, object, client_data_class_one, client_data_1) \ (new GcallbackOne((GinaClassCallbackProcOne)(class :: function), \ (void *)(class *)(object), \ (void *)(client_data_class_one)(client_data_1))) #define CALLBACK2(class, function, object, client_data_class_one, client_data_1, client_data_class_two, client_data_2) \ (new GcallbackTwo((GinaClassCallbackProcTwo)(class :: function), \ (void *)(class *)(object), \ (void *)(client_data_class_one)(client_data_1), \ (void *)(client_data_class_two)(client_data_2))) #endif Gina_Callback_H ================================================================================ Listing of Gcallback.C: ======================= /* @(#)Gcallback.C 1.10 2/25/91 */ #include <Gina/Gobject.h> #include <Gina/Gcallback.h> #include <Gina/Gtimer.h> extern "C" { extern void XmAddProtocolCallback(Widget, Atom, Atom, XtCallbackProc, XtPointer); extern void XmRemoveProtocolCallback(Widget, Atom, Atom, XtCallbackProc, XtPointer); } Gcallback:: Gcallback(GinaSimpleCallbackProc proc) { function_name = proc; cb_type = CB_NOT_ATTACHED; } Gcallback:: ~Gcallback() { if( cb_type != CB_NOT_ATTACHED ) remove(); } void Gcallback:: evaluate(caddr_t call_data) { (function_name)(call_data); } void Gcallback:: attach_to_widget(Widget widget, char *name) { Require( cb_type == CB_NOT_ATTACHED ); cb_type = CB_WIDGET; cb_info.callback.widget = widget; cb_info.callback.name = name; XtAddCallback(widget, name, (XtCallbackProc)Gcallback::widget_handler, (XtPointer)this); } void Gcallback:: attach_to_widget(Widget widget, EventMask mask, Boolean non_maskable) { Require( cb_type == CB_NOT_ATTACHED ); cb_type = CB_EVENT; cb_info.atom.widget = widget; cb_info.event.mask = mask; cb_info.event.non_maskable = non_maskable; XtAddEventHandler(widget, mask, non_maskable, (XtEventHandler)Gcallback::widget_handler, (XtPointer)this); } void Gcallback:: attach_to_widget(Widget widget, Atom property, Atom protocol) { Require( cb_type == CB_NOT_ATTACHED ); cb_type = CB_PROTOCOL; cb_info.atom.widget = widget; cb_info.atom.property = property; cb_info.atom.protocol = protocol; XmAddProtocolCallback(widget, property, protocol, (XtCallbackProc)Gcallback::widget_handler, (XtPointer)this); } void Gcallback:: attach_to_timer(Gtimer *timer) { Require( cb_type == CB_NOT_ATTACHED ); cb_type = CB_TIMER; cb_info.timer.timer = timer; } void Gcallback:: remove() { Require( cb_type != CB_NOT_ATTACHED ); switch(cb_type) { case CB_WIDGET: XtRemoveCallback(cb_info.callback.widget, cb_info.callback.name, (XtCallbackProc)function_name, (XtPointer)this); break; case CB_EVENT: XtRemoveEventHandler(cb_info.event.widget, cb_info.event.mask, cb_info.event.non_maskable, (XtEventHandler)function_name, (XtPointer)this); break; case CB_PROTOCOL: XmRemoveProtocolCallback(cb_info.atom.widget, cb_info.atom.property, cb_info.atom.protocol, (XtCallbackProc)function_name, (XtPointer)this); break; case CB_TIMER: cb_info.timer.timer->remove_callback(this); break; case CB_NOT_ATTACHED: break; } cb_type = CB_NOT_ATTACHED; } void Gcallback:: widget_handler(Widget, Gcallback *cb, caddr_t call_data) { cb->evaluate(call_data); } void Gcallback:: timer_handler(Gtimer *timer, XtIntervalId *id) { Require(*id == timer->id); Require( ! timer->destroyed ); Require(timer->enabled); Require(timer->running); Gina_Debug_NL ("TimerDispatcher"); timer->enabled = False; // TimeOut is removed automatically by Xt if( timer->active_in_handler ) timer->install_timeout(); timer->busy = True; for( timer->callbacks.start(); ! timer->callbacks.off(); timer->callbacks.forth() ) timer->callbacks.item()->evaluate((caddr_t)(void *)timer); timer->busy = False; if(! timer->active_in_handler && timer->running && ! timer->enabled && ! timer->destroyed ) timer->install_timeout(); if( timer->running && timer->destroyed ) timer->deinstall_timeout(); if( timer->destroyed ) delete timer; } GclassCallback :: GclassCallback (GinaClassCallbackProc proc, void* obj) : ((GinaSimpleCallbackProc)proc) { object = obj; } void GclassCallback::evaluate(caddr_t call_data) { ((GinaClassCallbackProc)function_name)(object, call_data); } GcallbackOne:: GcallbackOne(GinaClassCallbackProcOne proc, void* obj, void *cd_one) : ((GinaClassCallbackProc)proc, obj) { client_data_one = cd_one; } void GcallbackOne::evaluate(caddr_t call_data) { ((GinaClassCallbackProcOne)function_name)(object, client_data_one, call_data); } GcallbackTwo:: GcallbackTwo(GinaClassCallbackProcTwo proc, void* obj, void *cd1, void *cd2) : ((GinaClassCallbackProcOne)proc, obj, cd1) { client_data_two = cd2; } void GcallbackTwo::evaluate(caddr_t call_data) { ((GinaClassCallbackProcTwo)function_name)(object, client_data_one, client_data_two, call_data); }