struct interdependencies

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Russell Shaw

    struct interdependencies

    Hi,

    I have two structs in a header file, and they reference each other,
    causing a compile error. Is there a standard way to deal with this?

    typedef struct {
    ...
    RtAction *actions;
    } RtWidget;

    typedef struct {
    ...
    void (*action)(RtWid get *widget, XEvent *event);
    } RtAction;
  • Robert Gamble

    #2
    Re: struct interdependenci es

    On Mon, 18 Apr 2005 00:07:07 +1000, Russell Shaw wrote:
    [color=blue]
    > Hi,
    >
    > I have two structs in a header file, and they reference each other,
    > causing a compile error. Is there a standard way to deal with this?
    >
    > typedef struct {
    > ...
    > RtAction *actions;
    > } RtWidget;
    >
    > typedef struct {
    > ...
    > void (*action)(RtWid get *widget, XEvent *event);
    > } RtAction;[/color]

    How about this:

    typedef struct RtAction * RtAction;
    typedef struct RtWidget * RtWidget;

    struct RtWidget {
    ...
    RtAction actions;
    };

    struct RtAction {
    ...
    void (*action)(RtWid get widget, XEvent *event);
    };

    Rob Gamble

    Comment

    • Chris Torek

      #3
      Re: struct interdependenci es

      In article <b57aj2-l6a.ln1@main.an atron.com.au>
      Russell Shaw <rjshawN_o@s_pa m.netspace.net. au> wrote:[color=blue]
      >I have two structs in a header file, and they reference each other,
      >causing a compile error. Is there a standard way to deal with this?
      >
      > typedef struct {
      > ...
      > RtAction *actions;
      > } RtWidget;
      >
      > typedef struct {
      > ...
      > void (*action)(RtWid get *widget, XEvent *event);
      > } RtAction;[/color]

      Yes. Stop using typedefs.

      I am serious. They are not buying you much here, and -- by misleading
      you -- are the cause of most of your grief. Typedefs do not define
      types. "struct" keywords define types (when followed by "{", or
      a tag and "{"). You have two type-defining "struct" keywords above.

      You *must* use at least one structure tag here, because structure
      tags can be declared as incomplete types and to be used in "forward
      reference" fashion. Once you have the tags, you do not need the
      typedefs.

      struct RtAction; /* forward-declare the "RtAction" type */
      struct RtWidget; /* forward-declare the "RtWidget" type */

      struct RtWidget { /* re-declare, and now define, the "RtWidget" type */
      ...
      struct RtAction *actions; /* refers to earlier forward-decl */
      };

      struct RtAction { /* define the "RtAction" type */
      ...
      void (*action)(struc t RtWidget *, XEvent *);
      };

      (Obviously you can eliminate at least one of the "forward
      declarations". Since a definition is always also a declaration,
      whichever actual definition comes first -- in this case
      "struct RtWidget {" -- does not need a previous declaration.
      In this *particular* case, you can actually drop both forward
      declarations, but if you were to define the RtAction type
      first, you would need the forward declaration for the RtWidget
      type, due to somewhat silly, albeit "natural", scope rules.)

      Now, if you have some reason you must insist on using those useless
      "typedef" keywords just to avoid writing out C's "type" keyword
      (i.e., "STRangely-spelled User-defined abstraCt Type" or "struct")
      later, note that once you have forward-declared a structure, you
      can then make a typedef alias for it:

      struct forward_declare d_type;

      typedef struct forward_declare d_type another_name_fo r_it;

      /*
      * now:
      * struct forward_declare d_type *p;
      * and:
      * another_name_fo r_it *q;
      * declare p and q as pointers to the incomplete type
      * whose "true name" is "forward_declar ed_type". The
      * identifier "another_name_f or_it" is just an alias for
      * the real name.
      */

      Of course, you can combine the two into one big mess (typedefs
      always make a mess :-) ) with the usual combo deal:

      typedef struct forward_declare d_type another_name_fo r_it;

      The important thing is to use C's "type" keyword (i.e., "struct")
      with a tag, so that you can repeat the name of the type when you
      go to define it later:

      struct forward_declare d_type {
      ... contents ...
      };

      Without the tag, "struct {" starts defining a new, different,
      unnamed type, instead of defining ("completing ") the earlier
      incomplete type.

      Note that:

      typedef struct { ... } alias;

      defines a new type ("struct { ... }") that has no name and can
      never be referred-to again, but then captures the "no-name" type
      with the alias. For discussion purposes, we might refer to the
      first no-name type as "$compiler_gene rated_name$0000 0", using
      "$compiler_gene rated_name$0000 1" for the second, and so on. Then
      we could say that:

      typedef struct { int a; } alias00000;
      typedef struct { int a; } alias00001;

      defines two new structure types, $compiler_gener ated_name$00000
      and $compiler_gener ated_name$00001 , and gives these two names
      (which cannot be spelled out in Standard C, at least, because
      "$" is not a valid identifier) the aliases "alias00000 " and
      "alias00001 ". So even though:

      struct $compiler_gener ated_name$00000 var_x;

      does not work, we can then write:

      alias00000 var_x;

      Of course, this *looks like* two variable names, instead of C's
      usual declaration syntax, but this is the big drawback of typedefs:
      they muck up the syntax. Given:

      void f(x);

      is "x" a typedef-alias or a variable-name? Grab a C89 compiler
      and find out:

      % cat t.c
      #ifdef TY
      typedef struct tag { char *p; } x;
      #endif
      void f(x); /* line 4 */
      void g(void) { f(3); } /* line 5 */
      % cc -ansi -pedantic -c t.c
      t.c:4: warning: parameter names (without types) in function declaration
      % cc -DTY -ansi -pedantic -c t.c
      t.c: In function `g':
      t.c:5: incompatible type for argument 1 of `f'
      %

      The answer is: it is whichever one it is. If x has been declared
      as a typedef-name, it is a type-name, and not a variable name; if
      x has not been declared as a typedef-name, it is a variable name,
      using C89's "implicit int" rule. (GCC produces a warning, which
      is nice of it, since chances are good that this is not what the
      programmer really meant; but this is not a "required diagnostic"
      and some compilers do not warn.)

      (This is fixed in C99, which has dropped the implicit int rule.)

      The fact that typedefs muck up the syntax is the reason that those
      who use typedef almost always invent some sort of spelling rules
      to make the typedefs stand out. In POSIX, this is the "_t" suffix,
      so that we have things like "uid_t" and "mode_t" and "pthread_t" .
      In much other software, one spells a typedef (and nothing else)
      with an initial capital letter, so that:

      GargleBlaster

      is a typedef but:

      mixPanGalacticD rink

      is a function. (HHGttG was on last night. :-) ) In Wind River's
      conventions, typedefs are spelled in all-caps, so that they look
      like macros (like Standard C's "FILE", in "FILE *stdin" and such).

      In case it was not obvious, my preferred solution is "avoid
      typedefs". :-) Of course, as with rules for code optimization,
      it has some exceptions. Instead of "don't do it yet", it is
      more a case of "for sufficiently complicated names, go ahead
      and resort to a typedef, because it makes something clearer."
      The classic situation is the one that comes up with the Standard
      C "signal" function, which takes and returns a pointer to
      a signal-handler:

      /* version 1, without typedef */
      void (*signal(int sig, void (*func)(int)))( int);


      /* version 2, with a typedef */
      typedef void (*SIG_FUNC_PTR) (int);

      SIG_FUNC_PTR signal(int sig, SIG_FUNC_PTR func);

      Version 2 is marginally easier to understand than Version 1,
      primarily because it is now clear that signal() both takes and
      returns a value of one particular (now-named) type: "signal takes
      a new pointer-to-signal-handling-function, and returns the old
      one".

      Note that this does not affect structures, of course, because
      user-defined abstract types ("struct"s) can and should have
      names ("tags"):

      struct zorg *change_evil(in t which, struct zorg *new_evil);

      Here "struct zorg" is the name of the type, just like "int" is the
      name of a type; it just happens to be spelled with the keyword for
      "user-defined-type-name" followed by the user-defined type-name,
      instead of a single word. This is a *good thing*: no special
      typographic convention is required to alert the user (and compiler)
      to the fact that this *is* a user-defined type-name. We do not
      have to write "struct zorg_t" (_t suffix) or "struct Zorg"
      (initial-capital) or "struct ZORG" (all-caps), because the "struct"
      keyword does the trick.

      Executive summary: "typedef"s bad. Avoid. :-)
      --
      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

      • Russell Shaw

        #4
        Re: struct interdependenci es

        Robert Gamble wrote:[color=blue]
        > On Mon, 18 Apr 2005 00:07:07 +1000, Russell Shaw wrote:
        >
        >[color=green]
        >>Hi,
        >>
        >>I have two structs in a header file, and they reference each other,
        >>causing a compile error. Is there a standard way to deal with this?
        >>
        >> typedef struct {
        >> ...
        >> RtAction *actions;
        >> } RtWidget;
        >>
        >> typedef struct {
        >> ...
        >> void (*action)(RtWid get *widget, XEvent *event);
        >> } RtAction;[/color]
        >
        >
        > How about this:
        >
        > typedef struct RtAction * RtAction;
        > typedef struct RtWidget * RtWidget;
        >
        > struct RtWidget {
        > ...
        > RtAction actions;
        > };
        >
        > struct RtAction {
        > ...
        > void (*action)(RtWid get widget, XEvent *event);
        > };
        >
        > Rob Gamble[/color]

        That works well, tho i used:

        typedef struct RtAction RtAction;
        typedef struct RtWidget RtWidget;

        Comment

        • Russell Shaw

          #5
          Re: struct interdependenci es

          Chris Torek wrote:[color=blue]
          > In article <b57aj2-l6a.ln1@main.an atron.com.au>
          > Russell Shaw <rjshawN_o@s_pa m.netspace.net. au> wrote:
          >[color=green]
          >>I have two structs in a header file, and they reference each other,
          >>causing a compile error...[/color][/color]
          ....[color=blue]
          > Executive summary: "typedef"s bad. Avoid. :-)[/color]

          Hmm, thanks for that somewhat long post. You should put it on

          Comment

          • Emmanuel Delahaye

            #6
            Re: struct interdependenci es

            Russell Shaw wrote on 17/04/05 :[color=blue]
            > Hi,
            >
            > I have two structs in a header file, and they reference each other,
            > causing a compile error. Is there a standard way to deal with this?
            >
            > typedef struct {
            > ...
            > RtAction *actions;
            > } RtWidget;
            >
            > typedef struct {
            > ...
            > void (*action)(RtWid get *widget, XEvent *event);
            > } RtAction;[/color]

            Yes, as long as you deal with pointers...

            typedef struct RtAction RtAction;

            typedef struct {
            ...
            RtAction *actions;
            } RtWidget;

            struct RtAction {
            ...
            void (*action)(RtWid get *widget, XEvent *event);
            } RtAction;

            --
            Emmanuel
            The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
            The C-library: http://www.dinkumware.com/refxc.html

            I once asked an expert COBOL programmer, how to
            declare local variables in COBOL, the reply was:
            "what is a local variable?"

            Comment

            • Keith Thompson

              #7
              Re: struct interdependenci es

              Russell Shaw <rjshawN_o@s_pa m.netspace.net. au> writes:[color=blue]
              > Robert Gamble wrote:[color=green]
              >> On Mon, 18 Apr 2005 00:07:07 +1000, Russell Shaw wrote:[color=darkred]
              >>>I have two structs in a header file, and they reference each other,
              >>>causing a compile error. Is there a standard way to deal with this?
              >>>
              >>> typedef struct {
              >>> ...
              >>> RtAction *actions;
              >>> } RtWidget;
              >>>
              >>> typedef struct {
              >>> ...
              >>> void (*action)(RtWid get *widget, XEvent *event);
              >>> } RtAction;[/color]
              >> How about this:
              >> typedef struct RtAction * RtAction;
              >> typedef struct RtWidget * RtWidget;
              >> struct RtWidget {
              >> ...
              >> RtAction actions;
              >> };
              >> struct RtAction {
              >> ...
              >> void (*action)(RtWid get widget, XEvent *event);
              >> };
              >> Rob Gamble[/color]
              >
              > That works well, tho i used:
              >
              > typedef struct RtAction RtAction;
              > typedef struct RtWidget RtWidget;[/color]

              Good for you. I was just about to complain that Robert's solution,
              though valid, is a bit obfuscated. He has the name RtAction referring
              both to a type (the struct) and to a pointer to that type (the
              typedef). They're different things, so they should have different
              names.

              Hiding a pointer type in a typedef is seldom a good idea unless you
              really want to hide it (i.e., you don't want the code that uses the
              type to know or care that it's a pointer).

              Chris Torek's post about avoiding typedefs is also good advice.

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

              • Keith Thompson

                #8
                Re: struct interdependenci es

                Chris Torek <nospam@torek.n et> writes:
                [...][color=blue]
                > Now, if you have some reason you must insist on using those useless
                > "typedef" keywords just to avoid writing out C's "type" keyword
                > (i.e., "STRangely-spelled User-defined abstraCt Type" or "struct")
                > later, note that once you have forward-declared a structure, youn
                > can then make a typedef alias for it:[/color]
                [...]

                I agree with what you wrote (though of course one must sometimes deal
                with code written by people who do like typedefs). Just one quibble.
                A struct declaration does create a type, but it's only one of several
                kinds of types (though the most flexible kind). Structs, unions,
                enums, pointers, and arrays are all types that can be created by
                declarations. What you wrote above implies that a struct declaration
                is *the* way to create a type in C.

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

                • Christian Bau

                  #9
                  Re: struct interdependenci es

                  In article <pan.2005.04.17 .15.22.18.55262 2@gmail.com>,
                  Robert Gamble <rgamble99@gmai l.com> wrote:
                  [color=blue]
                  > On Mon, 18 Apr 2005 00:07:07 +1000, Russell Shaw wrote:
                  >[color=green]
                  > > Hi,
                  > >
                  > > I have two structs in a header file, and they reference each other,
                  > > causing a compile error. Is there a standard way to deal with this?
                  > >
                  > > typedef struct {
                  > > ...
                  > > RtAction *actions;
                  > > } RtWidget;
                  > >
                  > > typedef struct {
                  > > ...
                  > > void (*action)(RtWid get *widget, XEvent *event);
                  > > } RtAction;[/color]
                  >
                  > How about this:
                  >
                  > typedef struct RtAction * RtAction;
                  > typedef struct RtWidget * RtWidget;[/color]

                  This might come up as an interview question.

                  What's wrong with the following code?

                  typedef struct RtAction * RtAction;
                  typedef struct RtWidget * RtWidget;

                  Comment

                  • Robert Gamble

                    #10
                    Re: struct interdependenci es

                    On Sun, 17 Apr 2005 20:45:05 +0000, Keith Thompson wrote:
                    [color=blue]
                    > Russell Shaw <rjshawN_o@s_pa m.netspace.net. au> writes:[color=green]
                    >> Robert Gamble wrote:[color=darkred]
                    >>> On Mon, 18 Apr 2005 00:07:07 +1000, Russell Shaw wrote:
                    >>>>I have two structs in a header file, and they reference each other,
                    >>>>causing a compile error. Is there a standard way to deal with this?
                    >>>>
                    >>>> typedef struct {
                    >>>> ...
                    >>>> RtAction *actions;
                    >>>> } RtWidget;
                    >>>>
                    >>>> typedef struct {
                    >>>> ...
                    >>>> void (*action)(RtWid get *widget, XEvent *event);
                    >>>> } RtAction;
                    >>> How about this:
                    >>> typedef struct RtAction * RtAction;
                    >>> typedef struct RtWidget * RtWidget;
                    >>> struct RtWidget {
                    >>> ...
                    >>> RtAction actions;
                    >>> };
                    >>> struct RtAction {
                    >>> ...
                    >>> void (*action)(RtWid get widget, XEvent *event);
                    >>> };
                    >>> Rob Gamble[/color]
                    >>
                    >> That works well, tho i used:
                    >>
                    >> typedef struct RtAction RtAction;
                    >> typedef struct RtWidget RtWidget;[/color]
                    >
                    > Good for you. I was just about to complain that Robert's solution,
                    > though valid, is a bit obfuscated. He has the name RtAction referring
                    > both to a type (the struct) and to a pointer to that type (the typedef).
                    > They're different things, so they should have different names.
                    >
                    > Hiding a pointer type in a typedef is seldom a good idea unless you
                    > really want to hide it (i.e., you don't want the code that uses the type
                    > to know or care that it's a pointer).
                    >
                    > Chris Torek's post about avoiding typedefs is also good advice.[/color]

                    Agreed on all 3 points, I was trying to demonstrate the concept but should
                    have changed the names for clarification, thanks for pointing this out.

                    Rob Gamble

                    Comment

                    • Old Wolf

                      #11
                      Re: struct interdependenci es

                      Chris Torek wrote:[color=blue]
                      >
                      > Stop using typedefs.
                      >[/color]

                      One problem I find when using struct tags rather than typedefs:

                      struct gargleblaster
                      {
                      int whatever;
                      };

                      void foo(struct garbleglaster *ptr);

                      will raise no compiler errors, until later on when I try to
                      define foo() and assign ptr, then it will tell me that ptr's
                      type is incompatible with what I'm assigning it to. Whereas:

                      typedef struct { int whatever; } hyperspacebypas s;
                      void bar(hypespaceby pass *ptr);

                      will cause a compiler error right then and there.

                      Comment

                      • Russell Shaw

                        #12
                        Re: struct interdependenci es

                        Old Wolf wrote:[color=blue]
                        > Chris Torek wrote:
                        >[color=green]
                        >>Stop using typedefs.
                        >>[/color]
                        >
                        > One problem I find when using struct tags rather than typedefs:
                        >
                        > struct gargleblaster
                        > {
                        > int whatever;
                        > };
                        >
                        > void foo(struct garbleglaster *ptr);
                        >
                        > will raise no compiler errors, until later on when I try to
                        > define foo() and assign ptr, then it will tell me that ptr's
                        > type is incompatible with what I'm assigning it to. Whereas:
                        >
                        > typedef struct { int whatever; } hyperspacebypas s;
                        > void bar(hypespaceby pass *ptr);
                        >
                        > will cause a compiler error right then and there.[/color]

                        Can't see why the latter should be in error. I was always using the
                        same thing in gcc.

                        Comment

                        • Keith Thompson

                          #13
                          Re: struct interdependenci es

                          Russell Shaw <rjshawN_o@s_pa m.netspace.net. au> writes:[color=blue]
                          > Old Wolf wrote:[color=green]
                          >> Chris Torek wrote:
                          >>[color=darkred]
                          >>>Stop using typedefs.
                          >>>[/color]
                          >> One problem I find when using struct tags rather than typedefs:
                          >> struct gargleblaster
                          >> {
                          >> int whatever;
                          >> };
                          >> void foo(struct garbleglaster *ptr);
                          >> will raise no compiler errors, until later on when I try to
                          >> define foo() and assign ptr, then it will tell me that ptr's
                          >> type is incompatible with what I'm assigning it to. Whereas:
                          >> typedef struct { int whatever; } hyperspacebypas s;
                          >> void bar(hypespaceby pass *ptr);
                          >> will cause a compiler error right then and there.[/color]
                          >
                          > Can't see why the latter should be in error. I was always using the
                          > same thing in gcc.[/color]

                          It's a (deliberate) typo: "hyperspacebypa ss" vs. "hypespacebypas s".

                          --
                          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 Croughton

                            #14
                            Re: struct interdependenci es

                            On Sun, 17 Apr 2005 20:45:05 GMT, Keith Thompson
                            <kst-u@mib.org> wrote:
                            [color=blue]
                            > Hiding a pointer type in a typedef is seldom a good idea unless you
                            > really want to hide it (i.e., you don't want the code that uses the
                            > type to know or care that it's a pointer).[/color]

                            It's something the authors of zlib did, with fatal results.

                            typedef void *voidp; /* looks harmless enough */

                            extern ... gzwrite(..., const voidp buf, ...);

                            func(const void* buffer)
                            {
                            gzwrite(..., buffer, ...); /* oops! */
                            }

                            I passed a pointer to constant void to gzwrite which expected a const
                            pointer to void, and there was nothing (short of putting in a (voidp)
                            cast) I could do to make it compile with gcc 3.x. gcc 2.95.x didn't
                            detect any inconsistency.. .

                            (The latest version of zlib doesn't do that -- it defines voidpc as
                            typedef const void *voidpc!)
                            [color=blue]
                            > Chris Torek's post about avoiding typedefs is also good advice.[/color]

                            I would disagree about avoiding them totally, but certainly the practice
                            of making "pointer types" is dangerous...

                            Chris C

                            Comment

                            • Chris Croughton

                              #15
                              Re: struct interdependenci es

                              On Mon, 18 Apr 2005 11:40:30 +1000, Russell Shaw
                              <rjshawN_o@s_pa m.netspace.net. au> wrote:
                              [color=blue]
                              > Old Wolf wrote:[color=green]
                              >> Chris Torek wrote:
                              >>[color=darkred]
                              >>>Stop using typedefs.[/color]
                              >>
                              >> One problem I find when using struct tags rather than typedefs:
                              >>
                              >> struct gargleblaster
                              >> {
                              >> int whatever;
                              >> };
                              >>
                              >> void foo(struct garbleglaster *ptr);
                              >>
                              >> will raise no compiler errors, until later on when I try to
                              >> define foo() and assign ptr, then it will tell me that ptr's
                              >> type is incompatible with what I'm assigning it to. Whereas:
                              >>
                              >> typedef struct { int whatever; } hyperspacebypas s;
                              >> void bar(hypespaceby pass *ptr);
                              >>
                              >> will cause a compiler error right then and there.[/color]
                              >
                              > Can't see why the latter should be in error. I was always using the
                              > same thing in gcc.[/color]

                              Did you spot the error in the first example using struct? The error is
                              the same (mistyped identifier), but in the first case the compiler will
                              just say "OK, I'm using a structure with incomplete type, no problem"
                              until you try to access something in that structure or pass a pointer to
                              a structure with the correct name to the function. And then it can take
                              ages to spot cause of the error, because it's subtle and removed from
                              the place of the actual error. That's why using the typedef is better,
                              the compiler will spot immediately "hypespacebypas s is not a type".

                              Chris C

                              Comment

                              Working...