mirror of
git://gcc.gnu.org/git/gcc.git
synced 2024-12-23 04:58:53 +08:00
b2dad0e372
2000-04-21 Benjamin Kosnik <bkoz@redhat.com> * libstdc++-v3: New directory. From-SVN: r33317
165 lines
4.9 KiB
Plaintext
165 lines
4.9 KiB
Plaintext
|
|
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 <A> // file <B>
|
|
#ifndef A #ifndef B
|
|
#define A 1 #define B 1
|
|
#include <B> #include <A>
|
|
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 <A>
|
|
|
|
The typical effect of such an "include loop" may be seen by tracing
|
|
the preprocessor activity:
|
|
|
|
C // file C.cc
|
|
C #include <A>
|
|
A // file <A>
|
|
A #ifndef A
|
|
A #define A 1
|
|
A #include <B>
|
|
B // file <B>
|
|
B #ifndef B
|
|
B #define B 1
|
|
B #include <A>
|
|
A // file <A>
|
|
A #ifndef A <-- oops, cpp symbol A defined already
|
|
A ... <-- skip <A> 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 <A>
|
|
are not available after the #include <A> 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 <bits/yyy.h> generally, or specifically <bits/std_xxx.h> for
|
|
an equivalent to the standard header <xxx>.
|
|
|
|
Standard headers <xxx> are all placed under directory std/, and
|
|
are ignored except during installation. These headers simply
|
|
#include the corresponding header <bits/std_xxx.h>.
|
|
|
|
Standard substitute headers <bits/std_xxx.h> 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 <A> and <B>
|
|
above, as follows:
|
|
|
|
// file <bits/std_A.h>
|
|
#ifndef _CPP_A
|
|
#define _CPP_A
|
|
# include <bits/A_types.h>
|
|
# include <bits/B_types.h>
|
|
# include <bits/A_funs.h>
|
|
#endif
|
|
|
|
// file <bits/std_B.h>
|
|
#ifndef _CPP_B
|
|
#define _CPP_B
|
|
# include <bits/A_types.h>
|
|
# include <bits/B_types.h>
|
|
# include <bits/B_funs.h>
|
|
#endif
|
|
|
|
// file <bits/A_types.h>
|
|
#ifndef _CPP_BITS_A_TYPES_H
|
|
#define _CPP_BITS_A_TYPES_H
|
|
typedef int A_type;
|
|
#endif
|
|
|
|
// file <bits/B_types.h>
|
|
#ifndef _CPP_BITS_B_TYPES_H
|
|
#define _CPP_BITS_B_TYPES_H
|
|
typedef int B_type;
|
|
#endif
|
|
|
|
// file <bits/A_funs.h>
|
|
#ifndef _CPP_BITS_A_FUNS_H
|
|
#define _CPP_BITS_A_FUNS_H
|
|
extern B_type g(A_type);
|
|
#endif
|
|
|
|
// file <bits/B_funs.h>
|
|
#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 <std/A>
|
|
#ifndef _CPP_A
|
|
#define _CPP_A
|
|
# include <bits/std_A.h>
|
|
#endif
|
|
|
|
// file <std/B>
|
|
#ifndef _CPP_B
|
|
#define _CPP_B
|
|
# include <bits/std_B.h>
|
|
#endif
|
|
|
|
Notice that the include guards are named uniformly except that
|
|
the guard for standard header <bits/std_A.h> is just _CPP_A,
|
|
identically as the header <A> 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 <bits/std_xxxx.h> should sub-include
|
|
other headers.
|
|
|
|
|