From: filo@ccwf.cc.utexas.edu (John Filo)
Newsgroups: comp.windows.open-look
Subject: Re: Q: C++ and callback problems.
Date: 28 Aug 92 00:06:11 GMT
Organization: The University of Texas at Austin
In-reply-to: svan@isy.liu.se's message of 24 Aug 92 17:11:21 GMT

In article <1992Aug24.171121.27530@isy.liu.se> svan@isy.liu.se (Jonas Svanberg) writes:

> I'm using xview myself and have given up the idea of having a nice
> and pure objectoriented system without old-c-style functions :-)

I've been able to use c++ with xview with some success, although the 
way I went about it is sort of a kludge, and after reading Steve's
article I realized that this method will only work on member functions 
that aren't virtual, so that might limit it's usefulness.

Below is a copy of a message I sent someone a few months back
addressing this same issue.  Since I missed the original poster's
question, I don't know if this will help you since the details are
specific to xview, but I imagine a similar scheme could be worked out
with most other toolkits.

=========================================================================
I've used C++ with xview before on a Sun.  I only had two problems.
First, C++ requires prototypes for all functions.  If your header
files don't handle this correctly, then you'll have to write prototypes
yourself, which can be a real pain in the ass.  The include files that 
come with Sun's xview handles this correctly so it wasn't really much of
a problem for me, except for tha fact that one of the include files had
to be modified in order for it to compile correctly.

The second problem is more serious and has to do with using member 
functions as callback procedures.  For example lets say you have an object 
that contains a Canvas and you want to use one of the member functions for 
it's repaint procedure. When xview invokes the repaint procedure, it will 
pass the arguments (Canvas, Xv_window, Display...) as if it were a regular 
C function. However, C++ expects an undeclared, implied "this" pointer to
passed as the first parameter.  This has the effect of shifting all the 
parameters one place to the left, with the Canvas id equal to the 
Xv_window id, the Xv_window id equal to the Display pointer and so on.  
The "this" pointer ends up being set to Canvas id.
There are several solutions to this problem.

  1. Don't use member functions for callbacks.  
     This isn't a very good solution since it reduces the usefullness
     of C++.
  2. Use a static member function that calls a non-static member 
     function.  This way, all the callback stuff is encapsulated
     inside the class containing the callback.  However, in order
     for this to work, the static member function must know
     which object to use when invoking the non-static member
     function. This can be accomplished with the following:

	// comewhere in cObject initialization
        xv_set(xvObject, 
               XV_KEY_DATA, KEY_THIS_PTR, &cObject,
               ???_PROC, staticCallback, 
               NULL);

     then, inside the static callback, you can get the object pointer
     as follows:

        staticCallback(XVObject xvObject, /* other args */)
        {
          CObject  *cObject;

          cObject = (CObject*) xv_get(xvObject, XV_KEY_DATA, KEY_THIS_PTR);
          cObject->nonStaticCallback(xvObject, /* other args */);  
        }

     This is kind of messy, but at least all the mess is hidden in the class
     definition.  My only complaint about this method is that you have to 
     define a static and non-static function for every callback.

  3. Use a technique similar to the one above, but without the static member
     function.


	// somewhere in cObject initialization 
          xv_set(xvObject,
                 XV_KEY_DATA, KEY_THIS_PTR, &cObject,
                 ???_PROC, nonstaticCallback,  //a weird case is required 
                 NULL);                        //here, but I'll have to 
                                               //look it up.

     then, inside the nonstatic callback,

         // note the lack of the XVobject arg, it's stored in the "this" ptr;
         nonstaticCallback(/* other args */
         {
             XVObject  xvObject = (XVObject *) this;
             this = xv_get(xvObject, XV_KEY_DATA, KEY_THIS_PTR);
             ...
             // procede as you would in any c++ member function
         }

     I prefer this method, since all the work is in one place.
     However, the assignment to the "this" pointer is probably 
     considered a no-no, and may even be obsolete.  I used 
     AT&T's CC, version 2.0, and I think the mention something 
     about it being kept in there for backwards compatability,
     but at some point would be removed from the language,
     so this may be unwise.  An alternative is to the following
     in the callback

         nonstaticCallback(/* other args */)
         {
             XVObject  xvObject = (XVObject *) this;
             CObject *me = xv_get(xvObject, XV_KEY_DATA, KEY_THIS_PTR);
             ...
             // now all members must be prepended with me->.  this is
             // very ugly and error prone, and probably not a good idea.
          }

      If you want ensured compatability in the future, I would probably 
      recommend method 2, although I use the 3rd method.  

Hope this helps,

John Filo
filo@ccwf.cc.utexas.edu