Ответ 1
Другая возможность, которая не использует sizeof
и расширение GCC, заключается в добавлении следующего кода в ваш код
#define PP_COMMASEQ_N() \
1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 0, 0
#define PP_COMMA(...) ,
#define PP_HASCOMMA(...) \
PP_NARG_(__VA_ARGS__, PP_COMMASEQ_N())
#define PP_NARG(...) \
PP_NARG_HELPER1( \
PP_HASCOMMA(__VA_ARGS__), \
PP_HASCOMMA(PP_COMMA __VA_ARGS__ ()), \
PP_NARG_(__VA_ARGS__, PP_RSEQ_N()))
#define PP_NARG_HELPER1(a, b, N) PP_NARG_HELPER2(a, b, N)
#define PP_NARG_HELPER2(a, b, N) PP_NARG_HELPER3_ ## a ## b(N)
#define PP_NARG_HELPER3_01(N) 0
#define PP_NARG_HELPER3_00(N) 1
#define PP_NARG_HELPER3_11(N) N
Результат
PP_NARG() // expands to 0
PP_NARG(x) // expands to 1
PP_NARG(x, 2) // expands to 2
Объяснение:
Трюк в этих макросах заключается в том, что PP_HASCOMMA(...)
расширяется до 0 при вызове с нулевым или одним аргументом и до 1 при вызове с хотя бы двумя аргументами. Чтобы различать эти два случая, я использовал PP_COMMA __VA_ARGS__ ()
, который возвращает запятую, когда __VA_ARGS__
пуст, и ничего не возвращает, когда __VA_ARGS__
не является пустым.
Теперь возможны три случая:
-
__VA_ARGS__
пусто:PP_HASCOMMA(__VA_ARGS__)
возвращает 0 иPP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
возвращает 1. -
__VA_ARGS__
содержит один аргумент:PP_HASCOMMA(__VA_ARGS__)
возвращает 0 иPP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
возвращает 0. -
__VA_ARGS__
содержит два или более аргумента:PP_HASCOMMA(__VA_ARGS__)
возвращает 1 иPP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
возвращает 1.
Макросы PP_NARG_HELPERx
необходимы только для разрешения этих случаев.
Edit:
Чтобы исправить проблему func(0, )
, нам нужно проверить, поставили ли мы ноль
или больше аргументов. Макрос PP_ISZERO
вступает в игру здесь.
#define PP_ISZERO(x) PP_HASCOMMA(PP_ISZERO_HELPER_ ## x)
#define PP_ISZERO_HELPER_0 ,
Теперь давайте определим другой макрос, который добавляет количество аргументов в список аргументов:
#define PP_PREPEND_NARG(...) \
PP_PREPEND_NARG_HELPER1(PP_NARG(__VA_ARGS__), __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER1(N, ...) \
PP_PREPEND_NARG_HELPER2(PP_ISZERO(N), N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER2(z, N, ...) \
PP_PREPEND_NARG_HELPER3(z, N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER3(z, N, ...) \
PP_PREPEND_NARG_HELPER4_ ## z (N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER4_1(N, ...) 0
#define PP_PREPEND_NARG_HELPER4_0(N, ...) N, __VA_ARGS__
Многим помощникам снова необходимо расширить макросы до числовых значений. Наконец, протестируйте его:
#define my_func(...) func(PP_PREPEND_NARG(__VA_ARGS__))
my_func() // expands to func(0)
my_func(x) // expands to func(1, x)
my_func(x, y) // expands to func(2, x, y)
my_func(x, y, z) // expands to func(3, x, y, z)
Пример онлайн:
http://coliru.stacked-crooked.com/a/73b4b6d75d45a1c8
См. также:
Просьба также посмотреть проект P99, который имеет гораздо больше расширенные препроцессорные решения, как это.