[comp.lang.c] Recursive #includes

samperi@marob.MASA.COM (Dominick Samperi) (02/26/89)

Are recursive #includes hazardous to your software's health? 

I'm working on a system where each header file has the form:

<contents of foo.h>:

#ifndef H_FOO
#define H_FOO

defines, declarations, etc.

#endif

This scheme permits header files to be included two or more times, even
recursively, where the second and subsequent includes of the same file
will have no effect. (That is, a header file A can include another header
file B, which in turn includes A, but since H_A will be defined when A
is included the first time, the second include of A will have no effect,
and the recursion will end.)

Although this trick will avoid the common problems caused by including
a header file more than once, I suspect that it will also encourage
the inclusion of files that are not even needed (since there is
no chance for multiple definitions, if there is some chance that the
definitions may be needed, what the heck). Furthermore, permitting
recursive includes may tend to smear the separation between modular
components of the software system, and designing a correct makefile
will be a mess.

These problems, together with the discovery that some preprocessors
consider any recursive include to be a fatal error, have motivated me
to discontinue the use of this trick, and discontinue the use of
recursive includes as well.

Perhaps others could comment on this.
-- 
Dominick Samperi -- ESCC
samperi@marob.masa.com
uunet!hombre!samperi

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/26/89)

In article <570@marob.MASA.COM> samperi@marob.MASA.COM (Dominick Samperi) writes:
>#ifndef H_FOO
>#define H_FOO
>defines, declarations, etc.
>#endif

That is a good, commonly used scheme.

>I suspect that it will also encourage the inclusion of files that are
>not even needed ...

That hasn't been my experience.

>Furthermore, permitting recursive includes may tend to smear the
>separation between modular components of the software system, and
>designing a correct makefile will be a mess.

If a module's interface depends on another's, then necessarily you
have to #include the other module's interface definition just to
properly declare this one's.  Of course most modules (packages)
should not have interfaces that are tied to others, but sometimes
it is necessary.

It's easy to get the Makefile correct; just declare that a header
depends on the others that it #includes.

>These problems, together with the discovery that some preprocessors
>consider any recursive include to be a fatal error, have motivated me
>to discontinue the use of this trick, and discontinue the use of
>recursive includes as well.

Any such preprocessor is badly broken.  K&R 1st Edition imposes no
such constraint.  How about telling us what brands of compiler have
this problem, so we can avoid purchasing them?

samperi@marob.MASA.COM (Dominick Samperi) (02/27/89)

In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>It's easy to get the Makefile correct; just declare that a header
>depends on the others that it #includes.

The makefile that we are currently using is structured this way, and
I suspect that this may be the reason that make (actually VMS MMS) 
takes forever to get started (i.e., go through the dependencies).

>Any such preprocessor is badly broken [i.e., one which does not permit
>recursive includes].  K&R 1st Edition imposes no
>such constraint.  How about telling us what brands of compiler have
>this problem...

I'm working with the Oasis port of AT&T's C++ translator to VMS. Their
preprocesor is named GCPP (could this be the GNU C preprocesor?). By
the way, I couldn't find any comments about recursive includes in
K&R, so I guess this means they are permitted?
-- 
Dominick Samperi -- ESCC
samperi@marob.masa.com
uunet!hombre!samperi

leo@philmds.UUCP (Leo de Wit) (02/27/89)

In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
    []
|>Furthermore, permitting recursive includes may tend to smear the
|>separation between modular components of the software system, and
|>designing a correct makefile will be a mess.
|
|If a module's interface depends on another's, then necessarily you
|have to #include the other module's interface definition just to
|properly declare this one's.  Of course most modules (packages)
|should not have interfaces that are tied to others, but sometimes
|it is necessary.
|
|It's easy to get the Makefile correct; just declare that a header
|depends on the others that it #includes.
|
    []

This is not correct, as it is never correct to declare make dependencies
between source files (this includes header files as well). It may very
well get you into trouble, as I'll show.

Regarding make dependencies I tend to use the rule that an update of a
dependency's source must imply the update of the dependency's target.
A common (though understandable) mistake is to declare dependencies
between C source files and their header files: there is none (at least
not in the sense of Make). The real dependency is between the header
file(s) and the preprocessed C file! (the C _source_ does not change
due to a change in the header file, the preprocessed source and hence
the corresponding object _does_).

The solution of the multilevel - and perhaps recursive - includes is
not that difficult: find, given a header file, recursively all header
files it includes; if you encounter the same header file in the list,
ignore it. The list of include files is so to speak the transitive
closure with respect to inclusion. Now declare a macro that consist of
the list of header file names a given header file (recursively)
includes, and use that in dependencies instead of the original given
header file.  (Side issue: of course you must treat conditional
includes as unconditional, since most makes cannot handle correctly a
change of an on the commandline supplied macro (though Sun's make
_does_); you'll have to manually touch sources / remove objects to
force a recompile.  Note that, if you want to do that conditional stuff
correctly too, the list may well dynamically change due to a C macro
change).

And now for the promised trouble:

Let's say we have a C file file.c that includes a header file1.h. This
header also includes a header: file2.h (you guessed it 8-). Now whether
or not file2.h includes file1.h (or possible other ones too), if
your makefile contains

file1.h : file2.h

you're bound to have trouble. Suppose file1.h has been stored in an 
SCCS directory, and you got out a copy for edit. You made a few changes
to file1.h, then to file2.h, and then said 'make'. Your edited copy
will be clobbered by the old one fetched by SCCS, using an implicit
make rule !! However, some clever SCCS'es - notably Sun's -  refuse to
write to a writable file, consider it a 'source fetched for edit', so
that Make will abort; some others most certainly do.
Have fun ...

   Leo.

ttwang@polyslo.CalPoly.EDU (Thomas Wang) (02/27/89)

In article <573@marob.MASA.COM> samperi@marob.masa.com (Dominick Samperi) writes:
>In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>>It's easy to get the Makefile correct; just declare that a header
>>depends on the others that it #includes.

>The makefile that we are currently using is structured this way, and
>I suspect that this may be the reason that make (actually VMS MMS) 
>takes forever to get started (i.e., go through the dependencies).

