Недавно я столкнулся с подобной проблемой, и я верю, что есть решение.
Ключевая идея заключается в том, что есть способ написать макрос NUM_ARGS
, чтобы подсчитать количество аргументов, которые задаются с помощью вариационного макроса. Вы можете использовать вариант NUM_ARGS
для построения NUM_ARGS_CEILING2
, который может сказать вам, задан ли макросу с переменным числом аргументов 1 аргумент или 2 или более аргументов. Затем вы можете написать свой макрос Bar
так, чтобы он использовал NUM_ARGS_CEILING2
и CONCAT
для отправки своих аргументов одному из двух вспомогательных макросов: один, который ожидает ровно 1 аргумент, а другой, который ожидает переменное число аргументов больше 1 .
Вот пример, где я использую этот трюк для написания макроса UNIMPLEMENTED
, который очень похож на BAR
:
ШАГ 1:
/**
* A variadic macro which counts the number of arguments which it is
* passed. Or, more precisely, it counts the number of commas which it is
* passed, plus one.
*
* Danger: It can't count higher than 20. If it's given 0 arguments, then it
* will evaluate to 1, rather than to 0.
*/
#define NUM_ARGS(...) \
NUM_ARGS_COUNTER(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, \
12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define NUM_ARGS_COUNTER(a1, a2, a3, a4, a5, a6, a7, \
a8, a9, a10, a11, a12, a13, \
a14, a15, a16, a17, a18, a19, a20, \
N, ...) \
N
ШАГ 1.5:
/*
* A variant of NUM_ARGS that evaluates to 1 if given 1 or 0 args, or
* evaluates to 2 if given more than 1 arg. Behavior is nasty and undefined if
* it's given more than 20 args.
*/
#define NUM_ARGS_CEIL2(...) \
NUM_ARGS_COUNTER(__VA_ARGS__, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
2, 2, 2, 2, 2, 2, 2, 1)
Шаг 2:
#define _UNIMPLEMENTED1(msg) \
log("My creator has forsaken me. %s:%s:%d." msg, __FILE__, \
__func__, __LINE__)
#define _UNIMPLEMENTED2(msg, ...) \
log("My creator has forsaken me. %s:%s:%d." msg, __FILE__, \
__func__, __LINE__, __VA_ARGS__)
ШАГ 3:
#define UNIMPLEMENTED(...) \
CONCAT(_UNIMPLEMENTED, NUM_ARGS_CEIL2(__VA_ARGS__))(__VA_ARGS__)
Где CONCAT реализован обычным способом. В качестве быстрой подсказки, если вышесказанное кажется запутанным: цель CONCAT заключается в расширении до другого макроса «вызов».
Обратите внимание, что сам NUM_ARGS не используется. Я просто включил его, чтобы проиллюстрировать основной трюк здесь. См. Блог Jens Gustedt P99 , где можно найти хорошее решение.
Две ноты:
NUM_ARGS ограничено числом аргументов, которые он обрабатывает. Мой
может обрабатывать только до 20, хотя число полностью произвольно.
NUM_ARGS, как показано, имеет ловушку в том, что он возвращает 1, когда дано 0 аргументов. Суть в том, что NUM_ARGS технически считает [запятые + 1], а не аргументы. В этом
частный случай, это на самом деле работает на наш
преимущество. _UNIMPLEMENTED1 отлично справится с пустым токеном
и это избавляет нас от необходимости писать _UNIMPLEMENTED0. Гастт имеет
Обходной путь для этого также, хотя я не использовал это, и я не уверен, будет ли это работать для того, что мы делаем здесь.