[comp.software-eng] 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

hans@nlgvax.UUCP (Hans Zuidam) (02/27/89)

Commodore uses the same scheme for their include files for the Amiga
software and it is a blessing. This way I (the user) never have to deal
with multiple includes. Commodore has extended the above a little:

	#ifndef EXEC_TASKS_H
	#define EXEC_TASKS_H

	#ifndef EXEC_NODES_H
	#include <exec/nodes.h>
	#endif !EXEC_NODES_H

	#ifndef EXEC_LISTS
	#include <exec/lists.h>
	#endif !EXEC_LISTS

	... Stuff here ...

	#endif !EXEC_TASKS_H

The include files are distributed over several related directories ("exec",
"graphics", "devices" and what not). Furthermore the header files include
what they themselves need. This is as it should be.

On BSD you reguarly have to include files that your program do not need but
files included do need. See for example the files in "net". If you try to
compile a file which only includes <net/if.h> you will get a nice bunch of
errors, and you will be left completely blank as to what to include also.
Or said different: I have to figure out what someone already figured out
before me (the BSD networking software does work ;-)).

Include files should be as much self contained as possible. They are in
many cases an interface between a pieces of code. A good test is that you
should be able to take a single include file and compile it without errors. 
With respect to Makefile dependencies: use makedepend and you will not have
troubles there. The method above even lets you build a dependency list by
hand *quickly*.
-- 
Hans Zuidam                                    E-Mail: hans@pcg.philips.nl
Philips Telecommunications and Data Systems,   Tel: +31 40 892288
Project Centre Geldrop, Building XR
Willem Alexanderlaan 7B, 5664 AN Geldrop       The Netherlands

psrc@pegasus.ATT.COM (Paul S. R. Chisholm) (02/27/89)

In article <570@marob.MASA.COM>, samperi@marob.MASA.COM (Dominick Samperi) writes:
> <contents of foo.h>:
> 
> #ifndef H_FOO
> #define H_FOO
>...stuff...
> #endif

This issue was beaten to death a few weeks ago in the comp.lang.c++
news group.  The obvious proposal is to not #include a file that's
already been #included, even if you see a second (or third, or forty-
second) #include for it.  This would also be more efficient that what
you suggest above, since the file doesn't even need to be opened again,
let alone read.  Unfortunately, it can break existing C code.

We had such a cpp (back in the bad old days of Net 1000, a large
development project).  People tended to #include the whole world, just
in case; but it did speed up compilation quite a bit (we had lots of
big header files).

The C++ header files I've seen do things like #ifndef STDIO_H, as you
suggest above.  It may not the best solution, but it works.  For more
information, look at old comp.lang.c++ articles (no, I don't know of
any place that archives them).

>Dominick Samperi -- ESCC, samperi@marob.masa.com, uunet!hombre!samperi

Paul S. R. Chisholm, AT&T Bell Laboratories, att!pegasus!psrc
psrc@pegasus.att.com, AT&T Mail !psrchisholm
I'm not speaking for the company, I'm just speaking my mind.

coggins@coggins.cs.unc.edu (Dr. James Coggins) (02/27/89)

In article <2630@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes:
>
>This issue was beaten to death a few weeks ago in the comp.lang.c++
>news group.  The obvious proposal is to not #include a file that's
>already been #included, even if you see a second (or third, or forty-
>second) #include for it. 
>
>Paul S. R. Chisholm, AT&T Bell Laboratories, att!pegasus!psrc

I was one of the principal beaters of this topic.

My net postings have been cleaned up, enhanced, and submitted to
SIGPLAN Notices (no idea yet on publication date).  They are also
available as a technical report from my department's SoftLab (software
research laboratory).  You can either watch SIGPLAN for J. Coggins and
G. Bollella, "Managing C++ Libraries" or e-mail for a copy of our
technical report (which will soon include an addendum with some
further techniques) to Pam Payne, Softlab Coordinator at
softlab@cs.unc.edu or to the address below. 

The organization scheme I descibed requires no preprocessor hacks,
includes headers once and once only, imposes minimally on the program
development process, and is still working well with VERY little
maintenance required.  In fact, we have extended it recently to handle
compilations of my research library for multiple architectures (Sun3,
Sun4, Vax780) and are preparing to incorporate RCS support directly
into the management structure. (These points will be described in the
addendum to the SoftLab TR). 