There is another semi-automatic way for figuring out the dependencies.

For every .h file, put in the following stuff:

/* foo.h */
#ifndef FOOH
#define FOOH
/* !actual include! foo.h */

...

#endif

Run the .c file through CPP, then grep the output file for '!actual include!'.
This way, the dependencies can be obtained relatively painlessly.


 -Thomas Wang ("I am, therefore I am."
                 - Akira               )

                                                     ttwang@polyslo.calpoly.edu

leo@philmds.UUCP (Leo de Wit) (02/27/89)

In article <573@marob.MASA.COM> samperi@marob.masa.com (Dominick Samperi) writes:
|In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|>It's easy to get the Makefile correct; just declare that a header
|>depends on the others that it #includes.
|
|The makefile that we are currently using is structured this way, and
|I suspect that this may be the reason that make (actually VMS MMS) 
|takes forever to get started (i.e., go through the dependencies).

Another might be the fact that you're using MMS; we have a product that
has to be generated both for Ultrix and VMS, and could keep the
makefiles fairly system independent. MMS/CMS takes a lot of time to get
started, as compared to Make/SCCS. Another point: Although the claim is
that MMS is, except for some syntax additions, the same as Make, I
found out it treated macros differently.

|>Any such preprocessor is badly broken [i.e., one which does not permit
|>recursive includes].  K&R 1st Edition imposes no
|>such constraint.  How about telling us what brands of compiler have
|>this problem...
|
|I'm working with the Oasis port of AT&T's C++ translator to VMS. Their
|preprocesor is named GCPP (could this be the GNU C preprocesor?). By
|the way, I couldn't find any comments about recursive includes in
|K&R, so I guess this means they are permitted?

As Dough said, unless it's broken, yes. Of course you have to do the
inclusion conditionally (or did you mean 'nested' instead of
'recursively' ?), since nesting can only occur up to an
implementation-defined limit (which must be at least 8, if I may
believe my - old - copy of the dpAns).

    Leo.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/28/89)

In article <573@marob.MASA.COM> samperi@marob.masa.com (Dominick Samperi) writes:
>By the way, I couldn't find any comments about recursive includes in
>K&R, so I guess this means they are permitted?

K&R 1st Edition, Appendix A, Section 12.2:
	A compiler control line of the form
		#include "filename"
	causes the replacement of the line by the entire contents
	of the file "filename".  ...
	#include's may be nested.

That doesn't seem ambiguous to me.  "causes" should be read as
a requirement on the implementation.  "may" gives the programmer
specific license (in case there was any question).

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/28/89)

In article <964@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>|It's easy to get the Makefile correct; just declare that a header
>|depends on the others that it #includes.
>This is not correct, as it is never correct to declare make dependencies
>between source files (this includes header files as well).

Sorry, but it IS technically correct.  Dependence is a transitive
property.  The only practical problem you could run into here occurs
when your version of "make" barfs on cyclic dependencies caused by
mutually-dependent headers.  If you don't have recursion among the
headers, though, you should be safe.

"make" has an inherent design problem in that it doesn't properly
distinguish between logical and physical dependencies.  That can
indeed cause some strange behavior and is responsible for the "FRC"
kludge one often sees in fancier makefiles.

The make/SCCS behavior you describe is clearly erroneous; a later-
modified checked-out file should never be clobbered by retrieval
from the archive.  I think this behavior occurs because of the
redundancy introduced in the default rules in an attempt to
compensate for "make" not getting the logical transitivity right.

djones@megatest.UUCP (Dave Jones) (02/28/89)

From article <964@philmds.UUCP>, by leo@philmds.UUCP (Leo de Wit):

> ... it is never correct to declare make dependencies
> between source files ...


y.tab.c: gram.y


   :-)  Dave

prc@maxim.ERBE.SE (Robert Claeson) (02/28/89)

In article <573@marob.MASA.COM>, samperi@marob.MASA.COM (Dominick Samperi) writes:

> I'm working with the Oasis port of AT&T's C++ translator to VMS. Their
> preprocesor is named GCPP (could this be the GNU C preprocesor?). 

As far as I know, Oasis (Oasys?) is only a reseller for Glockenspiel
of Ireland's C++ ports. These are based on the AT&T translators, but
with lots of bug fixes and better documentation. So GCPP really stands
for "Glockenspiel C PreProcessor".
-- 
Robert Claeson, ERBE DATA AB, P.O. Box 77, S-175 22 Jarfalla, Sweden
Tel: +46 (0)758-202 50  Fax: +46 (0)758-197 20
EUnet:   rclaeson@ERBE.SE               uucp:   {uunet,enea}!erbe.se!rclaeson
ARPAnet: rclaeson%ERBE.SE@uunet.UU.NET  BITNET: rclaeson@ERBE.SE

djones@megatest.UUCP (Dave Jones) (02/28/89)

From article <9736@smoke.BRL.MIL>, by gwyn@smoke.BRL.MIL (Doug Gwyn ):
> In article <964@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>>In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>>|It's easy to get the Makefile correct; just declare that a header
>>|depends on the others that it #includes.
>>This is not correct, as it is never correct to declare make dependencies
>>between source files (this includes header files as well).
> 
> Sorry, but it IS technically correct.
>

Can three play this "'tis 'tain't" game?

Sorry, but it is technically INCORRECT.

I quote from the _make_ manual: "A dependancy specifies a set of
things that the given target depends on -- that is, do something
to construct the target if the things it depends on have been
updated since the last time the target was constructed."

Unquote.

The man-page says the same thing in different words.

By what rationale can the first .h be said to depend on the second?
How is _make_ to "do something to construct the target", if not check
it out of SCCS or RCS?  And if it does that, what has the second .h
got to do with the price of beans in Slobovia?

He's gotcha, Doug.  Say, "Uncle."

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (02/28/89)

From article <2532@goofy.megatest.UUCP>, by djones@megatest.UUCP (Dave Jones):
> ) Are recursive #includes hazardous to your software's health? 

    Could we move this discussion to comp.lang.c, please?

willy@idca.tds.PHILIPS.nl (Willy Konijnenberg) (02/28/89)

