Необязательные параметры с макросами C ++

106

Есть ли способ получить необязательные параметры с помощью макросов C ++? Было бы неплохо и какая-то перегрузка.

Cenoc
источник
1
То же самое для C: stackoverflow.com/questions/11761703/… Должно быть таким же, поскольку препроцессоры в основном одинаковы: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Возможно, вам нужны перегрузки функций, параметры по умолчанию, вариативные шаблоны или, возможно, идиома именованных параметров
smoothware
Пожалуйста, обновите выбранный вами ответ до тех, за которые проголосовали, с реальными решениями, а не с высказыванием, получившим мало голосов,No you can't
Альберт Реншоу

Ответы:

158

Вот один из способов сделать это. Он использует список аргументов дважды, сначала для формирования имени вспомогательного макроса, а затем для передачи аргументов этому вспомогательному макросу. Он использует стандартный прием для подсчета количества аргументов макроса.

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

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

Дерек Ледбеттер
источник
2
Это довольно круто, но я не думаю, что сработало бы, если бы я просто сделал PRINT_STRING. В этом случае не было бы распечатки по умолчанию (и это действительно тот случай, который я хочу использовать). Тем не менее +1 за действительно круто.
Cenoc
2
у меня работает в gcc (и это очень умно!) :-), но не работает у меня в Visual Studio :-(
Тим Градуэлл
4
@TimGradwell - это из-за ошибки в компиляторе MSVC, которую они признали, но не исправляли почти десять лет. Тем не менее, обходные доступны .
BeeOnRope
Умно, но не работает для необязательных аргументов макроса с переменным числом аргументов из-за того, что у вас есть «выталкивание» в «GET_4th_ARG».
searchchengine27
это PRINT_STRING_MACRO_CHOOSERвообще нужно? Могу ли я заменить его внутренним телом напрямую и назвать все это с помощью (__VA_ARGS__)?
Херрготт,
85

С большим уважением к Дереку Ледбеттеру за его ответ - и с извинениями за то, что возродил старый вопрос.

Понимание того, что он делает, и изучение способности предшествовать __VA_ARGS__с, ##позволило мне придумать вариант ...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

Для неспециалистов вроде меня, которые наткнулись на ответ, но не совсем понимают, как он работает, я пошагово рассмотрю фактическую обработку, начиная со следующего кода ...

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

Становится ...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

Что становится лишь шестым аргументом ...

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

PS: Удалите #define для XXX_0, чтобы получить ошибку компиляции [то есть: если опция без аргументов не разрешена].

PPS: Было бы неплохо, если бы недопустимые ситуации (например: 5) были чем-то, что давало бы программисту более четкую ошибку компиляции!

PPPS: Я не эксперт, поэтому очень рад слышать комментарии (хорошие, плохие и другие)!

Давид Сорковский
источник
3
Вы можете получить явную ошибку компиляции, если преобразовали выбранный аргумент, который должен быть именем МАКРОСА, в строку, используя # (знак фунта), и сравнили его первые n символов с ожидаемым префиксом и, если совпадения нет, напечатали информативный ошибка.
AturSams 07
1
Вау, не знаю, работает ли это, но, по крайней мере, очень креативно!
Ограниченное искупление
4
почему первый аргумент всегда пуст? почему мы не можем просто его опустить: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman
2
Пустой первый аргумент (запятая) важен. ## __ VA_ARGS__, если перед ним стоит запятая - удаляет запятую, если ## __ VA_ARGS__ заменяется на ничего. Вы можете увидеть это в примере «Становится ...», так как первая строка (без аргументов) имеет только 6 параметров, а остальные получают 7. Этот трюк гарантирует, что ситуация без аргументов работает
Дэвид Сорковский
@Eric - это из-за ошибки в компиляторах Microsoft, но вы можете увидеть этот вопрос для обходных путей.
BeeOnRope
31

Макросы C ++ не изменились по сравнению с C. Поскольку в C не было аргументов перегрузки и аргументов по умолчанию для функций, у него определенно не было их для макросов. Итак, чтобы ответить на ваш вопрос: нет, эти функции не существуют для макросов. Ваш единственный вариант - определить несколько макросов с разными именами (или вообще не использовать макросы).

В качестве примечания: в C ++ обычно считается хорошей практикой максимально отказаться от макросов. Если вам нужны такие функции, есть большая вероятность, что вы злоупотребляете макросами.

sepp2k
источник
4
Обратите внимание, что причина, по которой невозможно «перегрузить» макросы, заключается в том, что они не имеют присущих им типов. Макросы просто расширяются.
mk12
2
Хотя я использую макросы как можно меньше, я обнаружил , что отладка с помощью вывода трассировки получает совсем немного проще с такими вещами , как __FILE__и __LINE__и такие ...
Кристиан Северин
не лучший ответ. это хороший ответ: stackoverflow.com/q/27049491/893406
v.oddou
Условная компиляция и отладка / ведение журнала - это область, в которой макросы действительно удобны и законны. Это знает каждый серьезный программист. Хорошая практика - уйти от использования макросов для определения констант и делать сумасшедшие вещи кодирования уровня C для создания шаблонов контейнеров. Я бы хотел, чтобы C ++ также добавил больше функций в макросы. Они ортогональны шаблонам. Лучшими, конечно, были бы кодлеты, которые позволяют мне добавлять генераторы в компилятор для специфичного для предметной области языка (аспектов).
Lothar
1
Я также думаю, что это не лучший ответ, потому что макрос - это нечто совершенно иное, чем любой вариант языка C ++, потому что он будет обрабатываться ДО компилятора. Таким образом, вы можете делать другие вещи, и ни один компилятор или компоновщик не должен оптимизировать код, потому что, возможно, его не нужно оптимизировать.
alabamajack 03
26

С большим уважением к Дереку Ледбеттеру , Дэвиду Сорковскому , Syphorlate за их ответы, а также за изобретательный метод обнаружения пустых аргументов макроса, разработанный Йенсом Густедтом в

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

наконец, я придумываю что-то, что включает все уловки, так что

  1. Использует только стандартные макросы C99 для достижения перегрузки функций, без расширения GCC / CLANG / MSVC (т.е. проглатывание запятой в конкретном выражении , ##__VA_ARGS__для GCC / CLANG и неявное проглатывание ##__VA_ARGS__для MSVC). Так что не стесняйтесь передавать недостающее --std=c99вашему компилятору, если хотите =)
  2. Работает как с нулевым аргументом , так и с неограниченным количеством аргументов , если вы расширите его дальше в соответствии с вашими потребностями
  3. Достаточно кроссплатформенный , по крайней мере, протестирован на

    • 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 с обновлением 4 для 64-разрядной версии Windows 7 SP1)

Для ленивых просто перейдите к самому последнему посту, чтобы скопировать исходный код. Ниже приводится подробное объяснение, которое, надеюсь, поможет и вдохновит всех людей, ищущих общие __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 становится очень приятным и приятным. знак равно

Если есть возможность дальнейшего упрощения этого подхода, сообщите мне об этом по адресу

https://github.com/jason-deng/C99FunctionOverload

Еще раз выражаю особую благодарность всем блестящим людям, которые вдохновили меня на эту работу! знак равно

Джейсон Денг
источник
3
Как это расширить до 3 или 4 функций?
Филлида
@Phylliida ideone.com/jD0Hm5 - поддерживается от нуля до пяти аргументов.
xx
9

Для тех, кто мучительно ищет решение VA_NARGS, которое работает с Visual C ++. Следующий макрос работал у меня безупречно (также с нулевыми параметрами!) В Visual C ++ Express 2010:

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))) : 0

