In article <JK******************@newsb.telia.net>
Michael Brennan <br************@gmail.comwrote:
>
I have a menu_item structure containing an union.
func is used if the menu item should use a callback,
and submenu if a popupmen should be shown.
struct menu_item {
enum { function, popup } type;
union {
int (*func)(int);
struct menu_item *submenu;
} action;
};
The problem is that I want to use an initializer to
initialize this structure.
But if I understood what I read in the FAQ right, you can't initialize
the unions in C89, only in C99 with ``designated initializers''.
More precisely, you can only initialize the *first* union member.
So if you have a function fn:
int fn(int);
struct menu_item m_fn = { function, { fn } };
which works in both C89 and C99; but if you have a popup "pu" you
need C99's:
struct menu_item pu = { popup, { .submenu = &m_fn } };
There is no completely-satisfactory solution to this problem, but
there is a method that, if you can use it at all, is likely to
work on "real implementations". Namely, write variants of the
"struct" that have each of the union members "first". In this
case:
enum menu_item_type { function, popup };
struct menu_item_if_function {
enum menu_item_type type; /* should be "function" */
union {
int (*func)(int);
struct menu_item *submenu;
} action;
};
struct menu_item_if_popup {
enum menu_item_type type; /* should be "popup" */
union {
struct menu_item *submenu;
int (*func)(int);
} action;
};
Now you can do this:
int fn(int);
struct menu_item_if_function m_fn = { function, { fn } };
struct menu_item_if_popup m_pu = { popup, { &m_fn } };
At some point, you pick one of the variants (arbitrarily) to be
the "main" one for use in the code. Let us say that in this case
you choose "menu_item_if_function" (which you might then rename
to remove the "_if_function" suffix, but for the moment I will
leave it in). Then you can make an array of pointers to the
various initialized objects (m_fn and m_pu above), and cast the
"wrong type" pointers (and hope very hard that the compiler has
not done bizarre things, so that this actually works):
struct menu_item_if_function *array[] = {
&m_fn,
(struct menu_item_if_function *)&m_pu,
NULL
};
>Or how about using a void pointer, and casting it between a fuction
pointer or a structure pointer?
This fails on some Real Implementations, where "int (*)(int)" is
128 bytes long (function pointers have a lot of stuff in them) but
"void *" is only 32 bytes long (data pointers have only a little
stuff in them).
The "multiple variants of the struct" trick is much more likely to
work: it depends only on the compiler making the layout of each
struct-containing-a-union independent of the order of the union
members. By having all the same members in each "sub-union", we
guarantee that they all have *enough* room (and alignment) for
*all* their members, and they should generally all have the same
size and layout. Then, since all the "struct"s have the same
members (in terms of size and layout), all the "struct"s should
have the same alignment and so on as well.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.