Discussion:
best way to get an int from Fl_Menu_Item user data
marty moore
2013-04-03 15:44:05 UTC
Permalink
Hi all,
Please excuse another newbie question.

I'm looking at storing an enum as user data in an Fl_Menu_Item user data. In the callback, I want to access the enum.

I know that I can put the enum into user data as a string:
enum eop { task0=200, task2, task3};

Fl_Menu_Item menu = {
{ "thing 1", 0, mycallback, (void*)"201"} .... }

void mycallback(widget* w, void* v)
{ ....
eop e = (eop) (atoi((char*)v));
...
}

but that seems a little cumbersome and inflexable.

I notice that old examples used
int i = (int)v;
but that won't work with gcc-4.4.5

I tried subclassing Fl_Menu_Item, but couldn't get it to work.

Is there a better way?

Will fltk-3 provide more flexibility?

Should this be covered in a tutorial? I couldn't find anything in search.... which surprised me. It seems like it should be covered somewhere for newbies.

Thanks,
Marty
Ian MacArthur
2013-04-03 16:05:09 UTC
Permalink
Assuming this is readable this time... (and not more B64 nonsense...)
Post by marty moore
I'm looking at storing an enum as user data in an Fl_Menu_Item user
data. In the callback, I want to access the enum.
enum eop { task0=200, task2, task3};
Fl_Menu_Item menu = {
{ "thing 1", 0, mycallback, (void*)"201"} .... }
void mycallback(widget* w, void* v)
{ ....
eop e = (eop) (atoi((char*)v));
...
}
What is it you are trying to do here?
Pass a numeric value into the callback in the userdata, I think; but you are doing it by passing a string than calling atoi() on it...
Post by marty moore
but that seems a little cumbersome and inflexable.
Indeed.
Worked example below.
Post by marty moore
I notice that old examples used
int i = (int)v;
but that won't work with gcc-4.4.5
Huh? Should do.
We might need to see your code.
Just tested with gcc 4.7.2 and that works fine...
Post by marty moore
Will fltk-3 provide more flexibility?
No, it is the same.
For now, fltk-1.3 is the recommended build, it is stable. Fltk3 less so...

//
// fltk-config --compile menu-userdata.cxx
//
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <stdio.h>

static Fl_Double_Window *main_win=(Fl_Double_Window *)0;
static Fl_Menu_Bar *menu_bar=(Fl_Menu_Bar *)0;

enum eop { task0=200, task2, task3};

static void cb_item1(Fl_Menu_*, void *v) {
int i = (int)v;
printf("Menu got %d\n", i);
fflush(stdout);
}

Fl_Menu_Item menu_menu_bar[] = {
{"A Menu", 0, 0, 0, 64, FL_NORMAL_LABEL, 0, 14, 0},
{"Item 1", 0, (Fl_Callback*)cb_item1, (void *)task0, 0, FL_NORMAL_LABEL, 0, 14, 0},
{"Item 2", 0, (Fl_Callback*)cb_item1, (void *)task2, 0, FL_NORMAL_LABEL, 0, 14, 0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0}
};

int main(int argc, char **argv) {
{ main_win = new Fl_Double_Window(356, 231, "Test Window");
{ menu_bar = new Fl_Menu_Bar(0, 0, 356, 20);
menu_bar->menu(menu_menu_bar);
} // Fl_Menu_Bar* menu_bar
main_win->end();
} // Fl_Double_Window* main_win
main_win->show(argc, argv);
return Fl::run();
}

