7
votes

Long story short, gcc and vc++ preprocessors have different output with the same input. It seems like variadic macros in vc++ doesn't do 'argument matching' (if its the right term) if passed to another macros. For example:

#define MACRO(a, ...)        head:a, tail:MACRO_OTHER(__VA_ARGS__)
#define MACRO_OTHER(a, ...)  head:a, tail:__VA_ARGS__

With

MACRO(1, 2, 3, 4, 5)

gcc output:

head:1, tail:head:2, tail:3,4,5

vc++ output:

head:1, tail:head:2,3,4,5, tail:

Apparently a in MACRO_OTHER is 2,3,4,5 with empty variadic arguments section. With this in mind, is there any way to create a vc++ alternative to the following macro (which works great with gcc)

#define VA_TYPES_WITH_ARGS(...) __VA_TYPES_WITH_ARGS(VA_NUM_ARGS(__VA_ARGS__),##__VA_ARGS__)
#define __VA_TYPES_WITH_ARGS(n, ...) _VA_TYPES_WITH_ARGS(n,##__VA_ARGS__)
#define _VA_TYPES_WITH_ARGS(n, ...) _VA_TYPES_WITH_ARGS_##n(__VA_ARGS__)
#define _VA_TYPES_WITH_ARGS_0()
#define _VA_TYPES_WITH_ARGS_1(type     ) type _arg1
#define _VA_TYPES_WITH_ARGS_2(type, ...) type _arg2, _VA_TYPES_WITH_ARGS_1(__VA_ARGS__)
#define _VA_TYPES_WITH_ARGS_3(type, ...) type _arg3, _VA_TYPES_WITH_ARGS_2(__VA_ARGS__)
// etc

It basically appends _argK for each argument.

Example:

VA_TYPES_WITH_ARGS(int, bool, float)

will expand to

int _arg3, bool _arg2, float _arg1

Any help will be greatly appreciated.


Related preprocessor questions:

Difference between gcc and Microsoft preprocessor

Unexpected Behaviour of GCC and VC++ Preprocessor

2
VC++ is highly crippled when it comes to C99. You can see workarounds all over the place in Boost.Preprocessor. Anyway, you can do what you desire with these workarounds already in place using Boost's BOOST_PP_ENUM macro if I'm not mistaken.chris
@chris But VC++ does aim for C++11, which provides the same functionality as C99 wrt variadic macros and empty macro arguments. At least, if something that's valid C++11 doesn't work in VC++, there's a decent chance VC++ will be fixed to make it work. Not so for C99, indeed. :)user743382
@hvd, Aiming for and achieving are very different things. Just look at all of the variadic template problems that come up. Anyway, the preprocessor is the same between C99 and C++11 minus a few smaller things such as ::, isn't it?chris
@chris Oh, sure, I wasn't disputing that, and have amended my previous comment to make it clearer what my point was.user743382
By the way, you're using reserved identifiers.chris

2 Answers

3
votes

A standalone approach that works for VC++, and breaks with GCC, is:

#define EXPAND(...) __VA_ARGS__
#define LPAREN (
#define RPAREN )

Now, instead of using a macro like x(__VA_ARGS__), use it like so: EXPAND(x LPAREN __VA_ARGS__ RPAREN).

This forces the VC++ preprocessor to scan for arguments later than it normally would.

You should be able to combine the two forms to put the compiler-specific bits in a single place.

Note though that there cannot be a standard C++ answer: you rely on x() being a macro invocation with no arguments. That's not how the preprocessor works in standard C++: if x is a variadic macro, x() invokes the macro with a single argument, and that single argument is empty. Additionally, you rely on being able to omit the variadic arguments: standard C++ also doesn't allow that. If a macro is defined as #define FOO(x,...), invoking it as FOO(1) is invalid. As there cannot be a standard approach, hopefully it is less of a problem for you that the various approaches will necessarily be compiler-specific.

3
votes

You can do this in a reasonably cross-compiler manner (and accept many more arguments with no extra work to boot) by using Boost, which, I can tell you from spending so much time looking at the headers, has many workarounds in place for issues such as these:

#define VA_TYPES_WITH_ARGS(...)   \
    BOOST_PP_ENUM(                \
        BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),    \
        VA_TYPES_WITH_ARGS_MACRO,               \
        BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
    )

#define VA_TYPES_WITH_ARGS_MACRO(z, n, data)       \
    BOOST_PP_TUPLE_ELEM(n, data) BOOST_PP_CAT(     \
        _arg,                                      \
        BOOST_PP_SUB(BOOST_PP_TUPLE_SIZE(data), n) \
    )                                              

VA_TYPES_WITH_ARGS(int, bool, float) //int _arg3 , bool _arg2 , float _arg1

The first macro enumerates (ENUM) over the variadic arguments, calling the second for each and adding joining commas. It forms a tuple from the variadic data to give to the second macro.

The second forms that element (TUPLE_ELEM) followed by _arg concatenated (CAT) with size - n (SUB), where size is the number of elements in the variadic data (given to the macro as a tuple) (TUPLE_SIZE).

See it work.