Parametry opcjonalne z makrami C ++

106

Czy istnieje sposób na uzyskanie opcjonalnych parametrów w makrach C ++? Przydałoby się też jakieś przeciążenie.

Cenoc
źródło
1
To samo dla C: stackoverflow.com/questions/11761703/… Powinien być taki sam, ponieważ preprocesory są w zasadzie takie same: stackoverflow.com/questions/5085533/ ...
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Być może przeciążenia funkcji, parametry domyślne, szablony wariadyczne lub ewentualnie nazwany idiom parametrów są tym, czego szukasz
smoothware
Proszę zaktualizuj wybraną odpowiedź do tych, które uzyskały wysokie głosy, podając rzeczywiste rozwiązania, a nie te, które miały niskie głosy, mówiącNo you can't
Albert Renshaw

Odpowiedzi:

158

Oto jeden sposób, aby to zrobić. Używa listy argumentów dwukrotnie, najpierw do utworzenia nazwy makra pomocnika, a następnie do przekazania argumentów do tego makra pomocniczego. Używa standardowej sztuczki, aby policzyć liczbę argumentów do makra.

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

Ułatwia to wywołującemu makro, ale nie piszącemu.

Derek Ledbetter
źródło
2
To całkiem fajne, ale nie sądzę, żeby zadziałało, gdybym właśnie wykonał PRINT_STRING. W takim przypadku nie byłoby domyślnego wydruku (i tak właśnie jest w przypadku, którego chcę użyć). Wciąż +1 za naprawdę fajne.
Cenoc
2
działa dla mnie w gcc (i jest bardzo sprytne!) :-), ale nie działa dla mnie w Visual Studio :-(
Tim Gradwell
4
@TimGradwell - jest to spowodowane błędem w kompilatorze MSVC, który potwierdzili, ale nie naprawili go od prawie dekady. Jednak dostępne są obejścia .
BeeOnRope
Sprytne, ale nie działa dla opcjonalnych zmiennych argumentów makr z powodu „wypchnięcia” rzeczy, które masz w „GET_4th_ARG”.
Searchengine
czy to w PRINT_STRING_MACRO_CHOOSERogóle potrzebne? Czy mogę bezpośrednio zastąpić jego wewnętrznym ciałem i nazwać to wszystko (__VA_ARGS__)?
Herrgott
85

Z wielkim szacunkiem dla Dereka Ledbettera za jego odpowiedź - i z przeprosinami za przywrócenie starego pytania.

Zrozumienie tego, co robił, i zebranie w innym miejscu zdolności poprzedzającej __VA_ARGS__z, ##pozwoliło mi wymyślić odmianę ...

// 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__)\
                                         ) 

Dla nie-ekspertów takich jak ja, którzy natkną się na odpowiedź, ale nie do końca rozumieją, jak to działa, przejdę przez rzeczywiste przetwarzanie, zaczynając od następującego kodu ...

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 

Staje się...

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

Co staje się dopiero szóstym argumentem ...

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

PS: Usuń #define dla XXX_0, aby uzyskać błąd kompilacji [tj .: jeśli opcja bez argumentów jest niedozwolona].

PPS: Byłoby miło, gdyby nieprawidłowe sytuacje (np .: 5) były czymś, co daje programiście wyraźniejszy błąd kompilacji!

PPPS: Nie jestem ekspertem, więc bardzo się cieszę, słysząc komentarze (dobre, złe lub inne)!

David Sorkovsky
źródło
3
Możesz otrzymać wyraźny błąd kompilacji, jeśli przekonwertowałeś wybrany argument, który ma być nazwą MAKRO na łańcuch za pomocą # (znak krzyżyka) i porównałeś jego pierwsze n znaków z oczekiwanym przedrostkiem, a jeśli nie ma dopasowania, wypisano informacyjny błąd.
AturSams,
1
Wow, nie wiem, czy to działa, ale jest przynajmniej bardzo kreatywne!
Limited Atonement
4
dlaczego pierwszy argument jest zawsze pusty? dlaczego nie możemy tego po prostu pominąć: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman
2
Ważny jest pusty pierwszy argument (przecinek). ## __ VA_ARGS__, jeśli jest poprzedzony przecinkiem - usuwa przecinek, jeśli ## __ VA_ARGS__ rozwinie się do zera. Możesz to zobaczyć w przykładzie "Staje się ...", ponieważ pierwsza linia (bez argumentów) ma tylko 6 parametrów, ale reszta otrzymuje 7. Ta sztuczka zapewnia, że ​​sytuacja bez argumentów działa
David Sorkovsky
@Eric - jest to spowodowane błędem w kompilatorach Microsoft, ale możesz zobaczyć to pytanie w celu obejścia problemu.
BeeOnRope
31

