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