Newsgroups: comp.lang.c Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!think.com!mintaka!bloom-picayune.mit.edu!news From: scs@adam.mit.edu (Steve Summit) Subject: Re: A question on C programming style Message-ID: <1991Apr13.013911.18151@athena.mit.edu> Summary: nested header files are fine Sender: news@athena.mit.edu (News system) Reply-To: scs@adam.mit.edu Organization: Thermal Technologies, Cambridge, MA References: <1991Apr12.103621.8907@umiami.ir.miami.edu> Date: Sat, 13 Apr 91 01:39:11 GMT Lines: 129 In article <1991Apr12.103621.8907@umiami.ir.miami.edu> devebw9f@miavax.ir.miami.edu writes: >Style 1: (No nested includes - user responsible for proper order of includes). >foo.h > extern save_data (FILE *fp); > >Style 2: (Nested inclusion). >foo.h > #include /* We know that this has to be included with this. */ > extern save_data (FILE *fp); [A fine FAQ list question, this.] Whether nested #included files are good style is, historically, (like so many of these style questions :-( ) a bit of a religious question. (ANSI's new guarantees may have shifted the balance somewhat.) When one header file makes use of something (usually a macro or typedef) defined in another, I feel that a nested #include directive is a good idea. The alternative -- requiring the #includer to #include other header files first -- loses big points in the information-hiding department, and leads to real maintenance headaches in practice. (It's too easy to forget to pre-#include something else, and the errors that result are typically non-obvious. If the requirements of the header file in question change, all its #includers must be modified.) Obviously, if header files are to be #included recursively, they must be "idempotent" (a word which only comes up in this discussion, meaning something like "can be multiply invoked safely"), since it is likely that they will end up getting #included twice. (In Bimal's second example, the #includer of foo.h might well #include anyway.) The standard technique for guaranteeing idempotency, which has already been mentioned, is to "turn off" each include file if it has been processed already: foo.h: #ifndef FOO_H #define FOO_H /* contents of foo.h */ #endif To cut down on compilation time a bit (by eliminating unnecessary file opening and namei overhead) some people prefer to do the protecting in the #includer: #ifndef FOO_H_INCLUDED #define FOO_H_INCLUDED #include "foo.h" #endif This is ugly, as the #ifndef has to be repeated (and the sentinel macro name agreed upon) in each #includer. Nested #include files can, in general, be confusing. Finding out where something is defined can require traversing a twisty little maze of #include directives. (This Gordian knot can be cut easily, however, with "grep *.h" .) Manual Makefile maintenance quickly becomes nearly impossible, so an automatic Makefile generating scheme, which follows the nested #include directives reliably, is a requirement. The confusion and Makefile maintenance drawbacks lead some people to recommend strongly against nested #include files. (Some people also feel that the #ifndef/#define technique is an unacceptably ugly kludge. As I said, it has been a religious argument.) However, I feel that the disadvantages of not using nested #include files (the loss of information hiding, and the burdensome requirements placed on each #includer) outweigh the disadvantages of using them. (As I've suggested, given the existence of grep and a good Makefile dependency generator, the disadvantages of nested #include files largely disappear. And the #ifndef/#define technique isn't really ugly, either, especially if I remember to call it a "technique" and not a "trick" :-) . ) I've slanted this discussion a bit towards the case when the file being recursively #included is a "project" file, under the programmer's control (and probably #included with "" rather than <>). When the header file to be recursively #included is a standard header file, as it was in Bimal's example, the options shift slightly. The ANSI C Standard guarantees that standard header files may safely be #included multiple times, so nested #inclusion (of standard headers) is safe in an ANSI environment. However, most pre-ANSI header files do not implement any idempotency, so programmers must be careful if portability to pre-ANSI systems is important. Since the "clean" #ifndef/#define technique is implemented inside the header file being protected, you can't apply it retroactively to an older standard header file which lacks it. (Even if you have write access to the standard header files on your system, and don't mind modifying them, you can't assume that a person you give your code to can.) However, for at least, a variation on the second, deprecated idempotency technique is viable: #ifndef EOF #include #endif It's not at all unusual for a data structure or subroutine interface defined in a header file to refer to the stdio FILE * type (exactly as in Bimal's example), so usages like this are common, and shouldn't be frowned upon. (It helps if, in top-level .c files, you always #include standard headers first, followed by "local" ones. If, on the other hand, you had things equivalent to #include "foo.h" #include , errors on pre-ANSI systems would be more likely. It's easy to protect "#include " with "#ifndef EOF" inside of foo.h; it's harder and messier to do so in every .c file. "It is agreed that the ice is thin here.") [Now all I have to do is shrink this discussion down to three sentences for the FAQ list.] Steve Summit scs@adam.mit.edu