Makra C ++ nie zmieniły się od C. Ponieważ C nie ma przeciążenia i domyślnych argumentów funkcji, z pewnością nie ma ich dla makr. Odpowiadając na pytanie: nie, te funkcje nie istnieją w przypadku makr. Jedyną opcją jest zdefiniowanie wielu makr o różnych nazwach (lub nieużywanie makr w ogóle).

Na marginesie: w C ++ za dobrą praktykę uważa się jak największe odejście od makr. Jeśli potrzebujesz takich funkcji, istnieje duża szansa, że ​​nadużywasz makr.

sepp2k
źródło
4
Zwróć uwagę, że powodem, dla którego niemożliwe jest „przeciążenie” makr, jest to, że nie mają one żadnych typów. Makra są po prostu rozwijane.
mk12,
2
Mimo, że korzystać z makr w jak najmniejszym stopniu, stwierdziliśmy, że debugowanie poprzez wyjście śledzenia dostaje trochę łatwiej z rzeczy, jak __FILE__i __LINE__i takie ...
Christian Severin
niezbyt dobra odpowiedź. to jest dobra odpowiedź: stackoverflow.com/q/27049491/893406
v.oddou
Kompilacja warunkowa i debugowanie / rejestrowanie to domena, w której makra są naprawdę przydatne i legalne. Wie o tym każdy poważny programista. Dobrą praktyką jest unikanie używania makr do definiowania stałych i robienie zwariowanych czynności związanych z kodowaniem na poziomie C w celu tworzenia szablonów kontenerów. Chciałbym, żeby C ++ dodał więcej funkcji do makr. Są prostopadłe do szablonów. Oczywiście najlepsze byłyby kodele, które pozwalają mi dodawać generatory do kompilatora dla języka specyficznego dla domeny (aspekty).
Lothar
1
Myślę też, że to nie jest dobra odpowiedź, ponieważ makro jest czymś zupełnie innym niż jakakolwiek opcja języka C ++, ponieważ zostanie obsłużona PRZED kompilatorem. Możesz więc robić inne rzeczy i żaden kompilator ani konsolidator nie musi optymalizować kodu, ponieważ być może nie jest to optymalizacja.
alabamajack
26

Z wielkim szacunkiem dla Dereka Ledbettera , Davida Sorkovsky'ego , Syphorlate za ich odpowiedzi, wraz z genialną metodą wykrywania pustych argumentów makro Jensa Gustedta w

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

w końcu wyszedłem z czymś, co zawiera wszystkie sztuczki, tak że rozwiązanie

  1. Używa tylko standardowych makr C99, aby osiągnąć przeciążenie funkcji, bez rozszerzenia GCC / CLANG / MSVC (tj. Połykanie przecinków przez określone wyrażenie , ##__VA_ARGS__dla GCC / CLANG i niejawne połykanie przez ##__VA_ARGS__dla MSVC). Więc --std=c99jeśli chcesz, możesz przekazać brakujące informacje do swojego kompilatora =)
  2. Działa dla zerowych argumentów , a także dla nieograniczonej liczby argumentów , jeśli rozszerzysz je dalej, aby dopasować je do swoich potrzeb
  3. Działa rozsądnie na wielu platformach , przynajmniej przetestowany pod kątem

    • GNU / Linux + GCC (GCC 4.9.2 na CentOS 7.0 x86_64)
    • GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 na CentOS 7.0 x86_64)
    • OS X + Xcode (XCode 6.1.1 w systemie OS X Yosemite 10.10.1)
    • Windows + Visual Studio (Visual Studio 2013 Update 4 w systemie Windows 7 SP1 64 bity)

Dla leniwców po prostu przejdź do ostatniego z tego postu, aby skopiować źródło. Poniżej znajduje się szczegółowe wyjaśnienie, które, miejmy nadzieję, pomoże i zainspiruje wszystkich szukających generała__VA_ARGS__ rozwiązań, takich jak ja. =)

Oto jak to działa. Najpierw zdefiniować przeciążony „funkcja” widoczne dla użytkowników, nazwałem go create, a związane rzeczywista definicja funkcji realCreate, a makro definicje z różną liczbą argumentów CREATE_2, CREATE_1, CREATE_0, jak pokazano poniżej:

