OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libstdc++-v3/] [docs/] [html/] [17_intro/] [HEADER_POLICY] - Rev 20

Compare with Previous | Blame | View Log


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.


Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.