Preprocessor question

Hello,

I have a question about macro expansion: is it valid for an #include file to end during the arguments of a macro? That is, suppose I have two source files:

/* test.h */
TEST(f,

and

/* test.c */
#define TEST(name,val) int name() { return val; }
#include "test.h"
1)

Does test.c expand to

int f() { return 1; }

or is that undefined behavior? clang --std=c99 -E test.c gives the expansion, but gcc --std=c99 -E test.c gives the error:

In file included from test.c:2:0:
test.h:1:0: error: unterminated argument list invoking macro "TEST"
  TEST(f,

My reading of the standard is that gcc is wrong; the only thing anywhere close to supporting it is 6.10.3 paragraph 11, which says that the presence of preprocesing tokens in the arguments of a macro is undefined behavior. I don't think it applies to this example, does it?

I'm asking because I'm writing a C preprocessor, and I just now thought of this. With the way I structured things it's really hard to make that example work, so I really want gcc to be right :) Should I just rewrite the way I'm handling #includes, or is gcc right?

8 comments · 84% upvoted · other discussions

comments

Neui

I found this at page 29 in your linked document (emphasis by me):

4. Preprocessing directives are executed, macro invocations are expanded, and_Pragma unary operator expressions are executed. If a character sequence that matches the syntax of a universal character name is produced by token concatenation (6.10.3.3), the behavior is undefined. A #include preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted

And in phase 3:

3. The source file is decomposed into preprocessing tokens and sequences of white-space characters (including comments). A source file shall not end in a partial preprocessing token or in a partial comment. Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is implementation-defined.

So it looks like gcc behavior is correct. Since it says "preprocessing token", i'd guess that normal C code wouldn't be affected.

moefh [OP]

Thanks! I should have paid more attention to the translation phases.

[deleted]

[deleted]

Neui

Page 182 (emphasis by me):

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.

OP used #include "test.h", and the spec talks about source file, so test.h is treated as a source file. Above the text in the spec for the <xyz> version talks about headers.

Also, I've looked what a header exactly is, and I found this at the bottom of page 199:

A header is not necessarily a source file,

So a header might be a source file, but it might also not (eg. implemented in the compiler itself I could imagine)

BootDisc

I am sharing this at work. I am not sure. I see why GCC claims it does not work, but honesty, am going to need to read the spec on a computer. Hopefully this comment helps me remember to start the debate later, we love stuff like this.

[deleted]

nice find, it's undocumented by the C spec afaik but I could be wrong.

if you were writing an implementation I reckon support it, perhaps issue a warning as well depending on the verbosity level. (clang could take it a step further in that regard)

if you were considering using it in your software, I'd reckon find another way to do it -- even if it were formally supported in the documentation, you'll probably want real support from compilers that reject it somewhere down the line (or even GCC right now) and realistically it is kind of a flaky construct. :p

bit_inquisition

Macro expansion happens in isolation so that what I suspect is happening. I can't tell you the exact sequence though.

OldWolf2

No.

nerd4code

EOF might count as a preprocessing token.