#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__)Część ostatecznie postanawia makro nazwy definicji, a druga (__VA_ARGS__)część obejmuje swoje listy parametrów. Tak więc wywołanie użytkownika do jest create(10)rozstrzygane na CREATE_1(10), CREATE_1część pochodzi z MACRO_CHOOSER(__VA_ARGS__), a(10) część z drugiej (__VA_ARGS__).

MACRO_CHOOSERStosuje podstęp, że jeśli __VA_ARGS__jest pusta, następujące wyrażenie jest łączony do ważnej rozmowy przez makro preprocesora:

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

Genialnie możemy zdefiniować to wynikowe wywołanie makra jako

#define NO_ARG_EXPANDER() ,,CREATE_0

Zwróć uwagę na dwa przecinki, zostaną one wkrótce wyjaśnione. Kolejne przydatne makro to

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

więc wezwania

create();
create(10);
create(20, 20);

są faktycznie rozszerzone do

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

Jak sugeruje nazwa makra, później policzymy liczbę argumentów. Oto kolejna sztuczka: preprocesor wykonuje tylko prostą zamianę tekstu. Wyprowadza liczbę argumentów wywołania makra jedynie z liczby przecinków, które widzi w nawiasach. Rzeczywiste „argumenty” oddzielone przecinkami nie muszą mieć prawidłowej składni. Może to być dowolny tekst. To znaczy, że w powyższym przykładzie NO_ARG_EXPANDER 10 ()jest liczony jako 1 argument dla wywołania środkowego.NO_ARG_EXPANDER 20i 20 ()są liczone jako 2 argumenty odpowiednio dla dolnego wywołania.

Jeśli użyjemy następujących makr pomocniczych, aby je dalej rozwinąć

##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses

Końcowe ,po CREATE_1jest obejściem dla GCC / CLANG, pomijając (fałszywie dodatni) błąd mówiący o tym ISO C99 requires rest arguments to be usedpodczas przekazywania -pedanticdo kompilatora. Jest FUNC_RECOMPOSERto obejście dla MSVC lub nie może poprawnie policzyć liczby argumentów (tj. Przecinków) w nawiasach przy wywołaniach makr. Wyniki są dalej rozwiązywane do

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

Jak zapewne widzieliście orle oczy, ostatnim jedynym krokiem, jakiego potrzebujemy, jest zastosowanie standardowej sztuczki zliczania argumentów, aby ostatecznie wybrać żądane nazwy wersji makr:

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

który rozwiązuje wyniki na

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

i na pewno daje nam pożądane, rzeczywiste wywołania funkcji:

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

Podsumowując wszystko razem, z pewnym przegrupowaniem instrukcji dla lepszej czytelności, całe źródło dwuargumentowego przykładu jest tutaj:

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

Chociaż jest to skomplikowane, brzydkie, obciążające programistę API, pojawia się rozwiązanie do przeciążania i ustawiania opcjonalnych parametrów funkcji C / C ++ dla nas szalonych ludzi. Korzystanie z wychodzących przeciążonych interfejsów API staje się bardzo przyjemne i przyjemne. =)

Jeśli istnieje jakiekolwiek dalsze uproszczenie tego podejścia, proszę o kontakt pod adresem

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

Jeszcze raz specjalne podziękowania dla wszystkich wspaniałych ludzi, którzy zainspirowali mnie i doprowadzili do wykonania tego dzieła! =)

Jason Deng
źródło
3
Jak rozszerzyć to na 3 lub 4 funkcje?
Phylliida
@Phylliida ideone.com/jD0Hm5 - obsługiwane od zera do pięciu argumentów.
xx
9

Dla każdego, kto szuka rozwiązania VA_NARGS, które współpracuje z Visual C ++. Poniższe makro działało dla mnie bezbłędnie (również bez parametrów!) W 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

Jeśli chcesz mieć makro z opcjonalnymi parametrami, możesz zrobić:

//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__))) 

