Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!ucbvax!bloom-beacon!adam.pika.mit.edu!scs From: scs@adam.pika.mit.edu (Steve Summit) Newsgroups: comp.lang.c Subject: Re: Recursive #includes Summary: nested #includes are worth it Keywords: nested recursive includes Message-ID: <9555@bloom-beacon.MIT.EDU> Date: 2 Mar 89 07:17:32 GMT References: <9736@smoke.BRL.MIL> <2538@goofy.megatest.UUCP> <4329@psuvax1.cs.psu.edu> <2941@hound.UUCP> Sender: daemon@bloom-beacon.MIT.EDU Reply-To: scs@adam.pika.mit.edu (Steve Summit) Lines: 62 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