In article <2533@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>From article <964@philmds.UUCP>, by leo@philmds.UUCP (Leo de Wit):
>> ... it is never correct to declare make dependencies
>> between source files ...
>
>y.tab.c: gram.y
>
>   :-)  Dave
Tough luck, y.tab.c is not your source.
It is actually generated from gram.y, as opposed to

prog.c: incl.h

where prog.c is not generated from incl.h, hence an out-of-date prog.c will
remain out-of-date and force another recompilation next time around, etc.

Ok, ok, I saw the :-) :-)

-- 
	Willy Konijnenberg		<willy@idca.tds.philips.nl>

raveling@vaxb.isi.edu (Paul Raveling) (03/01/89)

In article <4587@hubcap.UUCP> wtwolfe@hubcap.clemson.edu writes:
>From article <2532@goofy.megatest.UUCP>, by djones@megatest.UUCP (Dave Jones):
>> ) Are recursive #includes hazardous to your software's health? 
>
>    Could we move this discussion to comp.lang.c, please?

	I believe it's germane to this group, and should be
	considered one facet of software engineering issues 
	involving structuring and managing source code.  Other
	languages face the same issues.

	Lack of consistency in included source has easily
	demonstrated costs, and should be recognized as a
	software engineering problem.  Consider these 4 consecutive
	lines in my make log for contributed X11R3 software:
	
    MenuBox             Made            Needed to hack includes for test
    Xhp                 Make failed; syntax errors in include files
    Xsw                 Make failed; missing include file
    widgetwrap          Make failed; depends on include file installation


	Problems with included source were the largest cause
	of failures in generating "portable" software in this
	set.  If I search out and fix all such problems in this
	set of source, the cost of my time plus overhead will
	probably be on the order of tens of kilobucks.  Now
	consider a few thousand other people around the country
	doing the same... 


----------------
Paul Raveling
Raveling@isi.edu

throopw@agarn.dg.com (Wayne A. Throop) (03/01/89)

> gwyn@smoke.BRL.MIL (Doug Gwyn )
>> leo@philmds.UUCP (Leo de Wit) 
>>> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) 

>>> It's easy to get the Makefile correct; just declare that a header
>>> depends on the others that it #includes.
>> This is not correct, as it is never correct to declare make dependencies
>> between source files (this includes header files as well).
> Sorry, but it IS technically correct.  Dependence is a transitive
> property.
> "make" has an inherent design problem in that it doesn't properly
> distinguish between logical and physical dependencies.

It seems to me that to claim both that it is technically correct and
that make's rules run into a problem here is to miss the point Leo has
to make.  Of course, I think Leo is also missing at least some of the
points Doug has to make.  Let me try to untangle what I see as partial
truth on each side.

First, dependence *is* indeed a transitive property.  But the
recommended use of make is not technically correct, as Doug himself
points out just after claiming that it is.  Make does not distinguish
between "needed to construct" and "needed to use as input".  Make has
only the concept "needed to construct".  Leo is pointing out that,
given only the concept "needed to construct", it is never correct to
say that a source "depends" upon another source.

But on the other hand, it is correct to say that since dependence is
transitive, the .o does not need to depend directly upon all include
files that the compiler will read.

It seems to me that the "correct" way (or at least a better way) of
dealing with trees of include files is to introduce a phantom object
which depends upon "this .h and the phantom objects of the .hs it
includes".  The .o would depend on "this .c and the phantom objects of
the .hs it includes".  By "phantom object", I mean something not
corresponding to a file in the file system.

In this scheme, as in make, there is only "needed to construct", and
therefore no source should depend upon another source directly.  But
the "construction" of the conceptual object of "include file foo is
ready to be read" CAN depend upon a source, namely include file foo.

The alternative that Doug suggests, of having more than one kind of
"needed for" seems overly complicated.  The scheme I suggest works
even when the relevant .h files are constructed on the fly themselves,
(assuming some modifications to make, of course).

( This scheme still doesn't deal with cyclic includes broken with
  #ifdef... but despite there being ways to deal with that as well,
  my personal feeling is "don't DO that" is a good remedy.  )

--
The nation that controls magnetism will control... The Universe!
                                        --- Dick Tracy
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

steve@gondor.cs.psu.edu (Stephen 2. Williams) (03/01/89)

In article <2538@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>From article <9736@smoke.BRL.MIL>, by gwyn@smoke.BRL.MIL (Doug Gwyn ):
>> In article <964@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>>>In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:

>>>|It's easy to get the Makefile correct; just declare that a header
>>>|depends on the others that it #includes.

>>>This is not correct, as it is never correct to declare make dependencies
>>>between source files (this includes header files as well).

>Sorry, but it is technically INCORRECT.
>
>
>By what rationale can the first .h be said to depend on the second?
>How is _make_ to "do something to construct the target", if not check
>it out of SCCS or RCS?  And if it does that, what has the second .h
>got to do with the price of beans in Slobovia?

Consider the following stunt (I used it in a longish project of mine):

	prog: a.o b.o c.o
		cc -o prog  a.o b.o c.o

	a.o: master.h

	master.h: sub1.h sub2.h
		@touch master.h

Notice that the master.h file is created from the two files sub1.h
and sub2.h, presumably because of nested includes.  Some might gripe
that you are doing work where none is necessary, but it works quite
well.  *Technically* speaking, Dave is correct is saying that nested
dependencies cannot be directly represented, but make is apparently
general enough for a programmed solution.


>
>He's gotcha, Doug.  Say, "Uncle."


Say no such thing, Doug:-)



Steve 2. Williams	(steve%crown%journey%attmail@research.att.com)

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/01/89)

In article <2538@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>By what rationale can the first .h be said to depend on the second?

The first .h depends on the second, because for all X, if X depends on
the first .h, then X depends on the second.  That is a logical dependency,
not a physical one.  No action is required to update the first .h if the
second is changed (although as someone else pointed out, until a "touch"
is done on the first .h to "update" it, the effect of changing the second
will continue to be propagated toward the dependents of the first .h by
"make", resulting in unnecessary rebuilding of dependents of the first .h).

