CygnusX1
CygnusX1

Reputation: 21769

Invalid argument count in a variadic macro

I am trying to create a macro that would iterate over a defined list of terms, and for each invoke another macro, possibly with additional argument list. Here is what I have got:

#define ITERATE_OVER_TERMS(MACRO, ...) \
MACRO(Term1, __VA_ARGS__) \
MACRO(Term2, __VA_ARGS__) \
MACRO(Term3, __VA_ARGS__) \
... and so on

However, when I was trying to use it with Visual Studio 2015, i get an error

warning C4003: not enough actual parameters for macro 'BODY'

where BODY is the name of the macro passed as the MACRO argument. While technically a warning, it shows that something has gone wrong with the expansion.

In an attempt to narrow down the error, I reduced my example to the following:

#include <iostream>

#define ITERATE(MACRO, ...) \
MACRO(1, __VA_ARGS__) MACRO(2, __VA_ARGS__)

#define BODY(IterationArg, Arg1, Arg2) \
std::cout << IterationArg << Arg1 << Arg2 << std::endl;

int main() {
  ITERATE(BODY, 8, 9)
    return 0;
}

It gives me the error as shown above, while I expected it to compile succesfully and produce the output

189
289

It does seem to compile with g++, but not Visual Studio. What am I missing? Is there some walkaround for this to work?

Upvotes: 1

Views: 501

Answers (1)

CygnusX1
CygnusX1

Reputation: 21769

The problem is that Visual Studio expands __VA_ARGS__ after they are being passed into a subsequent macro, and not before. This has caused problems in the past as well, for example here -- Why does this variadic argument count macro fail with VC++?

In your case, consider a simple change in the code:

#include <iostream>

#define ITERATE(MACRO, ...) \
MACRO(1, __VA_ARGS__) MACRO(2, __VA_ARGS__)

#define BODY(IterationArg, Arg1, Arg2) \
std::cout << #Arg1 << std::endl;

int main() {
  ITERATE(BODY, 8, 9)
    return 0;
}

The argument #Arg1 is stringified, showing us its contents in the output:

8, 9
8, 9

Not what we expected, huh?

A solution is the same as in the linked question: force an expansion through a dummy EXPAND macro:

#define EXPAND(x) x

#define ITERATE(MACRO, ...) \
EXPAND(MACRO(1, __VA_ARGS__)) EXPAND(MACRO(2, __VA_ARGS__))

This gives you the desired result both in VS and gcc.

Upvotes: 2

Related Questions