USmail for the tech report: Pam Payne, SoftLab Coordinator
                            Computer Science Department
                            CB#3175
                            University of North Carolina
                            Chapel Hill, NC 27599-3175
---------------------------------------------------------------------
Dr. James M. Coggins          coggins@cs.unc.edu
Computer Science Department   
UNC-Chapel Hill               Whaat! is the sound of an issue being
Chapel Hill, NC 27514-3175    beaten to death!
---------------------------------------------------------------------
 

heather@burnett.SEAS.UCLA.EDU (02/28/89)

In article <570@marob.MASA.COM> samperi@marob.MASA.COM (Dominick Samperi) writes:
>Are recursive #includes hazardous to your software's health? 
>
> etc...
>
>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.
>
For those that have BSD 4.3 based C compilers, I would like to point out
that a recent addition to the C preprocessor enables the preprocessor
to generate a dependancy list that can be included in a Makefile.  Since
the C preprocessor had to parse #include directives anyway (and it does
this recursively), adding an option (-M) to simply parse #includes and 
spit them out wasn't difficult.

For those of you unlucky enough not to have this tool, here's something
to look forward to (and it's actually pretty easy to write something
that will do this for you in the meantime).

Heather Burris, UCLA

jbn@glacier.STANFORD.EDU (John B. Nagle) (02/28/89)

In article <2630@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes:
>In article <570@marob.MASA.COM>, samperi@marob.MASA.COM (Dominick Samperi) writes:
>> <contents of foo.h>:
>> 
>> #ifndef H_FOO
>> #define H_FOO
>>...stuff...
>> #endif

      It does add to your compile times, since the entire file you're not
including has to be read and parsed.  

      I support the idea that #include should not include files "already
included".  The exact semantics of this need to be carefully defined,
so that it's well-defined whether #include <stdio.h> and "#include
"stdio.h" are considered to be identical, and the implications of pathnames
need to be thought out.  But it's a good idea.

      Given this, it would be normal for all .h files to contain includes
for any .h files they require, and creators of .c files could totally ignore
the internal dependency problems of the library.

      For those rare situations when you really want to insert a file
more than once, a new command, perhaps "#insert" should be defined.

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

From article <570@marob.MASA.COM), by samperi@marob.MASA.COM (Dominick Samperi):
) 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
) 

It is very hazardous to your health.  If you write recursively
nested include-files, you can work yourself into all sorts of 
quandries if things get a little messed up. (I speak from experience.)

The reason for the #ifndef is not for recursively defined headers.
(Use undeclared struct-this and struct-that for forward references.)
It is to let you include that include-file without worrying about
whether some other include-file has already included it. (There are
some who reject includes in includes, for this very reason, but 
that's a different holy war.)

So.... do this instead:

 
 #ifndef H_FOO

  defines, declarations, etc.

 #define H_FOO 
 #endif
 

This allows you to include an include-file twice, but not recursively.

jef@ace.ee.lbl.gov (Jef Poskanzer) (02/28/89)

In the referenced message, jbn@glacier.UUCP (John B. Nagle) wrote:
}>> #ifndef H_FOO
}>> #define H_FOO
}>>...stuff...
}>> #endif
}
}      It does add to your compile times, since the entire file you're not
}including has to be read and parsed.  

Read, yes; parsed, no.  You would be surprised at how few cycles are
used in reading an include file and just handling the #ifs.  On a
Sun 3/260:

stdio.h's	user		system		elapsed
1		0.6		0.4		5
  delta		   6.3		   1.5		  7
501		6.9		1.9		12
  delta		   6.5		   1.3		  7
1001		13.4		3.2		19
  delta		   6.7		   1.0		  10
1501		20.1		4.2		29

Looks to me like handling a stdio-sized include file for a second time
costs about 0.013 user, 0.003 system, and 0.015 elapsed.  I think I can
live with that, especially considering the minutes to hours of my time
that the current idiotic #include scheme wastes each time I need to
figure out what files to include.

I argued this issue a few years ago when I was working at UniSoft.
I provided data similar to the above, and assumed that the benefits
of including only the files you explicitly reference would be obvious
to all.  Sadly, I was ignored.
---
Jef

            Jef Poskanzer   jef@helios.ee.lbl.gov   ...well!pokey
                         In Stereo (where available)

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?