As I remarked in another posting, the problem is that there are logical
dependencies, with crisp mathematical properties, and physical dependencies,
which require some actions to update the targets.  "make" unfortunately
does not distinguish between these, causing lots of grief over the years.
Some versions of "make" lean more one way than the other, other versions
vice-versa.  In some sense they're ALL botched.  That's one of the reasons
you see so many "new make" implementations.  (There are a lot of other
problems, too, but that's the worst one in my opinion.)

One of the reasons I don't normally show all the dependencies on headers
in my Makefiles is that "make" simply cannot really get it "right", and
a "dependency" on a header really is a "might depend" on the header
anyway, so letting "make" do its thing without manual intervention can
result in a lot of unnecessary recompilations.  Some object/header
relationships are obviously sufficiently closely coupled that the
dependency should be shown, but nested headers may well not need to
be listed in the Makefile.  It depends on how you conduct your software
development operations..

rkl1@hound.UUCP (K.LAUX) (03/02/89)

	I suppose, given all the discussion, that it might be best to Not Use
Nested Includes at all.  Then the only problems (minor) would be to keep the
MakeFile Dependencies up to date and in sync with the actual Includes in the
source code module and to get the order of inclusion correct (in case one
Header File needs something that appears in another Header file).

--rkl

jas@ernie.Berkeley.EDU (Jim Shankland) (03/02/89)

In article <9752@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <2538@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>>By what rationale can the first .h be said to depend on the second?
>
>The first .h depends on the second, because for all X, if X depends on
>the first .h, then X depends on the second.

This sounds a little funny (well, wrong).  Dependency is a binary,
transitive relation D(a, b).  It means:  "a is functionally dependent
on b.  If the value of b changes, the value of a is likely to change as
a consequence."  You can't conclude that because for all X, D(X, h1)
implies D(X, h2), therefore D(h1, h2) is the case.  You might come up
with some new relation I, and *define* it as follows:  if and only if,
for all X, D(X, h1) implies D(X, h2), then I(h1, h2).  Such a relation
may be useful; but it's not the dependency relation D.

>As I remarked in another posting, the problem is that there are logical
>dependencies, with crisp mathematical properties, and physical dependencies,
>which require some actions to update the targets.  "make" unfortunately
>does not distinguish between these, causing lots of grief over the years.

I don't understand the distinction you're trying to draw.  What
make cares about, and should care about, is the D relation.
Providing support for the user to specify the I relation might be
useful, but only as a notational shorthand:  make could derive D
relationships from the given I relationships, relieving the programmer
of the burden of enumerating those D relationships herself.  But
they're not equivalent, and treating them as such leads you into
the "touch hdr1.h" morass.

A better approach, I think, is to automate as much dependency determination
as possible.  Various makefile generators and "new makes" do this,
modulo a few snags involving conditional inclusion.  More could be done.

>One of the reasons I don't normally show all the dependencies on headers
>in my Makefiles is that "make" simply cannot really get it "right", and
>a "dependency" on a header really is a "might depend" on the header
>anyway, so letting "make" do its thing without manual intervention can
>result in a lot of unnecessary recompilations.

Ah, you like to live dangerously :-).  Most of the time, it's better to
do a little extra recompilation than to discover that the "bug" you've
been tearing your hair out over for the last day and a half is really
caused by an out-of-date module.

You're right, make and the makefile generators are overly conservative
in determining the D relation.  They could do better by doing syntactic
analysis of the changes to a source file.  That is probably the way of
the future for environments supporting large software projects.
Determining the minimal correct D relation is, in general,
undecidable:  if I add some code to a source file, it is undecidable
whether that code will ever be executed, hence whether the source file
needs to be recompiled.  No matter.  I think syntactic analysis will
get us close enough for practical purposes.

Jim Shankland
jas@ernie.berkeley.edu

djones@megatest.UUCP (Dave Jones) (03/02/89)

From article <9752@smoke.BRL.MIL>, by gwyn@smoke.BRL.MIL (Doug Gwyn ):
> In article <2538@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>>By what rationale can the first .h be said to depend on the second?
> 
> The first .h depends on the second, because for all X, if X depends on
> the first .h, then X depends on the second.

You're bluffing!

The premise follows from the conclusion, but it doesn't work the
other way 'round. (Show your work in the margin, or on a separate sheet.)

You said in an earlier posting that the conclusion follows from the
transitive  property of "depend", but it doesn't.

I really don't have a clue as to what "depend" means to you, but it's
sure not the way _make_ defines it in the manual or on the man-page,
and which, I might add, is also the way my dictionary defines it:

   "to exist by virtue of some necessary relation."


> That is a logical dependency,
> not a physical one.  No action is required to update the first .h if the
> second is changed (although as someone else pointed out, until a "touch"
> is done on the first .h to "update" it, the effect of changing the second
> will continue to be propagated toward the dependents of the first .h by
> "make", resulting in unnecessary rebuilding of dependents of the first .h).
> 

If you touch the first .h in the rule, then the first .h depends on the
second, and _make_ will function as advertised.

dff@Morgan.COM (Daniel F. Fisher) (03/02/89)

In article <3701@xyzzy.UUCP> throopw@agarn.dg.com (Wayne A. Throop)
writes:
> [clarification between two antithetical positions followed by
> a reasonable proposal involving "phantom objects" to define
> a ready to be read attribute for header files followed by this:]
>
>( This scheme still doesn't deal with cyclic includes broken with
>  #ifdef... but despite there being ways to deal with that as well,
>  my personal feeling is "don't DO that" is a good remedy.  )

Since the world is not as well layered as one would sometimes like
to believe, it is likely one will at usually need to cope with cyclic
includes.  That is unless one does away with modularity by putting
everything in the same include file.  

Suppose one assumes a system in which there is no cyclic dependencies
between modules, then the dependency relationship is a complete
partial ordering of the set of modules.  So one could represent is
as a sufficiently deep layer cake.  But if the system cannot be
layered, say due to a reflexive relationship between two modules
(I point to your objects and you point to mine), this implies that
there must be cyclic dependencies.

