MSVC doesn't expand __VA_ARGS__ correctly
Asked Answered
R

3

73

Consider this code:

#define F(x, ...) X = x and VA_ARGS = __VA_ARGS__
#define G(...) F(__VA_ARGS__)
F(1, 2, 3)
G(1, 2, 3)

The expected output is X = 1 and VA_ARGS = 2, 3 for both macros, and that's what I'm getting with GCC, however, MSVC expands this as:

X = 1 and VA_ARGS = 2, 3
X = 1, 2, 3 and VA_ARGS =

That is, __VA_ARGS__ is expanded as a single argument, instead of being broken down to multiple ones.

Any way around this?

Renayrenckens answered 27/2, 2011 at 16:59 Comment(0)
C
75

Edit: This issue might be resolved by using /Zc:preprocessor or /experimental:preprocessor option in recent MSVC. For the details, please see here.

MSVC's preprocessor seems to behave quite differently from the standard specification.
Probably the following workaround will help:

#define EXPAND( x ) x
#define F(x, ...) X = x and VA_ARGS = __VA_ARGS__
#define G(...) EXPAND( F(__VA_ARGS__) )
Chlo answered 27/2, 2011 at 17:24 Comment(9)
__VA_ARGS__ isn't part of standard C++ yet. Does the draft standard actually specify what the behavior should be in this case?Genaro
@bk1e: Sorry, as I don't have the ability, I can't explain the preprocess in upcoming C++ standard in detail here, but it is unlikely to be quite different from C99.Chlo
Could someone please explain this ? Is "and VA_ARGS = VA_ARGS" a valid piece of C code or is it just a human readable text that is here as a comment ? if this is valid code, what is "and VA_ARGS = VA_ARGS" doing ? Thank you.Donata
@Donata It was there in the original question. It's human readable text that demonstrates the result of macro expansion after the preprocessor is done with it. Or rather. it's more like a template: It's literal text except for the __VA_ARGS__ bit, which gets expanded (It's not valid C code but we're only interested in preprocessing here.)Aeroplane
@q126y: Sorry, I can't explain the technical reason. I saw Microsoft Connect's page reporting this as a bug before, but the page seems unavailable now. I think this behavior is a bug of MSVC, and EXPAND macro in the answer is a pure workaround.Chlo
I don't know C that well, but and is a C++ alias for &&, along with or = ||, etc. I couldn't find these in C keyword lists but I'm finding it difficult to search for words like "and" and "or" and exclude "C++" while searching for "C" (I'd love to know how to communicate 'I mean what I said' to search engines when quotes alone don't do the job.) If you're using C macros but compiling C++, and is both legible and valid; I assume the visual ambiguity (&& or &?) and possibly the language barrier or precedent/convention/style make it rare. en.cppreference.com/w/cpp/keyword/andTeddi
Oh I think I see...let's say that __VA_ARGS__ is 1, 2 instead for simplicity. EXPAND(x) x takes F(__VA_ARGS__)) and replaces it with literally F(1, 2) so that F(1, 2) gets processed instead of F(__VA_ARGS__) where __VA_ARGS__ is seen as a single entity if that makes sense...Liftoff
@IseWisteria, @rbrich already mentioned it below that there is now the compiler switch /Zc:preprocessor see here which will unpack __VA_ARGS__ correctly. Maybe worth editing your answer to include this in addition to your original solution?Anesthesiology
@hassec: Thank you for informing!(also thank you @rbrich!)Chlo
A
32

I posted the following Microsoft support issue:

The following program gives compilation error because the precompiler expands __VA_ARGS__ incorrectly:

#include <stdio.h>

#define A2(a1, a2) ((a1)+(a2))

#define A_VA(...) A2(__VA_ARGS__)

int main(int argc, char *argv[])
{
    printf("%d\n", A_VA(1, 2));
    return 0;
}

The preprocessor expands the printf to: printf("%d\n", ((1, 2)+()));

instead of printf("%d\n", ((1)+(2)));

I received the following unsatisfying answer from a Microsoft compiler team developer:

Hi: The Visual C++ compiler is behaving correctly in this case. If you combine the rule that tokens that match the '...' at the inital macro invocation are combined to form a single entity (16.3/p12) with the rule that sub-macros are expanded before argument replacement (16.3.1/p1) then in this case the compiler believes that A2 is invoked with a single argument: hence the error message.

Assimilative answered 18/9, 2011 at 6:11 Comment(4)
Thanks for passing along MS's rationale. It seems they are interpreting "combined to form a single item" in 16.3.1/p12 as "combined to form a single, permanently indivisible preprocessor token", which would seem to be less useful. I'd expect the substituted tokens to be reseparated at least for the rescan step given in 16.3.4, which seems to be what other compilers are doing.Calbert
I strongly agree but I'm evidently spoiled by GCC and Clang. Can either of you think of a use case for the MSVC behavior, or is it just a matter of consistency for consistency's sake in spite of expressibility? I'm well out of my element, but "...In this case the compiler believes..." doesn't sound very convincing, let alone useful for anyone trying to write agnostic code. I have a few ideas for workarounds, but my Windows partition is out of commission. I'd like to see someone attempt it in any case. Sorry for the rant and/or necropost.Teddi
FWIW, here's another bug on the same issue, where the team admits it is an error but says it isn't high enough priority to fix (7 years ago).Trisa
There is now /experimental:preprocessor or /Zc:preprocessor compiler switch which makes the preprocessor behave correctly. It fixed the issue for me.Curious
B
0

What version of MSVC are you using? You will need Visual C++ 2010.

__VA_ARGS__ was first introduced by C99. MSVC never attempted to support C99, so the support was not added.

Now, however, __VA_ARGS__ is included in the new C++ standard, C++2011 (previously known as C++0x), which Microsoft apparently plans to support, so it has been supported in recent versions of MSVC.

BTW, you will need to use a .cpp suffix to your source file to get this support. MSVC hasn't updated its C frontend for a long time.

Bootstrap answered 18/9, 2011 at 6:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.