user4696876
user4696876

Reputation: 35

Variadic macro argument count not working as expected

So, basically I'm trying to implement a macro to count the number of arguments in VA_ARGS.

For the sake of simplicity it only works up to 3 parameters. The problem is that when the macro is used with less than 3 parameters, it doesn't work, and triggers the "expected an expression" error.

#define EXPAND( x ) x
#define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, PP_RSEQ_N()))
#define PP_ARG_N(_1, _2, _3, N,...) N
#define PP_RSEQ_N() 3,2,1,0

void main()
{
    printf("\nTEST PP_NARG: %i", PP_NARG());        //Doesn't work (in this case it shouldn't work, so it's correct)
    printf("\nTEST PP_NARG: %i", PP_NARG(0));       //Doesn't work
    printf("\nTEST PP_NARG: %i", PP_NARG(0,0));     //Doesn't work
    printf("\nTEST PP_NARG: %i", PP_NARG(0,0,0));   //Works
}

Keeping just the line that works it compiles correctly and prints "TEST PP_NARG: 3".

I believe the problem might be that PP_RSEQ_N() is only expanding to "3", instead of "3,2,1,0" for some reason, since even if PP_RSEQ_N() is defined as this

#define PP_RSEQ_N() 10,9,8,7,6,5,4,3,2,1,0

it still doesn't work with less than 3 parameters.

Im using the MSVC compiler and it may be the cause of the problem, since it doesn't behave very well with macros, as seen here: MSVC doesn't expand __VA_ARGS__ correctly

Upvotes: 2

Views: 678

Answers (2)

lgnom
lgnom

Reputation: 160

A PP_ARG_N() implementation which can also distinguish between invocation with and without parameter can be found here (kudos to Scott Morrison). Response for your little program:

TEST PP_NARG: 0
TEST PP_NARG: 1
TEST PP_NARG: 2
TEST PP_NARG: 3

Upvotes: 0

H Walters
H Walters

Reputation: 2674

In your implementation PP_RSEQ_N() is an argument to PP_ARG_N. As an argument, it is only expanded in the argument substitution phase of preprocessing, but that only happens just prior to replacing the argument in its replacement list (so long as, in the replacement list, it's not being stringified and is not participating in a paste).

Since PP_ARG_N only has its fourth argument N in its replacement list, PP_RSEQ_N() will only expand if you happen to pass three arguments in. (There is a second scan during the rescan and replacement phase, which applies after argument substitution... but that has no effect here as PP_RSEQ_N() is mentioned in a call).

Get rid of this macro and just put it in the PP_NARG like this:

#define EXPAND( x ) x
#define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 3,2,1,0))
#define PP_ARG_N(_1, _2, _3, N,...) N

...and things "work" fine:

PP_NARG() expands to 1
PP_NARG(x) expands to 1
PP_NARG(x,y) expands to 2

Note, however, that PP_NARG() doesn't give you 0. Arguably that's actually correct; to the preprocessor, this is not passing zero arguments. It's passing one argument that is just empty. It's the same thing as #define X(A) OPEN A CLOSE/X() yielding OPEN CLOSE. If for some reason you want this to expand to 0, there may be some finagling to get that to happen, but for this answer I'm only focusing on getting you over this one hump.

Upvotes: 2

Related Questions