Search code examples
c++cc-preprocessorc11preprocessor-directive

Cross-file #if and #endif - should it be legal?


According to the C11 standard,

A preprocessing directive of the form

# include "q-char-sequence" new-line

causes the replacement of that directive by the entire contents of the source file identified by the specified sequence between the " delimiters.

So if I have a header file test.h containing:

#endif

And a source file test.c containing:

#if 1
#include "test.h"

Shouldn't it pass the preprocessing phase according to the standard by replacing the content of test.h in place?

But I cannot do this with clang, which says:

In file included from test.c:2:
./test.h:1:2: error: #endif without #if
#endif
 ^
test.c:1:2: error: unterminated conditional directive
#if 1
 ^
2 errors generated.

So what is the behavior that the standard specified?


Solution

  • If you read e.g. this C++ #include reference the included file is first run through translation phases one to four, and phase four is running the preprocessor (recursively).

    That means the file you include must be complete in regards to #if and #endif.

    This happens in C as well.


    After reading the C11 specification (ISO/IEC 9899:2011 [2012]) what I think happens is this:

    The compiler is in the preprocessor phase (phase 4) and evaluates the #if 1 preprocessor directive. The condition evaluates to true, so it enters the block inside the condition. There is sees the #include "test.h" directive.

    When the compiler processes an include directive it temporarily stops processing the current file, to process the included file. This processing of the included file goes through compilation phase 1 through 4 (inclusive) before continuing with the current source file.

    When the processing of the included header file itself comes to phase 4 and starts to process the #endif directive, then it doesn't go up in the recursive inclusion stack to find the matching #if, the preprocessor only looks at the current stack-frame (the current file). Therefore you get the first error about no #if. The standard doesn't actually say anything about this. All it says, basically, is that an #if must have a matching #endif. That the compiler doesn't go up the inclusion stack to find the matching #if seems to be more an implementation detail.

    Anyway, the preprocessor ends its processing of the header file with step 3 in phase 4, which is

    At the end of this phase, all preprocessor directives are removed from the source.

    So when the control comes back to the preprocessing for the source file, the file it actually includes doesn't contain any preprocessing directives. All that is included is an empty file, basically. And this leads to the second error, that there is no #endif for the #if, because there really isn't.