In a modular style, one usually specifies the interface to a module
inside a header file.  This header file is included in any source
file that references objects defined in the module.  In particular,
a header file for one module should include another header file for
another if that first modules interface depends on declarations found
in the second's interface.  In this way, a module that depends on
another, but not directly on things that that module's interface
depends, only needs to include the header file of modules on which
it directly depends.  [Aside: This is the source of my recommendation
to myself any anyone who will listen that header files, by themselves,
should compile/lint cleanly.  Of course, its bad form to define
data objects or functions in a header, so if its compiled, a header
should yield an object file with zero length data and text segments.]

As an example, consider a module called tran.c, that handles a data
structure defining transactions in a system and trproc.c, that handles
a data structure defining transaction processors in the same system.
In this system, transactions are received by a front-end processor,
routed to the appropriate back-end processor which processes the
transaction and returns the response to the front-end for forwarding
to the originator of the transaction.  One of the requirements is
that it be possible to close a processor and dispose of all its
transactions in a reasonable manner (return a negative response to
the originator, and free the resources allocated for the transaction).
In this system, struct tran includes pointers to the front-end and
back-end processors that are handling the transaction.  The processors
also contain lists of transactions that they are currently processing.
Closing a transaction processor requires it to abort processing of
its transactions on their other processor.  So tran.c refers to
trproc.c and trproc.c refers to tran.c and tran.h refers to trproc.h
and trproc.h refers to tran.h.  So we have a situation in which cyclic
dependencies are quite natural and I claim necessary.

tran.h:
-------------------------------------
#ifndef included_tran_h
#define included_tran_h

#include "trproc.h"

struct tran
{
	struct trproc *frontend_proc;
	struct trproc *backend_proc;
...
};
...
#endif
-------------------------------------

tran.c:
-------------------------------------
#include "trproc.h"
#include "tran.h"
...
-------------------------------------

trproc.h:
-------------------------------------
#ifndef included_trproc_h
#define included_trproc_h

#include "tran.h"

struct trproc
{
	struct tran **trans;
...
};
...
#endif
-------------------------------------

trproc.c:
-------------------------------------
#include "tran.h"
#include "trproc.h"
...
-------------------------------------

-- 
Daniel F. Fisher
dff@morgan.com

geoff@yasc.PLA.CA.US (Geoff Leach) (03/02/89)

