Intializing struct containing an union

Discussion in 'C Programming' started by Michael Brennan, Jul 24, 2006.

  1. 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''.
    I could use the C99 feature (I think, I haven't really checked it out),
    but I usually try to only code in C89, so I wonder if there is some
    good way to solve this in C89 (not absolutely neccessary though).
    I could of course just remove the union and only use two regular
    variables, func and submenu, and use just one of them depending on
    the types.
    Or how about using a void pointer, and casting it between a fuction
    pointer or a structure pointer?

    Any comments are appreciated!

    /Michael Brennan
     
    Michael Brennan, Jul 24, 2006
    #1
    1. Advertising

  2. Michael Brennan

    Chris Torek Guest

    In article <JK2xg.10322$>
    Michael Brennan <> wrote:
    >
    >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.
     
    Chris Torek, Jul 25, 2006
    #2
    1. Advertising

  3. Michael Brennan

    Jack Klein Guest

    On Mon, 24 Jul 2006 12:08:09 GMT, Michael Brennan
    <> wrote in comp.lang.c:

    >
    > 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''.


    You understand it incorrectly. In C89 you may initialize the first
    member of a union. You may not initialize any other member.

    > I could use the C99 feature (I think, I haven't really checked it out),
    > but I usually try to only code in C89, so I wonder if there is some
    > good way to solve this in C89 (not absolutely neccessary though).
    > I could of course just remove the union and only use two regular
    > variables, func and submenu, and use just one of them depending on
    > the types.
    > Or how about using a void pointer, and casting it between a fuction
    > pointer or a structure pointer?


    There is absolutely no defined conversion whatsoever between pointers
    to functions of any signature, and pointers to objects of any type.
    There are platforms where the two are of different sizes.

    Do you have more than 1,000,000 of these? If not, or even if so,
    consider changing it to:

    struct menu_item
    {
    int (*func)(int);
    struct menu_item *submenu;
    }

    And initialize like this:

    struct menu_item top_menu_tree [] =
    {
    { func1, NULL },
    { NULL, file_menu_tree },
    { NULL, edit_menu_tree },
    { NULL, search_menu_tree },
    { NULL, tool_menu_tree },
    { show_help, NULL }
    };

    The enumerator is no longer needed, a simple test for NULL will do.

    Consider that on typical desktop platforms today (i.e., 32-bit),
    sizeof (int) == sizeof (void*) == sizeof (int (*)(int). Even if your
    compiler provides an option to instantiate enumerated types in the
    smallest integer type that can hold the value, it is quite possible
    that alignment padding will make your structure as large as mine.

    And even if your struct ends up, let's say, 3 bytes shorter than mine,
    how many of them will your program actually have? What price
    correctness and portability?

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://c-faq.com/
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
     
    Jack Klein, Jul 25, 2006
    #3
  4. Chris Torek <> writes:
    [...]
    > 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).


    Not that it's relevant to your point, but do you really mean 128 and
    32 bytes rather than bits?

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jul 25, 2006
    #4
  5. Michael Brennan

    Chris Torek Guest

    >Chris Torek <> writes:
    >> 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).


    In article <>
    Keith Thompson <> wrote:
    >Not that it's relevant to your point, but do you really mean 128 and
    >32 bytes rather than bits?


    Not really sure; I was thinking of the AS/400, where pointers are
    "ridiculously large".
    --
    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.
     
    Chris Torek, Aug 7, 2006
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Matt Garman
    Replies:
    1
    Views:
    694
    Matt Garman
    Apr 25, 2004
  2. Chris Fogelklou
    Replies:
    36
    Views:
    1,447
    Chris Fogelklou
    Apr 20, 2004
  3. Peter Dunker

    union in struct without union name

    Peter Dunker, Apr 26, 2004, in forum: C Programming
    Replies:
    2
    Views:
    932
    Chris Torek
    Apr 26, 2004
  4. Intializing Pointer Array

    , Dec 23, 2006, in forum: C Programming
    Replies:
    11
    Views:
    531
    Random832
    Dec 24, 2006
  5. Frank B.
    Replies:
    1
    Views:
    276
    Howard
    Sep 18, 2006
Loading...

Share This Page