Recursive #includes

Steve Summit scs at adam.pika.mit.edu
Thu Mar 2 17:17:32 AEST 1989


In article <2941 at hound.UUCP> rkl1 at 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 at adam.pika.mit.edu



More information about the Comp.lang.c mailing list