From: scs@adam.mit.edu (Steve Summit)
Subject: Re: Incomplete structure declarations: only within structs?
Organization: none, at the moment
Date: Thu, 8 Oct 1992 17:44:25 GMT

In article <FARRELL.92Oct8114020@fjord.reo.dec.com>, John M.
Farrell (farrell@fjord.reo.dec.com) replies to an article of
Chris Torek's which I hadn't even seen yet (was there a change of
newsgroups involved?), discussing incomplete structure declarations:

> ...I haven't found a way to forward reference a typedef in the same way.
> 
> In foo.h I have:
> 
>     typedef struct foo
>     {
>         size_t         len;
>         unsigned char *buf;
>     } FOO;
> 
> In bar.h I want:
> 
>     extern void bar (FOO *f);
> 
> And in bar.c:
> 
>     #include "bar.h"
>     #include "foo.h"
>     void bar (FOO *f)
>     {...
>
> Is there anything I can add to bar.h to ensure that the FOO in the prototype
> refers to the typedef defined later, in foo.h ?

Since bar.h depends on foo.h (confusing names, by the way), I
would make the dependence explicit by having bar.h #include foo.h .
This, of course, opens up the nested header file can of worms;
see the comp.lang.c frequently-asked questions (FAQ) list for a
brief synopsis.

As it stands, it's a syntax error for bar.c to #include bar.h
first.  This could almost be fixed by changing bar.h to read

	typedef struct foo FOO;
	extern void bar(FOO *f);

, or, if you really wanted to, 

	struct foo;
	typedef struct foo FOO;
	extern void bar(FOO *f);

, but neither of these guarantee in and of themselves "that the
FOO in the prototype refers to the typedef defined later, in
foo.h", and furthermore (and more significantly), defining the
FOO typedef in bar.h makes the later definition in foo.h an error.

Another approach is just to skip the typedefs.  Then bar.h
is back to

	struct foo;
	extern void bar(struct foo *f);

(which is equivalent to the fragment in Chris's earler post that
John was responding to).

I'm not sure why people are so eager to define typedefs for
structs.  It is, I suppose, a pattern followed by many textbook
authors, and it is also done automatically for you in C++.
However, if you insist on defining typedefs for structures, you
can run into several problems:

     1.	This one (bar.h can't be #included before foo.h because
	it uses FOO)

     2.	Nested header files, if you use them to solve problem 1,
	and if you consider them a problem (I don't)

     3.	It's easy to fall into a trap in which you think you
	can't declare a self-referential structure (e.g. a linked
	list), or a pair of mutually-referential structures (see
	the FAQ list again)

     4.	To solve problem 3, you have to use two names for the
	structure (a struct tag name and a typedef name), which
	isn't the end of the world, but it means that you either
	have to use foo for both the tag and the typedef, or
	differentiate between foo and FOO, or between foo and
	foo_t, which upsets some people

     5.	I'm pretty sure I've come across other problems, but I
	forget them right now.

"But wait," you say.  "typedefs are a Good Thing, because they
promote Data Abstraction."  Perhaps.  In my poor addled brain,
the only thing that a typedef for a struct usually buys you is
that you don't have to type the seven characters "struct " when
declaring objects, pointers, or functions of the struct type.
If you promise not to peek inside of a struct foo,

	struct foo f;

is just about as abstract as

	FOO f;

If I'm interested in enforcing the abstraction, I omit the
declaration of the structure in the public header file #included
by consumers; all they can see is things like

	extern struct foo *allocfoo(void);
	extern void initfoo(struct foo *);

, and I've got as much type safety (and, with the right mindset [1],
as much abstraction), as if I'd hidden struct foo behind typedef FOO.

					Steve Summit
					scs@adam.mit.edu

Note 1.  The "mindset" is obviously that all abstract types are
implemented as structs.  (In practice, I find this mindset quite
easy to uphold, especially in small or self-contained projects.)
If you want to abstract away the difference between structs,
pointers-to-struct, and/or int indices into arrays-of-struct
(e.g. Unix file descriptors), or between structs, ints, doubles,
and longs (or other types), then typedefs are indeed valuable,
especially if you expect that the decisions (e.g. the choices
between several incompatible implementation paths) will change
during development.  (For example, if you've defined a typedef
for a special date/time datatype, calling code which used the
typedef would need only recompilation, and not rewriting, if the
data/time datatype changed from a long int to a double to a
struct to a pointer-to-struct.)
Newsgroups: comp.lang.c