views:

777

answers:

6

Consider this (horrible, terrible, no good, very bad) code structure:

#define foo(x) // commented out debugging code

// Misformatted to not obscure the point
if (a)
foo(a);
bar(a);

I've seen two compilers' preprocessors generate different results on this code:

if (a)
bar(a);

// and

if (a)
;
bar(a)

Obviously, this is a bad thing for a portable code base.

My question: What is the preprocessor supposed to do with this? Elide comments first, or expand macros first?

+2  A: 

According to MSDN, comments are replaced with a single space in the tokenization phase, which happens before the preprocessing phase where macros are expanded.

Jim Lewis
+4  A: 

Unfortunately, the original ANSI C Specification specifically excludes any Preprocessor features in section 4 ("This specification describes only the C language. It makes no provision for either the library or the preprocessor.").

The C99 specification handles this explicity, though. The comments are replaced with a single space in the "translation phase", which happens prior to the Preprocessing directive parsing. (Section 6.10 for details).

VC++ and the GNU C Compiler both follow this paradigm - other compilers may not be compliant if they're older, but if it's C99 compliant, you should be safe.

Reed Copsey
sorry, but what you link to is __not__ the ANSI C spec; the actual spec describes the translation phases in section 2.1.1.2; I posted an overview over these phases not long ago: http://stackoverflow.com/questions/1476892/poster-with-the-8-phases-of-translation-in-the-c-language/1479972#1479972
Christoph
Yeah - not sure. I always use (mostly) C99 compliant compilers. It looks like the OP is using a C99 compiler, though, since // as comments didn't exist in C89.
Reed Copsey
Many C compilers I've come across support the C++/C99 '//' comments even if they don't support anything else from C99.
Michael Burr
And I tagged it C99
Novelocrat
@Novelocrat: As I said, if you're using C99, you're safe. Technically, though, there are few fully compliant C99 compilers (MS and GNU both are not 100% compliant, for example).
Reed Copsey
+1  A: 

As described in this copy-n-pasted decription of the translation phases in the C99 standard, removing comments (they are replaced by a single whitespace) occurs in translation phase 3, while preprocessing directives are handled and macros are expanded in phase 4.

In the C90 standard (which I only have in hard copy, so no copy-n-paste) these two phases occur in the same order, though the description of the translation phases is slightly different in some details from the C99 standard - the fact that comments are removed and replaced by a single whitespace character before preprocessing directives are handled and macros expanded is not different.

Again, the C++ standard has these 2 phases occur in the same order.

As far as how the '//' comments should be handled, the C99 standard says this (6.4.9/2):

Except within a character constant, a string literal, or a comment, the characters // introduce a comment that includes all multibyte characters up to, but not including, the next new-line character.

And the C++ standard says (2.7):

The characters // start a comment, which terminates with the next newline character.

So your first example is clearly an error on the part of that translator - the ';' character after the foo(a) should be retained when the foo() macro is expanded - the comment characters should not be part of the 'contents' of the foo() macro.

But since you're faced with a buggy translator, you might want to change the macro definition to:

#define foo(x) /* junk */

to workaround the bug.

However (and I'm drifting off topic here...), since line splicing (backslashes just before a new-line) occurs before comments are processed, you can run into something like this bit of nasty code:

#define evil( x) printf( "hello "); // hi there, \
                 printf( "%s\n", x); // you!



int main( int argc, char** argv)
{
    evil( "bastard");

    return 0;
}

Which might surprise whoever wrote it.

Or even better, try the following, written by someone (certainly not me!) who likes box-style comments:

int main( int argc, char** argv)
{
                            //----------------/
    printf( "hello ");      // Hey, what the??/
    printf( "%s\n", "you"); // heck??         /
                            //----------------/
    return 0;
}

Depending on whether your compiler defaults to processing trigraphs or not (compilers are supposed to, but since trigraphs surprise nearly everyone who runs across them, some compilers decide to turn them off by default), you may or may not get the behavior you want - whatever behavior that is, of course.

Michael Burr
Or move the comment off the #define line.
jmucchiello
The point of the question is that `junk` is actual code, that was commented out when not in use for debugging.
Novelocrat
This answer is quite good, and contains the gem of truth that really helps me - use `/* */`-style comments in macros and I'm safe.
Novelocrat
+1  A: 

Never put // comments in your macros. If you must put comments, use /* */. In addition, you have a mistake in your macro:

#define foo(x) do { } while(0) /* junk */

This way, foo is always safe to use. For example:

if (some condition)
    foo(x);

will never throw a compiler error regardless of whether or not foo is defined to some expression.

Vitali
`junk` is the code that is sometimes the body of the macro. My predecessors were not so careful.
Novelocrat
Can you please clarify what you mean? It's tough to give advice without a concrete example.
Vitali
A: 
Joshua
A: 

Is there a way to do something like this:

#include <iostream>
using namespace std;

#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr // cerr
#endif

int main() {
   _cerr << "TEST message" << endl;
}

So that when _TEST_ is not defined, some lines of code are commented, or removed from the code. From the previous messages I got that comments are processed before the rest, so this code is wrong. But how can I get the behaviour I need without using #ifdefs explicitely?

Pietro2