Header Policy ------------- The C++ Standard specifies many mutual dependencies among the headers it defines. It offers no advice on how to arrange headers to avoid problems. The worst such problem is circular references. Most simply this is "A includes B, B includes A": // file // file #ifndef A #ifndef B #define A 1 #define B 1 #include #include typedef int A_type; typedef int B_type; extern B_type g(A_type); extern A_type f(B_type); #endif /* A */ #endif /* B */ // file C.cc #include The typical effect of such an "include loop" may be seen by tracing the preprocessor activity: C // file C.cc C #include A // file A #ifndef A A #define A 1 A #include B // file B #ifndef B B #define B 1 B #include A // file A #ifndef A <-- oops, cpp symbol A defined already A ... <-- skip contents A #endif B typedef int B_type; B extern A_type f(B_type); <-- error, A_type not defined yet. B #endif /* B */ A typedef int A_type; A extern B_type g(A_type); A #endif /* A */ The main symptom of #include loops is that definitions from file are not available after the #include for certain include orders. The number of standard headers makes testing all permutations of include order impractical, so a policy is needed to prevent chaos. In any case, for some standard headers (as for the above) no ordering can eliminate the loop. Other factors influence the policy. Typical implementations of Make (unfortunately including GNU make) have bugs relating to file names with no suffix, that lead to such problems as failure to track dependencies on such files and an inclination to _delete_ them. Therefore, headers used in building the library are always of the form generally, or specifically for an equivalent to the standard header . Standard headers are all placed under directory std/, and are ignored except during installation. These headers simply #include the corresponding header . Standard substitute headers that have any complexity may sub-include other headers. When they sub-include non-standard headers, they first include all the headers required for that non-standard header. Mutual dependencies are handled by splitting up the declarations intended for standard headers among two or more files, and then interleaving them as needed. For example, we replace and above, as follows: // file #ifndef _CPP_A #define _CPP_A # include # include # include #endif // file #ifndef _CPP_B #define _CPP_B # include # include # include #endif // file #ifndef _CPP_BITS_A_TYPES_H #define _CPP_BITS_A_TYPES_H typedef int A_type; #endif // file #ifndef _CPP_BITS_B_TYPES_H #define _CPP_BITS_B_TYPES_H typedef int B_type; #endif // file #ifndef _CPP_BITS_A_FUNS_H #define _CPP_BITS_A_FUNS_H extern B_type g(A_type); #endif // file #ifndef _CPP_BITS_B_FUNS_H #define _CPP_BITS_B_FUNS_H extern A_type f(B_type); #endif Of course we have the standard headers under their mandated names: // file #ifndef _CPP_A #define _CPP_A # include #endif // file #ifndef _CPP_B #define _CPP_B # include #endif Notice that the include guards are named uniformly except that the guard for standard header is just _CPP_A, identically as the header in std/. At installation the files std/* can be replaced by symbolic links, or simply copied into place as is. The result is: include/ include/A -> bits/std_A.h include/B -> bits/std_A.h include/bits/ include/bits/std_A.h include/bits/std_B.h include/bits/A_types.h include/bits/B_types.h include/bits/A_funs.h include/bits/B_funs.h Of course splitting up standard headers this way creates complexity, so it is not done routinely, but only in response to discovered needs. Another reason to split up headers is for support of separate compilation of templates. This interacts with the foregoing because template definitions typically have many more dependencies on other headers than do pure declarations. Non-inline template definitions are placed in a separate ".tcc" file that is included by the standard header, and any other standard header that requires definitions from it for its implementation. The key to preventing chaos, given the above structure, is: Only standard headers should sub-include other headers.