Если вам нужен макрос с необязательными параметрами, вы можете:

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

Это сработало для меня, как и в vc. Но для нулевых параметров это не работает.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
Syphorlate
источник
Я получаюunresolved external symbol _bool referenced in function _main
Авидан Борисов
да, в некоторых случаях такое может случиться. вам нужно знать, что bool (#__ VA_ARGS__)? отличается от других макросов, поскольку он оценивается во время выполнения. в зависимости от вашего случая вы можете опустить эту часть кода.
Syphorlate
2
На самом деле я остановился на pastebin.com/H3T75dcn, который отлично работает (тоже 0 аргументов).
Авидан Борисов
Спасибо за ссылку, и да, вы также можете сделать это с помощью sizeof, но для меня это не сработало в некоторых случаях, но принцип тот же (логическая оценка).
Syphorlate
Не могли бы вы привести несколько примеров, когда это не удается?
Авидан Борисов
7

gcc/ g++поддерживает макросы varargs, но я не думаю, что это стандарт, поэтому используйте его на свой страх и риск.

Пол Р
источник
4
Они являются стандартными для C99, и они также добавляются в C ++ 0x.
greyfade
5
#include <stdio.h>

#define PP_NARG(...) \
    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
    PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
    63,62,61,60,                   \
    59,58,57,56,55,54,53,52,51,50, \
    49,48,47,46,45,44,43,42,41,40, \
    39,38,37,36,35,34,33,32,31,30, \
    29,28,27,26,25,24,23,22,21,20, \
    19,18,17,16,15,14,13,12,11,10, \
    9,8,7,6,5,4,3,2,1,0

#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: В основном безвредны.

Джо Д.
источник
в вашем коде есть ошибка. пожалуйста, сделайте :%s/MY_MACRO_/THINK_/g:)
João Portela
кроме того, он не работал с нулевыми аргументами с использованием g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
Жоао Портела
1
Для макросов с переменным адресом не существует нулевых аргументов, поскольку пустой токен является допустимым заполнителем.
Пол Фульц II
3

