Счетчик аргументов макроса Variadic не работает должным образом

Итак, в основном я пытаюсь реализовать макрос для подсчета количества аргументов в VA_ARGS.

Для простоты работает только до 3 параметров. Проблема в том, что когда макрос используется с менее чем 3 параметрами, он не работает и вызывает ошибку "ожидаемое выражение".

#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
}

Сохраняя только строку, которая работает, она правильно компилируется и печатает "TEST PP_NARG: 3".

Я полагаю, что проблема может заключаться в том, что PP_RSEQ_N() расширяется только до "3", а не "3,2,1,0" по какой-то причине, поскольку даже если PP_RSEQ_N() определен так

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

он все еще не работает с менее чем 3 параметрами.

Я использую компилятор MSVC, и это может быть причиной проблемы, так как он не очень хорошо работает с макросами, как видно здесь: MSVC неправильно расширяет __VA_ARGS__

2 ответа

Решение

В вашей реализации PP_RSEQ_N() это аргумент PP_ARG_N, В качестве аргумента он раскрывается только на этапе подстановки аргумента предварительной обработки, но это происходит только перед заменой аргумента в его списке замещения (если в списке замещения он не является строковым и не участвует в вставить).

поскольку PP_ARG_N только имеет четвертый аргумент N в списке замены, PP_RSEQ_N() будет расширяться только в том случае, если вы передадите три аргумента. (Во время фазы повторного сканирования и замены выполняется второе сканирование, которое применяется после замены аргумента... но здесь это не имеет никакого эффекта, так как PP_RSEQ_N() упоминается в звонке).

Избавьтесь от этого макроса и просто поместите его в PP_NARG как это:

#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

... и вещи "работают" нормально

PP_NARG() расширяется до 1
PP_NARG(x) расширяется до 1
PP_NARG(x,y) расширяется до 2

Обратите внимание, однако, что PP_NARG() не дает вам 0. Возможно, это действительно правильно; препроцессору, это не передача нулевых аргументов. Он передает один аргумент, который просто пуст. Это то же самое, что #define X(A) OPEN A CLOSE/X() получая OPEN CLOSE, Если по какой-то причине вы хотите, чтобы это увеличилось до 0, возможно, возникнет некоторая путаница, чтобы это произошло, но для этого ответа я сосредоточусь только на том, чтобы помочь вам преодолеть один этот горб.

А PP_ARG_N()реализацию, которая также может различать вызовы с параметром и без него, можно найти здесь (спасибо Скотту Моррисону). Ответ на вашу маленькую программу:

      TEST PP_NARG: 0
TEST PP_NARG: 1
TEST PP_NARG: 2
TEST PP_NARG: 3
Другие вопросы по тегам