From article <9752@smoke.BRL.MIL>, by gwyn@smoke.BRL.MIL (Doug Gwyn ):
> 
> As I remarked in another posting, the problem is that there are logical
> dependencies, with crisp mathematical properties, and physical dependencies,
> which require some actions to update the targets.  "make" unfortunately
> does not distinguish between these, causing lots of grief over the years.
> Some versions of "make" lean more one way than the other, other versions
> vice-versa.  In some sense they're ALL botched.  That's one of the reasons
> you see so many "new make" implementations.  (There are a lot of other
> problems, too, but that's the worst one in my opinion.)
>
> One of the reasons I don't normally show all the dependencies on headers
> in my Makefiles is that "make" simply cannot really get it "right", and
> a "dependency" on a header really is a "might depend" on the header
> anyway, so letting "make" do its thing without manual intervention can
> result in a lot of unnecessary recompilations.  Some object/header
> relationships are obviously sufficiently closely coupled that the
> dependency should be shown, but nested headers may well not need to
> be listed in the Makefile.  It depends on how you conduct your software
> development operations..

Turns out there's a cure for this.  Try "cake", posted to comp.sources.unix,
volume 12.  Not only does it permit the user to program _all_ his/her own
rules, but it also permits the specification of dynamic dependencies.

scs@adam.pika.mit.edu (Steve Summit) (03/02/89)

In article <2941@hound.UUCP> rkl1@hound.UUCP (K.LAUX) writes:
>I suppose, given all the discussion, that it might be best to Not Use
>Nested Includes at all.  Then the only problems (minor) would be...
>to get the order of inclusion correct (in case one
>Header File needs something that appears in another Header file).

In a large but well-structured project, this is certainly not a
minor problem.  (The discussion has now come full circle; the
original correspondent understood this, but it's an important
point that's worth repeating.)

Most people agree that the only way to maintain control of a
large piece of software is to break it up into modules, which can
be treated as black boxes, doing everything possible to keep the
"coupling" between modules to a bare minimum.

In C, it is natural and useful to provide a separate header file
for each module; any code using the module must #include its
header file.  Requiring someone who uses module A, and therefore
#includes "A.h", to first include "B.h", because module A happens
to be built upon module B, is essentially disclosing something
about A's implementation that the caller shouldn't have to know.
This is more than a theoretical problem: aside from the bother,
more serious practical difficulties rapidly emerge.  One example
that springs to mind would be a change to A's implementation, to
base it upon module C instead of B.  Such a change should be
invisible to users of A, yet (under a scheme that doesn't use
nested #includes) each would have to change their code to
#include C.h instead of B.h.  This is a source-level change, not
a simple matter of recompilation, and is completely contrary to
the spirit of modular independence.

(For a concrete and practical example, consider a symbol table
module which is built on a binary tree module.  The users of the
symbol table module shouldn't care if it is later changed to use
a hash table module instead.)

Another, larger and nastier, problem would be keeping all of the
header #inclusions in the right order when several modules are
being used.  This problem can get out of hand very fast, and
determining the correct order typically requires inspecting the
contents of each header file, which is not the sort of thing
you're supposed to have to do with a "black box."

It is true that nested #includes introduce a few difficulties of
their own, but they are limited in scope and easily disposed of
by using good tools (e.g. the automated Makefile dependency
generators and #ifndef X_H/#define X_H tricks being discussed).
The problems introduced by not using nested #includes, on the
other hand, are neverending.

If you have never worked on a large project, the difficulties
associated with less-than-perfect modular isolation may not seem
worth all the fuss.  ("A grep here, a global query-replace or a
sed script there, and we're off and running!")  The bigger the
project, though, the more paralyzing these seemingly
inconsequential details can become.  Long ago I came to the firm
conclusion that nested #includes, although not perfect, are the
only way to go.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

leo@philmds.UUCP (Leo de Wit) (03/02/89)

In article <9736@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|In article <964@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
|>In article <9727@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|>|It's easy to get the Makefile correct; just declare that a header
|>|depends on the others that it #includes.
|>This is not correct, as it is never correct to declare make dependencies
|>between source files (this includes header files as well).
|
|Sorry, but it IS technically correct.  Dependence is a transitive
|property.  The only practical problem you could run into here occurs
|when your version of "make" barfs on cyclic dependencies caused by
|mutually-dependent headers.  If you don't have recursion among the
|headers, though, you should be safe.

Not so. You always have a problem. If I may quote from S.I.Feldman's
'Make - A Program for Maintaining Computer Programs':

(section Description Files and Substitutions)

"  A dependency line may have either a single or a double colon. A target
"  name may appear on more than one dependency line, but all of those
"  lines must be of the same (single or double colon) type.
"  
"  1.    For the usual single-colon case, at most one of these dependency
"        lines may have a command sequence associated with it. If the
"        target is out of date with any of the dependents on any of the
"        lines, and a command sequence is specified (even a null one
"        following a semicolon or a tab), it is executed; otherwise a
"        default creation rule may be invoked.
"  
"  2.    In the double-colon case, a command sequence may be associated
"        with each dependency line; if the target is out of date with any
"        of the files on a particular line, the associated commands are
"        executed. A builtin-rule may also be executed. This detailed form
"        is of particular value in updating archive-type files.

Note especially the last sentence of 1.: "otherwise a default creation
rule may be invoked.". Although it might not be clear, the implicit
rules are meant here (suffix dependencies). NOT a .DEFAULT rule, which
is only invoked if a target must be made and no explicit rule has been
specified and no implicit rules can be applied (an implicit rule can be
applied when both suffixes appear in the .SUFFIXES list, the 'source
suffix' file exists, and an implicit rule has been given for this pair
of suffixes). I checked Ultrix's native make (/bin/make), its S5 make
(/bin/s5make), our local make (a System III port, /usr/local/make) and
Sun's make (from /usr/sunpro, the one that sorts out header
dependencies and has lots of fancy stuff). They all behave this way,
that is: invoking an implicit rule if a dependency 'to be updated' has
no associated command sequence.

A little demo to make things clear; because not all makes support SCCS,
I don't use .h~.h rules; instead .h files are made dependent from .x
files, and the .x file copied to the .h if needed (of course in real
a check to prevent clobbering an existing file would be added).

(demo)  Script started on Wed Mar  1 21:31:49 1989
(demo)  philmds> cat makefile
(demo)  
(demo)  a.out : main.c inc1.h
(demo)  	$(CC) main.c
(demo)  
(demo)  inc1.h : inc2.h
(demo)  
(demo)  .SUFFIXES : .h .x
(demo)  
(demo)  .x.h :
(demo)  	cp $< $*.h
(demo)  
(demo)  philmds> cat main.c
(demo)  #include "inc1.h"
(demo)  
(demo)  main(){}
(demo)  philmds> cat inc1.h
(demo)  #include "inc2.h"
(demo)  philmds> cat inc2.h
(demo)  /* Nothing in here */
(demo)  philmds> cp inc1.h inc1.x
(demo)  philmds> /bin/make
(demo)  cp inc1.x inc1.h
(demo)  /bin/cc main.c
(demo)  philmds> touch inc2.h
(demo)  philmds> /bin/make
(demo)  cp inc1.x inc1.h
(demo)  /bin/cc main.c
(demo)  philmds> touch inc2.h
(demo)  philmds> /usr/local/make
(demo)  	cp inc1.x inc1.h
(demo)  	cc main.c
(demo)  philmds> touch inc2.h
(demo)  philmds> /bin/s5make
(demo)  	cp inc1.x inc1.h
(demo)  	cc main.c
(demo)  philmds> 
(demo)  script done on Wed Mar  1 22:02:21 1989

I suspect the reason that your approach works for instance on Suns, is
that those makes have implicit knowledge of SCCS; note it did not get
it right with the implicit rule I added myself (.x.h).

|The make/SCCS behavior you describe is clearly erroneous; a later-
|modified checked-out file should never be clobbered by retrieval
|from the archive.

True. In fact, the problem (in /usr/local/make) is in the implicit rules:

.h~.h:
	$(GET) $(GFLAGS) -p $< > $*.h

You can draw your own conclusions ... Of course it can only occur with
dependencies between sources (which I still consider incorrect), but
the clobbering is unforgivable.

|                   I think this behavior occurs because of the
|redundancy introduced in the default rules in an attempt to
|compensate for "make" not getting the logical transitivity right.

Maybe you expect too much of Make; you want a target name both to
represent a logical and a physical item. To use the demo example, the
preprocessed inc1.h is dependent of inc1.h and inc2.h, inc1.h itself is
not (inc1.h does not change if you modify inc2.h). The use of dummy
targets (I think that was what you meant by FRC?) I do not consider a
kludge.

	Leo.

dff@Morgan.COM (Daniel F. Fisher) (03/03/89)

In article <9752@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>)
writes:
>                     No action is required to update the first .h if the
>second is changed (although as someone else pointed out, until a "touch"
>is done on the first .h to "update" it, the effect of changing the second
>will continue to be propagated toward the dependents of the first .h by
>"make", resulting in unnecessary rebuilding of dependents of the first .h).

Gack!! If a makefile touch(1)es a .h, it may cause a program that uses
a different set of -D, -I and -U options in to be unnecessarily recompiled.
As Doug says, "No action is required," so none should be taken.  It is quite
possible to arrange for make to know about the exact header file dependencies
of a given object file (compiled with a particular set of -D, -I and -U
options).  If the same source file is compiled using other options, it should
result in a different object file, with a different set of dependencies.
In System V, the -H compiler option will cause cpp to list one stdout or
stderr (I forget which), the pathnames of the include files it visits.  In
BSD, the -M compiler option will actually generate make dependencies on
stdout.  These features can be used in a shell script with awk and/or sed to
build a very nice make dependency generator.  It can be called for each object
file that is a target of the Makefile in a depend target within the makefile.
Make programs with the .INIT and include features can actually automatically
rerun make depend before evaluating other targets or including the
automatically generated make dependencies if the makefile is changed.  Make
depend can be rerun manually if #include lines are changed in relevant .c
or .h files.  If no automatic way is possible, manually generating these
dependencies, though painful, will suffice.  In my experience, generating
the dependencies, even manually, pays of in the long run.

-- 
Daniel F. Fisher
dff@morgan.com

dff@Morgan.COM (Daniel F. Fisher) (03/03/89)

In article <2941@hound.UUCP> rkl1@hound.UUCP (K.LAUX) writes:
>	I suppose, given all the discussion, that it might be best to Not Use
>Nested Includes at all.  Then the only problems (minor) would be to keep the
>MakeFile Dependencies up to date and in sync with the actual Includes in the
>source code module and to get the order of inclusion correct (in case one
>Header File needs something that appears in another Header file).
>
>--rkl

But nested includes obviate the need to know the correct order of inclusion,
which would involve MANUALLY compiling and then topologically sorting an
include file list.  This is far more tedious than simply compiling a list
of make dependencies from the transitive closure of the "source file includes
other source file" relation.  And the availability of automatic header
dependency generation even removes this tedium.  Furthermore, if one only
needs to include those header files that are directly referenced in a given
source file, allowing header files to include those headers that they need,
one need not change the list of included files in any source file unless and
until that source file otherwise changes.  Contrast this with having to add
include lines to a multitude of different source files when a shared header
file changes to require prior inclusion of another header file.  Or consider
the pain involved in transporting a program to a system with a somewhat
different set of LOW LEVEL system header files.  Personally, I'd rather deal
with nested includes.

-- 
Daniel F. Fisher
dff@morgan.com

stacey@hcr.UUCP (Stacey Campbell) (03/03/89)

In article <570@marob.MASA.COM> samperi@marob.UUCP writes:
>#ifndef H_FOO
>#define H_FOO
>
>defines, declarations, etc.
>
>#endif

It is almost essential on very large projects to implement a
form of guarding of recursive includes.  I'm currently working
on a project where a typical 'crunched' cpp file is over 10000
lines and where over 100 included files are nested up to 14 levels.
The guarding is implemented differently;

(contents of bar.h)

#ifndef FOO
#include <foo.h>
#endif

(contents of foo.h)

#ifndef FOO
#define FOO
...
#endif

Yes, two sets of guards just in case someone implements a
module including foo.h without checking FOO.  Prechecking
to see if a module has already been included saves an
enormous amount of compile time and file inclusion overhead.
-- 
Stacey Campbell, HCR Corporation, {lsuc,utzoo,utcsri}!hcr!stacey

throopw@agarn.dg.com (Wayne A. Throop) (03/04/89)

> dff@Morgan.COM (Daniel F. Fisher)
>> throopw@agarn.dg.com (Wayne A. Throop)
>>( This scheme still doesn't deal with cyclic includes broken with
>>  #ifdef... but despite there being ways to deal with that as well,
>>  my personal feeling is "don't DO that" is a good remedy.  )

>  it is likely one will [...] need to cope with cyclic
> includes.  That is unless one does away with modularity by putting
> everything in the same include file.

Daniel's examples of this that follow are quite good, but there are
usually palatable alternatives to actually having a.h include b.h
include a.h (breaking the cycle purely by preprocess-time defines).
I'll try to explain these palatable (at least to me) alternatives.  I
emphasize again: there are indeed cases where recursive includes are a
very VERY attractive solution, but there are practically always
alternatives that are at least palatable if not ideal.

> But if the system cannot be
> layered, say due to a reflexive relationship between two modules
> (I point to your objects and you point to mine), this implies that
> there must be cyclic dependencies.

Note that there must be a cyclic dependency only if the INTERFACE to
one module needs to know about the interface to another and vice
versa.  Structs containing pointers to each other is a good example of
this, but there are at least two ways of arranging things in such a
way that cycles do not arise.  The first, as Daniel suggests, is to
define both types in a single header used by both modules.  This
implies that there is one such "buddy" header for (more or less) each
pair of intertwined modules.  Palatable in some cases, but in general
only as a last resort, I suppose.

But note that each module only needs to be able to declare a pointer
to the other's type.  In C, this can be done using incompleted types.
(See section 3.5.2.3 of pANS, and especially footnote 48 of that
section.)  The header file for the first module would export only the
fact that it will implement a struct, and provide the name and the
pointer type for it.  Similarly for the other module.  Then in the
implementation (the .c files), these structs can be defined in the
peace privacy and privacy of their own namespace, with no cyclic
includes.  This is the prefered way to do it, I'd say.  If it is the
case that the two modules need to export the internal details of their
structs, this can be done by having an interface1-level include file
which is to be included by "buddy" modules, and an interface2-level
include file which is to be included by other client modules.

Note that this second method of dealing with reflexive structs is
fully general, does not force "buddy" modules to get into bed together
in a common include file, and effectively gets rid of those annoying
cycles.

> As an example, consider a module called tran.c, that handles a data
> structure defining transactions in a system and trproc.c, that handles
> a data structure defining transaction processors in the same system.
> [...etc...]

I'll rewrite the example and present my alternative below.  Note that
the #ifndef stuff is now not needed to break cycles, but I routinely
use it anyway, to painlessly order nested includes and insure single
inclusion.  Interestingly enough, this rewriting is, in fact, more
modular than when we started, since neither module is privy to the
internals of the other's structs, and a typedef encapsulates the pointer
creation, so that reimplementing either one as (say) a table index
instead of a pointer does not require touching code in the other module.

tran.h:
-------------------------------------
#ifndef included_tran_h
#define included_tran_h

typedef struct tran *tran_t;
...
#endif
-------------------------------------

tran.c:
-------------------------------------
#include "trproc.h"
#include "tran.h"

struct tran
{
	trproc_t frontend_proc;
	trproc_t backend_proc;
...
};
...
-------------------------------------

trproc.h:
-------------------------------------
#ifndef included_trproc_h
#define included_trproc_h

typedef struct trproc *trproc_t;
...
#endif
-------------------------------------

trproc.c:
-------------------------------------
#include "tran.h"
#include "trproc.h"

struct trproc
{
	tran_t *trans;
...
};
...
-------------------------------------

--
Down, down in the basement we hear the sound of machines.
I, I, I'm driving in circles.  Come to my senses sometimes.
Why--why--why start it over?
                                        --- Talking Heads
--
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

ka@june.cs.washington.edu (Kenneth Almquist) (03/06/89)

In article <3804@xyzzy.UUCP>, throopw@agarn.dg.com (Wayne A. Throop) writes:
> But note that each module only needs to be able to declare a pointer
> to the other's type.  In C, this can be done using incompleted types.
> (See section 3.5.2.3 of pANS, and especially footnote 48 of that
> section.)

I've been told that incomplete types cannot be used in function
prototypes.  For example,

	void f(struct s *);

	struct s {
		int i;
	};

	void f(struct s *p) {
		...
	}

is illegal because s is not defined when the function prototype for
f is processed.  If so, this makes Wayne's suggestion pretty useless.
Have I been misinformed?
				Kenneth Almquist

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/07/89)

In article <7488@june.cs.washington.edu> ka@june.cs.washington.edu (Kenneth Almquist) writes:
>I've been told that incomplete types cannot be used in function prototypes.

I couldn't find such a prohibition in the pANS.
(But maybe it's there and I missed it.)

karl@haddock.ima.isc.com (Karl Heuer) (03/07/89)

In article <7488@june.cs.washington.edu> ka@june.cs.washington.edu (Kenneth Almquist) writes:
>I've been told that incomplete types cannot be used in function
>prototypes.  For example,
>	void f(struct s *);
>	struct s { ... };
>	void f(struct s *p) { ... }
>is illegal ...

Yes it is illegal, but due to a scoping problem, not a restriction against
incomplete types.  Adding the empty declaration `struct s;' at the top should
cause it to become legal.  (I just tested this with gcc.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

throopw@agarn.dg.com (Wayne A. Throop) (03/07/89)

> dff@Morgan.COM (Daniel F. Fisher)
> It is quite possible to arrange for make to know about the exact
> header file dependencies of a given object file (compiled with a
> particular set of -D, -I and -U options).

Not quite so easy when considered in full generality.  Fairly easy if
one wants to suppose that all include dependencies are known from
sources.  Quite a bit harder if one wants to cope with .c or include
files generated on the fly.  In such cases, the only fully general way
to do it is to have the compiler (where compiler subsumes
"preprocessor") co-operate co-routine-wise with the automated
construction system, and ask for (for example) each include file to be
constructed (if necessary) exactly when the compiler decides to open
it.

> Make depend can be rerun
> manually if #include lines are changed in relevant .c or .h files.

I much MUCH druther it be run automatically.  And it can be.  Since
around here we generate .c and .h files by various arcane processes
(and not just well-known ones like LEX and YACC), I prefer to have
include dependency tracking fully automated and capable of handling as
many cases of generated files as possible.

I haven't yet gotten compilers to co-operate in the above noted
fashion.  Until then, the tradeoff I make is to suppose that an
#include is going to be tread upon.  This makes for a rather small (in
my experience) tendency to compile modules that "don't need it".  But
having the whole thing automated in such a way that I don't need to
think about it and which is safe is a blessing well worth this
particular trade (IMHO).

Of course, people with different circumstances may well reasonably
choose other tradeoffs.

--
"A" is for Atom, they are all so small,
That we have not really seen any at all.
"B" is for Bomb.  They are much bigger.
So mister, you better keep off of the trigger.
         --- Edward Teller (in an interview on Nova)
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

throopw@agarn.dg.com (Wayne A. Throop) (03/08/89)

> karl@haddock.ima.isc.com (Karl Heuer)
>> ka@june.cs.washington.edu (Kenneth Almquist)
>>I've been told that incomplete types cannot be used in function
>>prototypes.  For example,
>>	void f(struct s *);
>>	struct s { ... };
>>	void f(struct s *p) { ... }
>>is illegal ...
> Yes it is illegal, but due to a scoping problem, not a restriction against
> incomplete types.  Adding the empty declaration `struct s;' at the top should
> cause it to become legal.  (I just tested this with gcc.)

Testing it with DG C, this amusing, almost inscrutable, (though in
spirit quite correct) error message resulted:

    You have already declared "f" as a variable,
    enumeration, or typedef in the current block with different type
    attributes.  The following are the previous and current types:
            Previous type: void (struct s *)
            Current  type: void (struct s *)

Note, however, that incompleted types can still be used to break the
kinds of cycles I was talking about, since (as Karl points out) this
is a scoping problem not encountered in derivatives of the example I
gave.

--
"Is it an infinite loop?"
"I dunno... it sure is *persistent*, anyhow."
                              --- unknown
--
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

geoff@cs.warwick.ac.uk (Geoff Rimmer) (03/12/89)

In article <9804@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> In article <7488@june.cs.washington.edu> ka@june.cs.washington.edu (Kenneth Almquist) writes:
> >I've been told that incomplete types cannot be used in function prototypes.
> 
> I couldn't find such a prohibition in the pANS.
> (But maybe it's there and I missed it.)

gcc will not allow it:

Script started on Sat Mar 11 17:04:06 1989
--geoff@emerald%   cat str.c

static void function (struct egg * a);

struct egg { int number1; float number2; };

static void function (struct egg * a)
{
        printf("In function with [%d,%f]\n",a->number1,a->number2);
}

extern void main (void)
{
        struct egg aa;
        aa.number1=23;
        aa.number2=12.345;
        function(&aa);
}

--geoff@emerald%   gcc -v
gcc version 1.34
--geoff@emerald%   gcc -ansi -pedantic str.c
str.c:1: warning: `struct egg' declared inside parameter list
str.c: In function function:
str.c:6: conflicting types for `function'
str.c:1: previous declaration of `function'
--geoff@emerald%   exit

script done on Sat Mar 11 17:05:06 1989

The strange thing is that it doesn't barf on the 

	struct void function (struct egg *a);

line, even though the structure is as yet undefined.  The problem is
when it compares the types of the function's arguments, and sees that
in the call to the function, the argument is type (struct egg *) and
in the declaration it is unknown.

Is this a problem with gcc, or does this happen with all ANSI compilers?

Geoff

	------------------------------------------------------------
	Geoff Rimmer, Computer Science, Warwick University, England.
			geoff@uk.ac.warwick.emerald

		"Ahhh, bonjour, monsieur."
		"Sod off."
			- Blackadder 3.
	------------------------------------------------------------