Intializing struct containing an union

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Michael Brennan

    #1

    Intializing struct containing an union


    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

  • Jack Klein

    #2
    Re: Intializing struct containing an union

    On Mon, 24 Jul 2006 12:08:09 GMT, Michael Brennan
    <brennan.brisad @gmail.comwrote 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_tre e },
    { 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.l earn.c-c++

    Comment

    • Chris Torek

      #3
      Re: Intializing struct containing an union

      In article <JK2xg.10322$E0 2.3473@newsb.te lia.net>
      Michael Brennan <brennan.brisad @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_fu nction {
      enum menu_item_type type; /* should be "function" */
      union {
      int (*func)(int);
      struct menu_item *submenu;
      } action;
      };
      struct menu_item_if_po pup {
      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_fu nction m_fn = { function, { fn } };
      struct menu_item_if_po pup 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_f unction" (which you might then rename
      to remove the "_if_functi on" 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_fu nction *array[] = {
      &m_fn,
      (struct menu_item_if_fu nction *)&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.

      Comment

      • Keith Thompson

        #4
        Re: Intializing struct containing an union

        Chris Torek <nospam@torek.n etwrites:
        [...]
        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_Keit h) kst-u@mib.org <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.

        Comment

        • Chris Torek

          #5
          Re: Intializing struct containing an union

          >Chris Torek <nospam@torek.n etwrites:
          >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 <ln7j22thxu.fsf @nuthaus.mib.or g>
          Keith Thompson <kst-u@mib.orgwrote:
          >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
          "ridiculous ly 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.

          Comment

          Working...