///////// end of file ///////////
Greg Ercolano
2013-04-03 16:49:00 UTC
Permalink
Post by Ian MacArthur
Post by Greg Ercolano
void mycallback(widget* w, void* v) {
eop e = (eop) (atoi((char*)v));
What is it you are trying to do here?
Probably trying to sidestep the 'precision loss' error from the
newer compilers due to sizeof(void*) != sizeof(int).

Using a char* is sizeof() compatible with a void*.

This was kinda covered last year in STR #2813:
http://www.fltk.org/str.php?L2813
Post by Ian MacArthur
We might need to see your code.
Just tested with gcc 4.7.2 and that works fine...
Might be a 32bit compile?

I get the precision loss error when I build that example on 4.4.6 with a 64bit box:

$ make foo2
Compiling foo2.cxx...
foo2.cxx: In function 'void cb_item1(Fl_Menu_*, void*)':
foo2.cxx:15: error: cast from 'void*' to 'int' loses precision
make: *** [foo2.o] Error 1
Greg Ercolano
2013-04-03 16:34:22 UTC
Permalink
Post by marty moore
I notice that old examples used
int i = (int)v;
but that won't work with gcc-4.4.5
Right -- probably a "precision loss" error during the void* -> int,
since sizeof(void*)==8 and sizeof(int)==4.

I think the best approach (in 1.3.x) is to use the new fl_intptr_t, e.g.

int i = fl_intptr_t(v);

Or, you could just use a long instead of an int, but that might cause
trouble on non-64bit builds.. which is I think what fl_intptr_t tries
to solve. (See the definition in FL/Fl_Widget.H)

There is a "long Fl_Widget::argument()" method which would let you do:

void mycallback(widget* w, void* v) {
long li = w->argument();
[..]

..but again, I think you'd loose on a 32bit build when doing the long->void* cast.
Which is why I think the fl_intptr_t is probably the best way to go.

While dicking around, of interest I found this also seems to evade
the precision loss error, not completely sure why:

void foo(Fl_Widget *w, void *v) {
int i = (int)(long)(v);
[..]
Post by marty moore
Should this be covered in a tutorial?
Probably -- I don't think fl_intptr_t is even documented yet
(it has a 'todo' signifier), but it's defined in Fl_Widget.H
and used in some of the examples, like test/tree.cxx.
Richard Sanders
2013-04-03 18:38:08 UTC
Permalink
Post by Greg Ercolano
Post by marty moore
I notice that old examples used
int i = (int)v;
but that won't work with gcc-4.4.5
Right -- probably a "precision loss" error during the void* -> int,
since sizeof(void*)==8 and sizeof(int)==4.
I think the best approach (in 1.3.x) is to use the new fl_intptr_t, e.g.
int i = fl_intptr_t(v);
Or, you could just use a long instead of an int, but that might cause
trouble on non-64bit builds.. which is I think what fl_intptr_t tries
to solve. (See the definition in FL/Fl_Widget.H)
When I first compiled with 64 bit linux the compiler complained about

int i = (int)(v);


but was happy with

long i = (long)(v);

Both Linux and mingw 32 bit are happy with the change to long from
int.

Mingw64 is another fish. AFAIK pointers in win64 are 64 bit pointers
and the compiler issues errors. The work around for that is to use the
compiler switch "fpermissive", then only warnings are issued.

Now in all cases long works so the same code covers all bets. Don't
know about macs though.
Cheers Richard
Ian MacArthur
2013-04-03 20:15:13 UTC
Permalink
Post by Richard Sanders
When I first compiled with 64 bit linux the compiler complained about
int i = (int)(v);
but was happy with
long i = (long)(v);
Greg's suggestion of using fl_intptr_t seems like the most portable option, should work *everywhere* I think!
Post by Richard Sanders
Both Linux and mingw 32 bit are happy with the change to long from
int.
Mingw64 is another fish. AFAIK pointers in win64 are 64 bit pointers
and the compiler issues errors. The work around for that is to use the
compiler switch "fpermissive", then only warnings are issued.
The *nix's (and VxWorks, others...) went from being ILP32, to being LP64, so in both those cases sizeof(long) == sizeof(ptr), but in the 64 bit cases sizeof(int) != sizeof(ptr).

However, in an attempt to preserve as much as possible of their API, MS decided to go from ILP32 to LLP64/IL32, so that in Win64 code sizeof(long) != sizeof(ptr), you need to use sizeof(long long) == sizeof(ptr)

Or... use intptr_t, since sizeof(intptr_t) == sizeof(ptr) everywhere...

And of course sizeof(fl_intptr_t) == sizeof(ptr) everywhere too!
Post by Richard Sanders
Now in all cases long works so the same code covers all bets.
Still think fl_intptr_t is the more portable solution long term.
Post by Richard Sanders
Don't know about macs though.
A Mac is just a *nix box these days...
Ian MacArthur
2013-04-03 20:05:52 UTC
Permalink
Post by Greg Ercolano
Post by marty moore
I notice that old examples used
int i = (int)v;
but that won't work with gcc-4.4.5
Right -- probably a "precision loss" error during the void* -> int,
since sizeof(void*)==8 and sizeof(int)==4.
I think the best approach (in 1.3.x) is to use the new fl_intptr_t, e.g.
int i = fl_intptr_t(v);
Or, you could just use a long instead of an int, but that might cause
trouble on non-64bit builds.. which is I think what fl_intptr_t tries
to solve. (See the definition in FL/Fl_Widget.H)
Ah: right... Yes, I probably have -fpermissive set in my build environment anyway, so don't see this...

I think something based around intptr_t (or fl_intptr_t) is the way to go, since on any host system, regardless of word size, that is "guaranteed" to provide an integral type that is large enough to hold a pointer...

Probably intptr_t would work just about everywhere nowadays I'd guess, since C99 (and is it in C++11 too?) is pretty widely supported now.

But the definition for fl_intptr_t should work *everywhere* so it is probably the most portable option.
marty moore
2013-04-03 20:22:20 UTC
Permalink
Hi,
thanks for all the info! I really appreciate the response.

I tried your suggestions on my system: Debian 6.5 (64 bit), gcc-4.4.5, emacs 23, fltk-1.3.2

I tested the following:

eop op = (eop)fl_intptr_t(v); works fine

int i = (int)v; fails to compile (loss of precision) like you all said.

long li = (long)v; works fine.

long i = (long)(w->argument()); compiles, but always results in '0'. Doesn't w point back to the Fl_Menu_Button that had the menu? I didn't set an argument in the Fl_Menu_Button.

So, I'm going forward with fl_intptr_t, since it works and probally will work in the future.

Thanks again,
Marty
Greg Ercolano
2013-04-03 21:58:25 UTC
Permalink
Post by marty moore
long i = (long)(w->argument()); compiles, but always results in '0'.
Doesn't w point back to the Fl_Menu_Button that had the menu?
Probably depends on if you're setting the callback for the widget,
or the callback for the items.

Each item can have its own callback and userdata.
Also, the Menu widget itself can also have its own callback + userdata.
(The latter is used for items that have no callback of their own, IIRC.)

If you're setting the callback for the widget, then
w->userdata() should have any user_data() you'd set.

But if you're setting the item's individual callbacks,
then to access that item's userdata, your callback would have
to first find the last item picked (with 'mvalue()')
and then access its user_data(), eg:

const Fl_Menu_Item *item = w->mvalue(); // get last picked item
long i = (long)item->user_data(); // get user data for that item

..or something like that.
MacArthur, Ian (Selex ES, UK)
2013-04-04 08:52:01 UTC
Permalink
Post by marty moore
long li = (long)v; works fine.
Yes; this is a good option, everywhere *except* Win64... On Win64, a long is still only 32-bit (everyone else decided that in their 64-bit generation a long would be 64-bits, but MS decided they needed to preserve the sizeof(long) == sizeof(int) relationship that is assumed in the WIN32 API...)

So, probably, fl_intptr_t is the more portable choice.



Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************
Loading...