To zadziałało również dla mnie w vc. Ale to nie działa dla zerowych parametrów.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
Syphorlate
źródło
Dostajęunresolved external symbol _bool referenced in function _main
Avidan Borisov
tak, może się to zdarzyć w niektórych przypadkach. musisz wiedzieć, że bool (#__ VA_ARGS__)? różni się od innych makr, ponieważ jest oceniany w czasie wykonywania. w zależności od przypadku możesz jednak pominąć tę część kodu.
Syphorlate
2
Skończyło się na pastebin.com/H3T75dcn, który działa doskonale (również 0 argumentów).
Avidan Borisov
Dzięki za link i tak, możesz to zrobić również za pomocą sizeof, ale dla mnie to nie działało w niektórych przypadkach, ale zasada jest taka sama (ocena logiczna).
Syphorlate
Czy mógłbyś podać kilka przykładów, w których się to nie udaje?
Avidan Borisov
7

gcc/ g++obsługuje makra varargs, ale nie sądzę, aby to było standardowe, więc używaj go na własne ryzyko.

Paul R.
źródło
4
Są standardowe w C99 i są również dodawane do 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 */
}

ZASTRZEŻENIE: W większości nieszkodliwe.

Joe D.
źródło
w twoim kodzie jest błąd. proszę :%s/MY_MACRO_/THINK_/g:)
João Portela
również nie działało z zerowymi argumentami przy użyciu g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela.
1
Zero argumentów nie istnieje dla makr variadiac, ponieważ pusty token jest prawidłowym symbolem zastępczym.
Paul Fultz II,
3

Nie do tego jest przeznaczony preprocesor.

To powiedziawszy, jeśli chcesz wejść w obszar poważnie trudnego programowania makr z odrobiną czytelności, powinieneś przyjrzeć się bibliotece preprocesorów Boost . W końcu nie byłoby C ++, gdyby nie było trzech poziomów programowania całkowicie zgodnych z Turingiem (preprocesor, metaprogramowanie szablonów i podstawowy poziom C ++)!

Pontus Gagge
źródło
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)

W momencie wywołania wiesz, ile argumentów przekażesz, więc naprawdę nie ma potrzeby przeciążania.

Edward Strange
źródło
2
Właściwie to pytałem o istnienie tej funkcji.
Cenoc
3

Bardziej zwięzła wersja kodu Dereka Ledbettera:

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;
}
Megamozg
źródło
3

Jako wielki fan okropnych potworów makro, chciałem rozwinąć odpowiedź Jasona Denga i uczynić ją użyteczną. (Na lepsze lub gorsze.) Oryginał nie jest zbyt przyjemny w użyciu, ponieważ za każdym razem, gdy chcesz zrobić nowe makro, musisz modyfikować zupę z dużym alfabetem, a jest jeszcze gorzej, jeśli potrzebujesz innej ilości argumentów.

Zrobiłem więc wersję z następującymi funkcjami:

  • 0 argumentów działa
  • Od 1 do 16 argumentów bez żadnych modyfikacji w niechlujnej części
  • Łatwo napisać więcej funkcji makr
  • Testowane w GCC 10, Clang 9, Visual Studio 2017

Obecnie zrobiłem maksymalnie 16 argumentów, ale jeśli potrzebujesz więcej (naprawdę teraz? Po prostu robisz się głupi ...) możesz edytować FUNC_CHOOSER i CHOOSE_FROM_ARG_COUNT, a następnie dodać przecinki do NO_ARG_EXPANDER.

Zobacz doskonałą odpowiedź Jasona Denga, aby uzyskać więcej informacji na temat implementacji, ale wstawię kod tutaj:

#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;
}
Kuukunen
źródło
2

Możesz użyć BOOST_PP_OVERLOADzboost biblioteki.

Przykład z oficjalnego dokumentu doładowania :

#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
wyłącz 13
źródło
0

W zależności od tego, czego potrzebujesz, możesz to zrobić za pomocą argumentów var z makrami. Otóż, opcjonalne parametry czy przeciążenie makr, nie ma czegoś takiego.

Gianni
źródło
-1

Żaden z powyższych przykładów (od Dereka Ledbettera, Davida Sorkovsky'ego i Joe D) do liczenia argumentów z makrami nie działał dla mnie przy użyciu Microsoft VCC 10. __VA_ARGS__Argument jest zawsze traktowany jako pojedynczy argument (tokenizowanie go z ##lub nie), więc przesunięcie argumentów, na którym polegają te przykłady, nie działa.

Krótka odpowiedź, jak twierdzi wielu innych powyżej: nie, nie można przeciążać makr ani używać w nich opcjonalnych argumentów.

TheProgammerd
źródło
1
Możesz, ale tylko w C99 lub C ++ 11 (ze względu na posiadanie __VA_ARGS__). VC2010 to C89 / C ++ 03 (z niektórymi bitami C ++ 11 zaczynają się pojawiać, ale jeszcze nie ten).
puetzk