How to implement EXCLUDE
The following code works as expected in GCC, but not in MSVC.
#define _EXCLUDE_FIRST_ARG_(first, ...) __VA_ARGS__
#define EXCLUDE_FIRST_ARG(...) _EXCLUDE_FIRST_ARG_(__VA_ARGS__)
example:
EXCLUDE_FIRST_ARG(a, b, c, d, e)
will evaluate in GCC(also checked in CLANG)
b, c, d, e
But when I using MSVC with this example, the result is empty.
And when I checked first
arguments in _EXCLUDE_FIRST_ARG_
, I found that the arguments a, b, c, d, e
were all bind to first
parameter and left ...
with nothing.
Is this because of MSVC preprocessor lacking of Argument Prescan feature or MSVC violating some c++ standards?
And how can I implement this EXCLUDE_FIRST_ARG functionality when using MSVC?
Is this because of MSVC preprocessor lacking of Argument Prescan feature or MSVC violating some c++ standards?
Here's how it works as best as I've been able to piece it together in Microsoft.
1. Macro definitions:
#define _EXCLUDE_FIRST_ARG_(first, ...) __VA_ARGS__
#define EXCLUDE_FIRST_ARG(...) _EXCLUDE_FIRST_ARG_(__VA_ARGS__)
...cause the macros to be defined, obviously.
2. Invocation
EXCLUDE_FIRST_ARG(a, b, c, d, e)
MS's preprocessor recognizes a function-like macro EXCLUDE_FIRST_ARG
is defined, so off to expansion:
2a. Argument identification
The macro's defined with formal parameter ...
; it's invoked with arguments a, b, c, d, e
. This is a variadic with no named parameters, so all arguments bind to the variadic parameter.
2b. Argument substitution
__VA_ARGS__
is mentioned in the replacement list for EXCLUDE_FIRST_ARG
. It's not being stringified or participating in a paste. This makes it eligible for argument substitution. Before replacing the parameters, the expression:
a, b, c, d, e
...is evaluated by the preprocessor. Nothing is special about these (no function-like macro invocations are done; and none of the arguments are object-like macros), so the evaluation is the same; that is, the evaluated arguments are:
a, b, c, d, e
By rules of argument substitution, that replaces the parameter "name" in the replacement list. Once that occurs, we have this:
_EXCLUDE_FIRST_ARG_(a, b, c, d, e)
Up to this point, all other major preprocessors do the same thing. But MS's preprocessor expresses a peculiar behavior when expanding __VA_ARGS__
; specifically, the peculiarity is that the entire replacement is only one token. You might see commas in that expansion; so do I; but to MS specifically it's still just one token.
2[between b and c]. Stringification and paste, in no particular order
This is just a semantic placeholder in this account. Here's where these operations would occur, but since we're doing neither nothing happens in this case.
2c. Rescan and further replacement
At this step, the resulting replacement list itself:
_EXCLUDE_FIRST_ARG_(a, b, c, d, e)
...is rescanned for further macros. During this rescan, the preprocessor notices that the object-like macro _EXCLUDE_FIRST_ARG_
is being called with "the argument" a, b, c, d, e
(to other major preprocessors, this is five arguments).
2c.a. Argument identification
_EXCLUDE_FIRST_ARG_
has one named parameter first
, and a variadic. NOTE: Technically this must be called with at least 2 arguments in pre-C++20 preprocessors; many major preprocessors (MS's included) accept one fewer.
For MS's preprocessor, this is called with one argument: a, b, c, d, e
, which is associated with the parameter first
. The variadic argument ...
is empty.
For most other major preprocessors, this is called with five arguments. first
is associated with a
; ...
with b, c, d, e
.
2c.b. Argument substitution
For MS's preprocessor, there is no mention of first
in the replacement list, so nothing about the associated argument is done. __VA_ARGS__
(which is not being stringified/pasted) doesn't qualify for MS's comma elision feature, so the empty variadic argument results in __VA_ARGS__
being replaced with a placemarker.
For most other major preprocessors, there's still no mention of first
in the replacement list, so nothing happens to a
. __VA_ARGS__
(not being stringified/pasted) is mentioned, so b, c, d, e
is evaluated, resulting in b, c, d, e
; that result replaces __VA_ARGS__
.
2c.c. Rescan and further replacement
For MS's preprocessor, nothing interesting is here. Since it's done, the placemarker is removed.
For the other preprocessors, b, c, d, e
is rescanned again resulting in the same.
In this case you can separate the parameter list from the macro using a helper:
#define _EXCLUDE_FIRST_ARG_(first, ...) __VA_ARGS__
#define CALL(A,B) A B
#define EXCLUDE_FIRST_ARG(...) CALL(_EXCLUDE_FIRST_ARG_,(__VA_ARGS__))
EXCLUDE_FIRST_ARG(a, b, c, d, e)
Roughly, EXCLUDE_FIRST_ARG
expands to CALL(_EXCLUDE_FIRST_ARG_,(a, b, c, d, e))
in an as stage with this approach. During rescan that invokes CALL
, which during as expands to _EXCLUDE_FIRST_ARG_
, and (a, b, c, d, e)
, after each is fully evaluated. Then during CALL
's rescan, the parameter list looks like multiple tokens again.
上一篇: MSVC相当于gcc / clang的
下一篇: 如何实现EXCLUDE