7

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

Community
  • 1
  • 1
vim
  • 454
  • 4
  • 11
  • 3
    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 Jul 22 '14 at 18:17
  • @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. :) –  Jul 22 '14 at 18:19
  • 2
    @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 Jul 22 '14 at 18:20
  • @chris Oh, sure, I wasn't disputing that, and have amended my previous comment to make it clearer what my point was. –  Jul 22 '14 at 18:21
  • possible duplicate of [MSVC doesn't expand \_\_VA\_ARGS\_\_ correctly](http://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly) – Gohan Jul 22 '14 at 18:36
  • You can dump the Microsoft preprocessor output to a file, or to stdout by adding /E or /P to confirm what it is doing. http://msdn.microsoft.com/en-us/library/3xkfswhy.aspx – Moby Disk Jul 22 '14 at 18:36
  • 2
    By the way, you're using [reserved identifiers](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier). – chris Jul 22 '14 at 18:54

2 Answers2

3

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.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Thanks, this is exactly what I wanted. But I forgot to mention that it should expand to nothing if no arguments provided. This implementation expands to `_arg1` – vim Jul 23 '14 at 11:36
  • @vim, I can't really think of an easier way than making use of some [`ISEMPTY` macro](http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/) as the condition of `BOOST_PP_IF` in the first macro with nothing as the true branch and this as the false branch. – chris Jul 23 '14 at 12:11
  • I've tried that, but the problem is that macros with no argument and macros with one argument both have arguments size of 1, so it's hard to tell what should we test in this case. – vim Jul 23 '14 at 12:46
  • 1
    @vim, You'd test `ISEMPTY(__VA_ARGS__)`. When called with no arguments, the macro is given one *empty* argument. – chris Jul 23 '14 at 12:50
  • I don't think I have the right mindset for this macros shenanigans, another problem came up and I'm stuck again. :) BOOST_PP_IF branching requires some extra parentheses to True and/or False statements evaluation, which are eventually expanded to `(int _arg3, bool _arg2, float _arg1)` instead of `int _arg3, bool _arg2, float _arg1`. Can't think of a way to remove them. – vim Jul 23 '14 at 15:18
  • @vim, It takes a while. I only started learning how to use Boost.PP after developing a sudden great interest in what the preprocessor was capable of. – chris Jul 23 '14 at 15:24
  • 1
    @vim, Yeah, my advice was wrong. I forgot that as soon as you put the existing macro into `BOOST_PP_IF`, it puts extra commas in, which means extra arguments to `BOOST_PP_IF`. I've [amended the example](http://coliru.stacked-crooked.com/a/28a79142fd4e1c2e) to either call the existing macro with the variadic data or call the result of `BOOST_PP_TUPLE_EAT()`, which just ignores the arguments and expands to nothing. – chris Jul 23 '14 at 15:35
  • It's really hard to read, but it works exactly how I want, thanks a lot. By the way, `BOOST_PP_IS_EMPTY` can be used instead of `ISEMPTY`. – vim Jul 23 '14 at 16:16
  • @vim, Considering it's not in [this reference](http://www.boost.org/doc/libs/1_55_0/libs/preprocessor/doc/index.html), I think `BOOST_PP_IS_EMPTY` is an implementation detail. – chris Jul 23 '14 at 16:22
3

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.