Добавление к __VA_ARGS__ - PullRequest
       46

Добавление к __VA_ARGS__

13 голосов
/ 29 октября 2010

Я знаю, что могу сделать это:

#define MACRO(api, ...) \
  bool ret = api(123, ##__VA_ARGS__);

Это всего лишь пример, это часть более сложного решения. Дело в том, что мне нужно добавить переменное количество аргументов к первым 123. ## заставляет компилятор убирать запятую после аргумента 123, если в MACRO не было передано ни одного аргумента.

Но теперь я хочу добавить аргументы в API, вот так:

#define MACRO(api, ...) \
  bool ret = api(__VA_ARGS__##, 456);

Нокандо. Одним из решений является использование двух макросов, скажем, MACRO и MACRO_V, и чтобы версия _V не обрабатывала никаких аргументов. Но есть ли способ заставить его работать с одним макросом?

Ответы [ 3 ]

17 голосов
/ 09 февраля 2012

Да, вы можете. Следующее поддерживает до 4 аргументов, но его можно тривиально расширить для поддержки more:

#define MACRO(api, ...) \
    bool ret = api(__VA_ARGS__ VA_COMMA(__VA_ARGS__) 456)

/*
 * VA_COMMA() expands to nothing if given no arguments and a comma if
 * given 1 to 4 arguments.  Bad things happen if given more than 4
 * arguments.  Don't do it.
 */
#define VA_COMMA(...) GET_6TH_ARG(,##__VA_ARGS__,COMMA,COMMA,COMMA,COMMA,)
#define GET_6TH_ARG(a1,a2,a3,a4,a5,a6,...) a6
#define COMMA ,

/* EXAMPLES */
MACRO(foo)                       /* bool ret = foo( 456)              */
MACRO(foo,1)                     /* bool ret = foo(1 , 456)           */
MACRO(foo,1,2,3,4)               /* bool ret = foo(1,2,3,4 , 456)     */
/* uh oh, too many arguments: */
MACRO(foo,1,2,3,4,5)             /* bool ret = foo(1,2,3,4,5 5 456)   */
MACRO(foo,1,2,3,4,5,6)           /* bool ret = foo(1,2,3,4,5,6 5 456) */

Этот же трюк используется для:

Объяснение

VA_COMMA окружает свои аргументы (__VA_ARGS__) шестью дополнительными аргументами: один пустой аргумент перед (не должен быть пустым & mdash; он отброшен) и четыре запятых и пустой аргумент после.

Эти шесть или более аргументов передаются в GET_6TH_ARG, который, как следует из его названия, расширяется до шестого аргумента. Все остальные аргументы отбрасываются.

Таким образом, MACRO(foo) раскрывается так:

step 0: MACRO(foo)
step 1: bool ret = foo( VA_COMMA() 456)
step 2: bool ret = foo( GET_6TH_ARG(,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo( 456)

MACRO(foo,1) расширяется так:

step 0: MACRO(foo,1)
step 1: bool ret = foo(1 VA_COMMA(1) 456)
step 2: bool ret = foo(1 GET_6TH_ARG(,1,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo(1 COMMA 456)
step 4: bool ret = foo(1 , 456)

MACRO(foo,1,2) расширяется так:

step 0: MACRO(foo,1,2)
step 1: bool ret = foo(1,2 VA_COMMA(1,2) 456)
step 2: bool ret = foo(1,2 GET_6TH_ARG(,1,2,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo(1,2 COMMA 456)
step 4: bool ret = foo(1,2 , 456)

MACRO(foo,1,2,3,4,5) расширяется так:

step 0: MACRO(foo,1,2,3,4,5)
step 1: bool ret = foo(1,2,3,4,5 VA_COMMA(1,2,3,4,5) 456)
step 2: bool ret = foo(1,2,3,4,5 GET_6TH_ARG(,1,2,3,4,5,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo(1,2,3,4,5 5 456)
4 голосов
/ 05 ноября 2010

Нет. Поведение ##, которое позволяет это работать в первом случае, является расширением GCC (C99 не допускает пустую часть аргумента переменной), и это особенно относится к случаю с запятой слева и __VA_ARGS__ справа. Смотрите, например http://gcc.gnu.org/onlinedocs/gcc-4.5.1/gcc/Variadic-Macros.html#Variadic-Macros (внизу страницы).

3 голосов
/ 14 марта 2014

Ну, я думаю, что это возможно с чем-то вроде этого:

#define NO_FIRST(first, ...) __VA_ARGS__
#define DO_APPEND_LAST(last, ...) NO_FIRST(__VA_ARGS__, last)
#define MACRO(api, ...) bool ret = api(DO_APPEND_LAST(456, dummy, ##__VA_ARGS__));

Не проверял, но должен работать в последних VS и gcc.

...