С большим уважением к Дерек Ледбеттер , Дэвид Сорковский , Syphorlate для их ответов, вместе с оригинальным методом обнаружения пустых макро-аргументов с помощью Jens Gustedt at
https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
наконец я придумываю что-то, что включает в себя все приемы, так что решение
- использует только стандартные макросы C99 для достижения перегрузки функций, без расширения GCC / CLANG / MSVC (т. е. глотание запятой определенным выражением
, ##__VA_ARGS__
для GCC / CLANG и неявное глотание на ##__VA_ARGS__
для MSVC).Так что не стесняйтесь передавать недостающие --std=c99
вашему компилятору, если хотите =) - Работает для нулевой аргумент , а также неограниченное количество аргументов , еслиВы расширяете его в соответствии с вашими потребностями
Работает разумно кросс-платформенный , по крайней мере проверено на
- GNU / Linux + GCC (GCC 4.9.2 в CentOS 7.0 x86_64)
- GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 в CentOS 7.0 x86_64)
- OS X + Xcode , (XCode 6.1.1 в OS X Yosemite 10.10.1)
- Windows + Visual Studio , (Visual Studio 2013 Update 4 в Windows7 SP1 64 бита)
Для ленивцев просто перейдите к самому последнему посту, чтобы скопировать источник.Ниже приводится подробное объяснение, которое, мы надеемся, помогает и вдохновляет всех людей, которые ищут общие решения __VA_ARGS__
, такие как я.=)
Вот как это происходит.Сначала определите видимую пользователем перегруженную «функцию», я назвал ее create
и соответствующее фактическое определение функции realCreate
, а также определения макросов с различным количеством аргументов CREATE_2
, CREATE_1
, CREATE_0
, какпоказано ниже:
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}
#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)
Часть MACRO_CHOOSER(__VA_ARGS__)
в конечном итоге разрешает имена определений макросов, а вторая часть (__VA_ARGS__)
содержит их списки параметров.Таким образом, пользовательский вызов create(10)
преобразуется в CREATE_1(10)
, часть CREATE_1
поступает из MACRO_CHOOSER(__VA_ARGS__)
, а часть (10)
поступает из второй (__VA_ARGS__)
.
Используется MACRO_CHOOSER
Хитрость в том, что если __VA_ARGS__
пусто, следующее выражение объединяется препроцессором в допустимый вызов макроса:
NO_ARG_EXPANDER __VA_ARGS__ () // simply shrinks to NO_ARG_EXPANDER()
Изобретательно, мы можем определить этот результирующий вызов макроса как
#define NO_ARG_EXPANDER() ,,CREATE_0
Обратите внимание на две запятые, они скоро будут объяснены.Следующий полезный макрос -
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
, поэтому вызовы
create();
create(10);
create(20, 20);
фактически расширены до
CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);
Как следует из названия макроса, мы должны считатьколичество аргументов позже.Вот еще одна хитрость: препроцессор выполняет только простую замену текста.Он выводит количество аргументов вызова макроса просто из числа запятых, которые он видит в скобках.Фактические «аргументы», разделенные запятыми, не обязательно должны иметь правильный синтаксис.Они могут быть любым текстом.То есть, в приведенном выше примере NO_ARG_EXPANDER 10 ()
считается как 1 аргумент для среднего вызова.NO_ARG_EXPANDER 20
и 20 ()
считаются 2 аргументами для нижнего вызова соответственно.
Если мы используем следующие вспомогательные макросы для их дальнейшего расширения
##define CHOOSE_FROM_ARG_COUNT(...) \
FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
FUNC_CHOOSER argsWithParentheses
Трейлинг ,
послеCREATE_1
- это обходной путь для GCC / CLANG, подавляющий ошибку (ложное срабатывание), говорящую, что ISO C99 requires rest arguments to be used
при передаче -pedantic
вашему компилятору.FUNC_RECOMPOSER
- это обходной путь для MSVC, или он не может правильно подсчитать количество аргументов (то есть запятых) в скобках макросов.Далее результаты сводятся к
FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);
Как вы, возможно, уже видели, как орлиные глаза, последний единственный шаг, который нам нужен, это использование стандартного трюка для подсчета аргументов, чтобы наконец выбрать нужные имена версий макросов:
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
, который разрешает результаты в
CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);
и, конечно, дает нам нужные, реальные вызовы функций:
realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);
Собирает все вместе, с некоторой перестановкой операторовдля лучшей читабельности, весь источник примера с двумя аргументами находится здесь:
#include <stdio.h>
void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}
#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
int main()
{
create();
create(10);
create(20, 20);
//create(30, 30, 30); // Compilation error
return 0;
}
Несмотря на сложность, уродливость, отягощающую разработчиков API, для сумасшедших людей существует решение для перегрузки и установки необязательных параметров функций C / C ++. Использование перегруженных API-интерфейсов становится очень приятным и приятным. =) * * 1 123
Если есть еще возможное упрощение этого подхода, пожалуйста, дайте мне знать на
https://github.com/jason-deng/C99FunctionOverload
Еще раз отдельное спасибо всем замечательным людям, которые вдохновили и привели меня к выполнению этой работы! =)