XForms: In 0.86 - fl_hide_object(input) calls callback ?

Mark Edward Johnston (M.E.Johnston@damtp.cam.ac.uk)
Thu, 14 Aug 1997 19:17:04 +0100 (BST)

To subscribers of the xforms list from Mark Edward Johnston <M.E.Johnston@damtp.cam.ac.uk> :

I have just downloaded 0.86 so I can use the
fl_adjust_form_size() function. I struck some other weird
problems though, with code which worked under 0.80.

As far as I can tell, the problems seem to be associated with
calls to fl_hide_object() on an input object. One thing I noticed
was that the callback associated with that object gets called
during the call to fl_hide_object(). This seems odd, no ?
It doesn't happen with objects of other types.
Anyway, the object gets hidden OK, but problems occur later, eg. when
I do fl_hide_form() on the form which contained the input, I get
a segmentation fault (Irix 5.3).
I tried it on Dec Alpha too, and there I got an arithmetic exception
in the callback (unsurprising, as in the callback I manipulate a slider,
and that has already been hidden and deleted and freed).
So the problem seems to be that the callback gets called during
fl_hide_object().

I include an example below (it's the C++ example I sent earlier - using
C or C++ callbacks doesn't affect the problem). Compile with
-DDEBUG to see what's happening. Please excuse all the #ifdefs.

Comment out the
fl_hide_object(input);
line and it works (this line is around line 223).

Any ideas appreciated.

Mark J.

--------------

// This file is BFCExample.cc

/*

A quick demo of how to encapsulate XForms objects and their callbacks
in C++ classes.

Mark Johnston, DAMTP, Cambridge 1995.

This uses ANSI C++ std string class. If your compiler doesn't have this,
you'll have to modify the code to use C char[] arrays.

gcc-2.7.2 will compile it.

Compile with eg. :

gcc BFCExample.cc -o BFCExample -g -I../include -L../libALPHA \
-lforms -lX11 -lstdc++ -lm -DDEBUG

(modify the -I and -L as appropriate, of course)

I'm unsure whether it's legal to pass the C++ function pointer
as a callback, since fl_set_object_callback() expects a pointer to a
"C" function. It's probably not legal, but it works on the systems
I've tried it on. To avoid this possible illegality, we can use
"C" functions as "stubs" to call the C++ callback, as shown below
inside the "#ifdef USE_C_CALLBACKS" directives. In my own programs
I'm still using the former method, despite its dubious legality.

To use "C" functions for callbacks instead of C++ functions,
give the flag "-DUSE_C_CALLBACKS", eg.

gcc BFCExample.cc -o BFCExample -g -I../include -L../libALPHA \
-lforms -lX11 -lstdc++ -lm -DDEBUG -DUSE_C_CALLBACKS

*/

#include <string>
#include "forms.h"
#include <iostream.h>
#include <strstream.h>
#include <stdlib.h>

// printing of objects to a string :

template <class T>
string toString(const T &obj)
{
ostrstream os;
os << obj << ends;
char *cs = os.str();
string ret(cs);
delete cs; // must explicitly delete memory used by os,
// as str() has been called
return ret;
}

class BoundedFloatParameter {
friend class BoundedFloatControl; // for simplicity

public:
BoundedFloatParameter(const string &label, float defaultval = 0.0,
float lowerbound = 0.0, float upperbound = 1.0) :
_label(label), _val(defaultval), _lb(lowerbound), _ub(upperbound) { }

// <- Should check default value is in range !

operator float() const { return _val; }

private:
string _label;
float _val, _lb, _ub;
};

class BoundedFloatControl {
public:
BoundedFloatControl(FL_FORM *parent, FL_Coord x, FL_Coord y,
FL_Coord w, FL_Coord h,
BoundedFloatParameter *par);

~BoundedFloatControl();

private:
BoundedFloatParameter *_par;
FL_FORM *_parent;

FL_OBJECT *slider;
FL_OBJECT *input;

// Callback must be a _static_ member function to have the correct signature
// for use as an XForms callback (non-static members get passed the "this"
// pointer of the objects to which they are associated as their first argument).
//
// This means the callback doesn't know which C++ object it is associated with.
// We get around this by hanging the this pointer of the C++ object off the
// u_vdata member of the FL_OBJECT struct of each Xforms object it contains.
// (see constructor below)

#ifdef USE_C_CALLBACKS
public:
#endif // USE_C_CALLBACKS
static void callback(FL_OBJECT* obj, long data);
};

#ifdef USE_C_CALLBACKS
extern "C" void BoundedFloatControl_callback(FL_OBJECT* obj, long data);
#endif // USE_C_CALLBACKS

BoundedFloatControl::BoundedFloatControl(
FL_FORM *parent, FL_Coord x, FL_Coord y, FL_Coord w,
FL_Coord h, BoundedFloatParameter *par)
: _par(par), _parent(parent)
{
fl_addto_form(_parent);

slider = fl_add_slider(FL_HOR_NICE_SLIDER, x + 2 * w/3, y, w/3, h, "");

#ifdef USE_C_CALLBACKS
fl_set_object_callback(slider, BoundedFloatControl_callback, 0);
#else
fl_set_object_callback(slider, callback, 0);
#endif // USE_C_CALLBACKS

fl_set_slider_return(slider, FL_RETURN_CHANGED);
fl_set_slider_bounds(slider, _par->_lb, _par->_ub);
fl_set_slider_value(slider, _par->_val);
fl_set_object_gravity(slider, FL_NorthWest, FL_NorthWest);

input = fl_add_input(FL_FLOAT_INPUT, x + w/3, y, w/3, h,
_par->_label.c_str());
fl_set_object_lsize(input,FL_NORMAL_SIZE);

#ifdef USE_C_CALLBACKS
fl_set_object_callback(input, BoundedFloatControl_callback, 0);
#else
fl_set_object_callback(input, callback, 0);
#endif // USE_C_CALLBACKS

fl_set_input(input, toString(_par->_val).c_str());
fl_set_object_gravity(input, FL_NorthWest, FL_NorthWest);

// This is the important bit :

slider->u_vdata = (void *) this;
input->u_vdata = (void *) this;

fl_end_form();
}

void BoundedFloatControl::callback(FL_OBJECT* obj, long data)
{
#ifdef DEBUG
cerr << "BoundedFloatControl::callback() called" << endl;
#endif // DEBUG
BoundedFloatControl *win = (BoundedFloatControl *) obj->u_vdata;

float value;

if (obj == win->input) value = atof(fl_get_input(obj));
else value = fl_get_slider_value(obj);

if (value < win->_par->_lb) value = win->_par->_lb;
else if (value > win->_par->_ub) value = win->_par->_ub;

win->_par->_val = value;

fl_set_input(win->input, toString(value).c_str());
fl_set_slider_value(win->slider, value);
#ifdef DEBUG
cerr << "BoundedFloatControl::callback() finished" << endl;
#endif // DEBUG
}

#ifdef USE_C_CALLBACKS
void BoundedFloatControl_callback(FL_OBJECT* obj, long data)
{
#ifdef DEBUG
cerr << "BoundedFloatControl_callback() called" << endl;
#endif // DEBUG
BoundedFloatControl::callback(obj, data);
#ifdef DEBUG
cerr << "BoundedFloatControl_callback() finished" << endl;
#endif // DEBUG
}
#endif // USE_C_CALLBACKS

BoundedFloatControl::~BoundedFloatControl()
{
#ifdef DEBUG
cerr << "BoundedFloatControl: destructor called" << endl;
cerr << "About to hide the slider" << endl;
#endif // DEBUG

fl_hide_object(slider);

#ifdef DEBUG
cerr << "Hid it." << endl;
cerr << "About to delete the slider" << endl;
#endif // DEBUG

fl_delete_object(slider);

#ifdef DEBUG
cerr << "Deleted it." << endl;
cerr << "About to free the slider" << endl;
#endif // DEBUG

fl_free_object(slider);

#ifdef DEBUG
cerr << "Freed it." << endl;
cerr << "About to hide the input" << endl;
#endif // DEBUG

// In XForms 0.86, uncommenting the next line causes problems.
// The callback gets called, and then later, in fl_hide_form(), we
// get a SIGSEV (on SGI 5.3)

fl_hide_object(input);

#ifdef DEBUG
cerr << "Hid it." << endl;
cerr << "About to delete the input" << endl;
#endif // DEBUG

fl_delete_object(input);

#ifdef DEBUG
cerr << "Deleted it." << endl;
cerr << "About to free the input" << endl;
#endif // DEBUG

fl_free_object(input);

#ifdef DEBUG
cerr << "Freed it." << endl;
cerr << "BoundedFloatControl: destructor finished" << endl;
#endif // DEBUG
}

int main(int argc, char *argv[])
{
fl_initialize(&argc, argv, "", 0, 0);

BoundedFloatParameter data1("data1"), data2("data2", 1.0, -1.0, 2.5);

cout << "data1 = " << data1 << "; data2 = " << data2 << endl;

const int wd = 300, gap = 8, ht = 30;
int vpos = gap, vdelta = ht + gap;

int numctrls = 2;

//---------------
// In a real application, the form itself would often be contained in a C++
// object, and the follwing junk would be in its constructor.
//---------------

FL_FORM *pform =
fl_bgn_form(FL_UP_BOX, wd + 2 * gap, gap + (numctrls + 1) * vdelta);
fl_end_form();

BoundedFloatControl *floatinput1 =
new BoundedFloatControl(pform, gap, vpos, wd, ht, &data1);
vpos += vdelta;

BoundedFloatControl *floatinput2 =
new BoundedFloatControl(pform, gap, vpos, wd, ht, &data2);
vpos += vdelta;

fl_addto_form(pform);
FL_OBJECT *ok =
fl_add_button(FL_NORMAL_BUTTON, gap + wd/4, vpos, wd/2, ht, "OK");
fl_set_object_lsize(ok,FL_NORMAL_SIZE);
fl_end_form();

fl_show_form(pform, FL_PLACE_SIZE, FL_FULLBORDER, "Test");

while (fl_check_forms() != ok);

//---------------
// ... and this in its destructor :

delete floatinput1;
delete floatinput2;

#ifdef DEBUG
cerr << "About to hide the form." << endl;
#endif // DEBUG
fl_hide_form(pform);
#ifdef DEBUG
cerr << "Hid it. About to free it." << endl;
#endif // DEBUG
fl_free_form(pform);
#ifdef DEBUG
cerr << "Freed it." << endl;
#endif // DEBUG

//---------------

cout << "data1 = " << data1 << "; data2 = " << data2 << endl;

return 0;
}

--------------

---
Mark Johnston.                    M.E.Johnston@damtp.cam.ac.uk
816 King's College, Cambridge, CB2 1ST, U.K.
 or: DAMTP, Silver St., Cambridge, CB3 9EW, U.K.
'phone: (home) +44 1223 500 585 (has voice mail, let it ring!)
WWW: http://www.damtp.cam.ac.uk/user/mej20/

_________________________________________________ To unsubscribe, send the message "unsubscribe" to xforms-request@bob.usuf2.usuhs.mil or see http://bob.usuf2.usuhs.mil/mailserv/xforms.html Xforms Home Page: http://bragg.phys.uwm.edu/xforms List Archive: http://bob.usuf2.usuhs.mil/mailserv/list-archives/