69

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?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
uj2
  • 2,255
  • 2
  • 21
  • 32

3 Answers3

69

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__) )
Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
  • `__VA_ARGS__` isn't part of standard C++ yet. Does the draft standard actually specify what the behavior should be in this case? – bk1e Feb 27 '11 at 17:42
  • 1
    @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. – Ise Wisteria Feb 27 '11 at 19:45
  • 2
    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. – Virus721 Sep 04 '15 at 12:37
  • @Virus721 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.) – melpomene Sep 07 '15 at 11:38
  • @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. – Ise Wisteria Nov 22 '15 at 07:11
  • 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. https://en.cppreference.com/w/cpp/keyword/and – John P Sep 10 '17 at 23:08
  • 3
    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... – solstice333 Feb 01 '18 at 18:30
  • 2
    @IseWisteria, @rbrich already mentioned it below that there is now the compiler switch `/Zc:preprocessor` see [here](https://learn.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview?view=msvc-160) which will unpack `__VA_ARGS__` correctly. Maybe worth editing your answer to include this in addition to your original solution? – hassec Feb 18 '21 at 09:37
  • @hassec: Thank you for informing!(also thank you @rbrich!) – Ise Wisteria Feb 19 '21 at 01:05
31

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.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
dmitryvolk
  • 331
  • 3
  • 3
  • 6
    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. – jcl Aug 29 '14 at 20:35
  • 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. – John P Sep 10 '17 at 23:24
  • 3
    FWIW, here's [another bug](https://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement) 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). – BeeOnRope Nov 22 '17 at 19:39
  • 4
    There is now [`/experimental:preprocessor`](https://learn.microsoft.com/en-us/cpp/build/reference/experimental-preprocessor?view=vs-2019) or `/Zc:preprocessor` compiler switch which makes the preprocessor behave correctly. It fixed the issue for me. – rbrich Jun 22 '20 at 11:13
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.

chys
  • 1,546
  • 13
  • 17