views:

275

answers:

4

I've encountered a problem with the new Visual C++ in VS 2010.

I've got a header with the following defines:

#define STC(y) #y
#define STR(y) STC(\y)
#define NNN(y) 0##y
#define NUM(y) NNN(y)

The intent is that you can have some constant around like

#define TOKEN x5A

and then you can have the token as a number or as a string:

NUM(TOKEN) -> 0x5A
STR(TOKEN) -> "\x5A"

This is the expected behavior under the the substitution rules of macros arguments and so far it has worked well with gcc, open watcom, pellesC (lcc), Digital Mars C and Visual C++ in VS2008 Express.

Today I recompiled the library with VS2010 Express only to discover that it doesn't work anymore! Using the new version I would get:

NUM(TOKEN) -> 0x5A
STR(TOKEN) -> "\y"

It seems that the new preprocessor treats \y as an escape sequence even within a macro body which is a non-sense as escape sequences only have a meaning in literal strings.

I suspect this is a gray area of the ANSI standard but even if the original behavior was mandated by the standard, MS VC++ is not exactly famous to be 100% ANSI C compliant so I guess I'll have to live with the new behavior of the MS compiler.

Given that, does anybody have a suggestion on how to re-implement the original macros behavior with VS2010?

EDIT: Corrected the NUM() macro

EDIT: Possible solution

I think I've found a way to do it:

#define STC(y) #y

#if defined(_MSC_VER) && (_MSC_VER >= 1600)
#define STA(x,y) STC(x##y)
#define STR(y) STA(\,y)
#else 
#define STR(y) STC(\y)
#endif

#define NNN(y) 0##y
#define NUM(y) NNN(y)

Now I get:

#define TOKEN x2E

NUM(TOKEN) -> 0x2E
STR(TOKEN) -> "\x2E"

Of course gcc complains about joining a backslash to a literal (\ ## x2E) because the result is not a valid preprocessor symbol but MS seems happy, hence the #ifdef.

I'll be happy to hear if anyone has a better solution!

+1  A: 

If you use

#define STR(y) STC(\\y) 

then it works for me with VS2010.

Stefan
Unfortunately it doesn't work for me. Expanding `STR(x41)` would give `"\\x41"` which is not what I need (`"\x41"`). Did you add anything else to make it work?
Remo.D
No, it doesn't give "\\x41" but really only "\x41".
Stefan
Ok, so I must have done something different then you. The desired behavior is that within the code STR(x41) is the same as `"A"` while NUM(x41) is the same as `0x41`. In other words if I do `printf("%s\n",STR(x40));` I should get `@` printed (a string with a single character) but with your definition of STR I get `\x40` (a string with 4 characters. If you get `@`, could you suggest where should I look to see why my result is different?
Remo.D
Ups, sorry, I thought you wanted to get "\x40" and not "@" in a printf.
Stefan
A: 

MSVC is not a C compiler, it is a C++ compiler. The C compiler doesn't even offer some C99 features, and it's not intended to be a competitive modern C compiler.

I'm also going to question your use of the preprocessor in this way - it seems more than a little OTT to me.

DeadMG
MSVC is a C89/90 and C++ compiler. It doesn't fully support C99, which still does not mean it can't be used as C compiler.
AndreyT
-1, regardless of the validity of your statements it is not an answer to his question
Daniel
Actually I'm not relying on any C99 feature. It should work in a c89 compiler as well.Regarding the use of those macro, I prefer to define things just once (the `TOKEN` define) rather than having to hunt in the code where that constant is used as part of a string or as a number in case I need to change the value.But it's a matter of taste, I guess.
Remo.D
+4  A: 

You are/were relying on some weird non-standard behavior of the compiler.

Your NUM macro is written incorrectly. It should have never worked, neither in the old nor in the new version of the compiler. When you do NUM(TOKEN) the result of the macro expansion in any standard-compliant compiler will be 0TOKEN and not 0x5A. In order to make your NUM macro work as intended you have to implement it in two-level fashion:

#define NUM_(y) 0##y
#define NUM(y) NUM_(y)

The only reason it "worked" for you so far is just another glitch in the compiler.

I'm not sure yet what's happening with the STR case. The compiler is indeed complaining about an unrecognized escape sequence. Must be a bug in the compiler.

AndreyT
Sorry about that, you're right and the original macro for numbers is what you describe. I was focusing on "STR" part and wrote the NUM on the fly, just to show why I needed that behavior. The orignal macros are more complicated than that.
Remo.D
Just re-reading your comment. Apart from the NUM problem, are you still suggesting that those macros are non standard? It seems to me it is fully compliant with the description of the preprocessor in the standard.
Remo.D
@Remo.D: No, your `STR` looks OK to me. I'd say that it is supposed to work as intended by you. And if it doesn't, it might be a bug in the compiler (unless I'm missing something).
AndreyT
+1  A: 

I think this is the correct definition for STR(y). Worked in a test program under gcc, and appears to use normal macro methods.

#define STC(y)      #y
#define STR(y)      "\\" STC(y)
tomlogic
Unfortunately this is not going to work. Having `"\\" "x41"` is not the same as having `"\x41"`. The first one is a four character strings: {`\ ` , `x`,`4`,`1`,0} and the second is a 1 character string {`A`,0}.Thanks anyway.
Remo.D
So right. So much for late night SOing...
tomlogic