2

In the very simple C program below, what is the expected compiler error? gcc is giving me 1 whereas MSVC 2013 is giving me 2.

#define foo
#define bar (defined(foo))

#if bar
#error 1
#else
#error 2
#endif

My questions are hopefully equally simple:

  1. What does the C spec say about the value of defined()? I can't seem to find anything that talks about setting its value to another macro.
  2. The actual code is not something I have control over and "#if bar" is used all over the place. What is the simplest way to change the #define so that #if bar will work as "expected" in MSVC? The only thing I can think of is to expand it out:

.

#ifdef foo
#define bar 1
#else
#define bar 2
#endif
o.c.
  • 271
  • 1
  • 3
  • 12

2 Answers2

2

The C spec says:

§6.10.1/1 The expression ... may contain unary operator expressions of the form defined identifier or defined(identifier) which evaluate to 1 if the identifier is currently defined as a macro name (that is, if it is predefined or if it has been the subject of a #define preprocessing directive without an intervening #undef directive with the same subject identifier), 0 if it is not.

§6.10.1/4 macro invocations in the list of preprocessing tokens that will become the controlling constant expression are replaced (except for those macro names modified by the defined unary operator), just as in normal text. If the token defined is generated as a result of this replacement process or use of the defined unary operator does not match one of the two specified forms prior to macro replacement, the behavior is undefined. After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0, and then each preprocessing token is converted into a token.

(emphasis mine) However, how macro replacement is very complex, and I think MSVC is defining foo as defined(bar) which is undefined behavior, wheras GCC is defining foo as 1 correctly. Since MSVC is then in undefined behavior, it does strange things.

As you say, the easiest fix is

#ifdef foo
#define bar 1
#else
#define bar 2
#endif
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • 1
    I don't think there's anything wrong with what MSVC does. In this case, *the token `defined` is generated as a result of … replacement process*. The specification clearly says it is UB, thus an implementation vendor is free to choose any course of action. The compiler might even include both branches of the conditional fragment, it would be a legal source of fun. – ach Dec 03 '14 at 19:37
  • @AndreyChernyakhovskiy: I also concluded UB, but in rereading, my reasoning was wrong; you're right. – Mooing Duck Dec 03 '14 at 20:09
  • I stopped reading the spec one paragraph too soon. I figured that it is UB but wanted to make sure. Thanks for the help! – o.c. Dec 04 '14 at 00:03
0

I believe the compiler will see everything after the name/macro being defined as part of the definition of the name/macro, that is, as program text, not as macro text.

int defined(char s);    // prototype of a function named "defined"

#define foo
#define bar defined(foo)

"bar" anywhere in the program text will now be replaced with a call to defined() with no argument (as "foo" is defined as empty).

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • `defined` is a magic preprocessor function> Any C/C++ function by the same name is probably ignored. (Having such a thing may be undefined behavior, I'd have to check) – Mooing Duck Dec 03 '14 at 20:12
  • @MooingDuck, why do you think so? `defined` is only defined to have a special meaning in `#if` and `#elif` conditional expression. It is entirely legal to have `defined` as an identifier otherwise. – ach Dec 03 '14 at 20:32
  • I confirmed that `defined` is not a keyword, so yeah, it's it's only defined to have special meaning in `if` and `elif`, in which contexts, any C/C++ function by the same name is presumably ignored. (I vageuly wonder what compilers do if one has `#define defined(x) 1`) – Mooing Duck Dec 03 '14 at 20:42
  • And why would 'defined' as program text be ignored? If it is not a C/C++ keyword, if it is not removed by the preprocessor (because it is not used in #if/elif), it remains as program text and will be compiled. – Paul Ogilvie Dec 04 '14 at 08:47