Необов’язкові параметри з макросами C ++


105

Чи є спосіб отримання необов'язкових параметрів за допомогою макросів C ++? Якась перевантаження теж буде непогана.


1
Те ж саме для C: stackoverflow.com/questions/11761703 / ... повинна бути однаковою , так як препроцесор в основному той же: stackoverflow.com/questions/5085533 / ...
Чіро Сантіллі郝海东冠状病六四事件法轮功

Може бути , перевантажені функції, параметри по замовчуванню, VARIADIC шаблони або , можливо, з ім'ям параметра ідіоми, що ви шукаєте
smoothware

Будь ласка, оновіть вибрану відповідь на високообов’язані відповіді актуальними рішеннями, а не низько оціненою приказкоюNo you can't
Альберт Реншо

Відповіді:


156

Ось один із способів зробити це. Він використовує список аргументів двічі, спершу для формування імені допоміжного макросу, а потім для передачі аргументів цьому макросу помічника. Для підрахунку кількості аргументів макросу використовується стандартний трюк.

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

Це полегшує абонент макросу, але не письменник.


1
Це досить круто, але я не думаю, що це спрацювало, якби я щойно зробив PRINT_STRING. У такому випадку не буде роздруківки за замовчуванням (і це насправді я хочу використовувати). Ще +1 для дійсно крутого.
Cenoc

2
працює для мене в gcc (і це дуже розумно!) :-), але не працює для мене у Visual Studio :-(
Tim Gradwell

3
@TimGradwell - це пов’язано з помилкою в компіляторі MSVC, яку вони визнали, але не виправили протягом майже десятиліття. Однак шляхові шляхи доступні .
BeeOnRope

Розумний, але не працює для необов'язкових варіантів макроаргументів через те, що відбувається в "GET_4th_ARG".
searchengine27

це 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
Ви можете отримати чітку помилку компіляції, якщо перетворили вибраний аргумент, який повинен бути іменем MACRO, у рядок за допомогою # (знак фунта) і порівняли його перші n символів із очікуваним префіксом, а якщо немає відповідності, надрукували інформативний помилка.
AturSams

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 ++ зазвичай вважається хорошою практикою максимально віддалятися від макросів. Якщо вам потрібні такі функції, є хороший шанс, що ви зловживаєте макросами.


4
Зауважте, що причина, через яку неможливо «перевантажувати» макроси, полягає в тому, що вони не мають властивих типів. Макроси просто розширюються.
mk12

2
Хоча я використовую макроси якомога менше, я виявив , що налагодження з допомогою виведення трасування отримує зовсім небагато простіше з такими речами , як __FILE__і __LINE__і такі ...
Крістіан Северин

не хороша відповідь. це хороший відповідь: stackoverflow.com/q/27049491/893406
v.oddou

Умовна компіляція та налагодження / ведення журналу - це область, де макроси справді зручні та легальні. Кожен серйозний програміст це знає. Хороша практика - це відмовитися від використання макросів для визначення констант і робити деякі шалені речі з кодування рівня C для створення шаблонів контейнерів. Я хочу, щоб C ++ також додав більше функцій до макросів. Вони ортогональні шаблонам. Найкраще, звичайно, будуть коделі, які дозволяють мені додавати генератори в компілятор для доменної мови (аспектів).
Лотар

1
Я також вважаю, що це не є гарною відповіддю, тому що макрос - це щось зовсім інше, ніж будь-який варіант мови C ++, тому що він буде оброблятися перед компілятором. Таким чином, ви можете робити інші речі, і жоден компілятор чи посилання не повинні оптимізувати код, оскільки, можливо, його не оптимізувати.
alabamajack

26

З найбільшою повагою до Дерека Ледбеттера , Девіда Сорковського , Сифорлату за їхні відповіді, а також геніального методу виявлення порожніх макроаргументів Йенса Гуведта на

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, Windows 7, 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);

Збираючи все разом, з деякою перестановкою висловлювань для кращої читабельності, тут є все джерело прикладу 2-аргументів :

#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 ++. Наступний макрос працював для мене бездоганно (також з нульовими параметрами!) У візуальному 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

Я отримуюunresolved external symbol _bool referenced in function _main
Авідан Борисов

так, це може статися в деяких випадках. вам потрібно знати, що bool (#__ VA_ARGS__)? відрізняється від інших макросів, оскільки вона оцінюється під час виконання. залежно від вашого випадку, ви можете опустити цю частину коду.
Сифолат

2
Я фактично закінчився pastebin.com/H3T75dcn, який прекрасно працює (теж 0 аргументів).
Авідан Борисов

Дякую за посилання, і так, ви можете це зробити, використовуючи розмір tooof, але для мене, який не працював у деяких випадках, але принцип той же (булева оцінка).
Сифолат

Чи можете ви навести кілька прикладів, коли це не вдається?
Авідан Борисов

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:)
Жоао Портела

Крім того, він не працював з нульовими аргументами, використовуючи g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela

1
Нульових аргументів для варіабельних макросів не існує, оскільки порожній маркер є дійсним заповнювачем.
Пол Фульц II

3

Це не зовсім те, для чого призначений препроцесор.

Це означає, що якщо ви хочете ввійти в область серйозних складних макропрограмувань з можливістю читабельності, вам слід поглянути на бібліотеку препроцесора Boost . Зрештою, це не було б C ++, якби не було трьох повністю Turing сумісних рівнів програмування (препроцесор, метапрограмування шаблонів та базовий рівень C ++)!


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
Я насправді запитував про існування функції.
Сенок

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

Як великий шанувальник жахливих макро-монстрів, я хотів розкрити відповідь Джейсона Денга і зробити його фактично корисним. (На краще чи гірше.) Оригінал не дуже приємний у використанні, оскільки вам потрібно змінювати суп великого алфавіту щоразу, коли ви хочете зробити новий макрос, а ще гірше, якщо вам потрібна різна кількість аргументів.

Тому я зробив версію з такими можливостями:

  • Справа з аргументом працює
  • 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бібліотеки.

Приклад з офіційного документа 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

0

Залежно від того, що вам потрібно, ви могли б зробити це з вар-аргами з макросами. Тепер, необов'язкові параметри або макро перевантаження, такого немає.


-1

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

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


1
Ви можете, але тільки в C99 або C ++ 11 (через наявність __VA_ARGS__). VC2010 - це C89 / C ++ 03 (деякі біти C ++ 11 починають з'являтися, але ще не цей).
puetzk
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.