cppinternals: Guard Macros
1
1 The Multiple-Include Optimization
1 *********************************
1
1 Header files are often of the form
1
1 #ifndef FOO
1 #define FOO
1 ...
1 #endif
1
1 to prevent the compiler from processing them more than once. The
1 preprocessor notices such header files, so that if the header file
1 appears in a subsequent '#include' directive and 'FOO' is defined, then
1 it is ignored and it doesn't preprocess or even re-open the file a
1 second time. This is referred to as the "multiple include
1 optimization".
1
1 Under what circumstances is such an optimization valid? If the file
1 were included a second time, it can only be optimized away if that
1 inclusion would result in no tokens to return, and no relevant
1 directives to process. Therefore the current implementation imposes
1 requirements and makes some allowances as follows:
1
1 1. There must be no tokens outside the controlling '#if'-'#endif'
1 pair, but whitespace and comments are permitted.
1
1 2. There must be no directives outside the controlling directive pair,
1 but the "null directive" (a line containing nothing other than a
1 single '#' and possibly whitespace) is permitted.
1
1 3. The opening directive must be of the form
1
1 #ifndef FOO
1
1 or
1
1 #if !defined FOO [equivalently, #if !defined(FOO)]
1
1 4. In the second form above, the tokens forming the '#if' expression
1 must have come directly from the source file--no macro expansion
1 must have been involved. This is because macro definitions can
1 change, and tracking whether or not a relevant change has been made
1 is not worth the implementation cost.
1
1 5. There can be no '#else' or '#elif' directives at the outer
1 conditional block level, because they would probably contain
1 something of interest to a subsequent pass.
1
1 First, when pushing a new file on the buffer stack,
1 '_stack_include_file' sets the controlling macro 'mi_cmacro' to 'NULL',
1 and sets 'mi_valid' to 'true'. This indicates that the preprocessor has
1 not yet encountered anything that would invalidate the multiple-include
1 optimization. As described in the next few paragraphs, these two
1 variables having these values effectively indicates top-of-file.
1
1 When about to return a token that is not part of a directive,
1 '_cpp_lex_token' sets 'mi_valid' to 'false'. This enforces the
1 constraint that tokens outside the controlling conditional block
1 invalidate the optimization.
1
1 The 'do_if', when appropriate, and 'do_ifndef' directive handlers
1 pass the controlling macro to the function 'push_conditional'. cpplib
1 maintains a stack of nested conditional blocks, and after processing
1 every opening conditional this function pushes an 'if_stack' structure
1 onto the stack. In this structure it records the controlling macro for
1 the block, provided there is one and we're at top-of-file (as described
1 above). If an '#elif' or '#else' directive is encountered, the
1 controlling macro for that block is cleared to 'NULL'. Otherwise, it
1 survives until the '#endif' closing the block, upon which 'do_endif'
1 sets 'mi_valid' to true and stores the controlling macro in 'mi_cmacro'.
1
1 '_cpp_handle_directive' clears 'mi_valid' when processing any
1 directive other than an opening conditional and the null directive.
1 With this, and requiring top-of-file to record a controlling macro, and
1 no '#else' or '#elif' for it to survive and be copied to 'mi_cmacro' by
1 'do_endif', we have enforced the absence of directives outside the main
1 conditional block for the optimization to be on.
1
1 Note that whilst we are inside the conditional block, 'mi_valid' is
1 likely to be reset to 'false', but this does not matter since the
1 closing '#endif' restores it to 'true' if appropriate.
1
1 Finally, since '_cpp_lex_direct' pops the file off the buffer stack
1 at 'EOF' without returning a token, if the '#endif' directive was not
1 followed by any tokens, 'mi_valid' is 'true' and '_cpp_pop_file_buffer'
1 remembers the controlling macro associated with the file. Subsequent
1 calls to 'stack_include_file' result in no buffer being pushed if the
1 controlling macro is defined, effecting the optimization.
1
1 A quick word on how we handle the
1
1 #if !defined FOO
1
1 case. '_cpp_parse_expr' and 'parse_defined' take steps to see whether
1 the three stages '!', 'defined-expression' and 'end-of-directive' occur
1 in order in a '#if' expression. If so, they return the guard macro to
1 'do_if' in the variable 'mi_ind_cmacro', and otherwise set it to 'NULL'.
1 'enter_macro_context' sets 'mi_valid' to false, so if a macro was
1 expanded whilst parsing any part of the expression, then the top-of-file
1 test in 'push_conditional' fails and the optimization is turned off.
1