На самом деле препроцессор не для этого предназначен.

Тем не менее, если вы хотите войти в область серьезного программирования макросов с минимальной удобочитаемостью, вам следует взглянуть на библиотеку препроцессора Boost . В конце концов, это не был бы C ++, если бы не было трех полностью совместимых с Тьюрингом уровней программирования (препроцессор, метапрограммирование шаблонов и базовый уровень C ++)!

Pontus Gagge
источник
3
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

В момент вызова вы знаете, сколько аргументов вы собираетесь передать, поэтому нет необходимости в перегрузке.

Эдвард Стрэндж
источник
2
Я действительно спрашивал о существовании этой функции.
Cenoc
3

Более сжатая версия кода Дерека Ледбеттера:

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}
Мегамозг
источник
3

Как большой поклонник ужасных макромонстров, я хотел расширить ответ Джейсона Дэна и сделать его действительно пригодным для использования. (Хорошо это или плохо.) Оригинал не очень удобен, потому что вам нужно изменять большой алфавитный суп каждый раз, когда вы хотите создать новый макрос, и еще хуже, если вам нужно другое количество аргументов.

Итак, я сделал версию с такими функциями:

  • 0 аргумент case работает
  • От 1 до 16 аргументов без каких-либо изменений в беспорядочной части
  • Легко написать больше макросов
  • Протестировано в gcc 10, clang 9, Visual Studio 2017

В настоящее время я только что сделал максимум 16 аргументов, но если вам нужно больше (правда? Вы просто становитесь глупым ...), вы можете отредактировать FUNC_CHOOSER и CHOOSE_FROM_ARG_COUNT, а затем добавить запятые в NO_ARG_EXPANDER.

Пожалуйста, см. Отличный ответ Джейсона Денга для получения более подробной информации о реализации, но я просто помещу здесь код:

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}
Куукунен
источник
2

Вы можете использовать BOOST_PP_OVERLOADиз boostбиблиотеки.

Пример из официального документа по ускорению :

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9
отключить13
источник
0

В зависимости от того, что вам нужно, вы можете сделать это с помощью макросов var args . Теперь, необязательных параметров или перегрузки макроса, такого нет.

Джанни
источник
-1

Ни один из вышеперечисленных примеров (от Дерека Ледбеттера, Дэвида Сорковского и Джо Д.) для подсчета аргументов с помощью макросов не работал у меня с использованием Microsoft VCC 10. __VA_ARGS__Аргумент всегда рассматривается как один аргумент (токенизируя его ##или нет), поэтому сдвиг аргументов, на который опираются эти примеры, не работает.

Итак, короткий ответ, как было сказано многими другими выше: нет, вы не можете перегружать макросы или использовать в них необязательные аргументы.

TheProgammerd
источник
1
Можно, но только в C99 или C ++ 11 (из-за наличия __VA_ARGS__). VC2010 - это C89 / C ++ 03 (некоторые биты C ++ 11 начинают появляться, но еще не этот).
puetzk