Optionale Parameter mit C ++ - Makros


105

Gibt es eine Möglichkeit, optionale Parameter mit C ++ - Makros abzurufen? Eine Art Überlastung wäre auch schön.


1
Gleiches gilt für C: stackoverflow.com/questions/11761703/… Sollte gleich sein, da die Präprozessoren grundsätzlich gleich sind: stackoverflow.com/questions/5085533/…
Ciro Santilli 20 冠状 病 六四 事件 法轮功

Vielleicht sind Funktionsüberladungen, Standardparameter, verschiedene Vorlagen oder möglicherweise die benannte Parametersprache das, wonach Sie suchen
Smoothware

Bitte aktualisieren Sie Ihre ausgewählte Antwort auf die hochstimmigen mit tatsächlichen Lösungen, nicht die niedrigstimmigen, die sagenNo you can't
Albert Renshaw

Antworten:


155

Hier ist eine Möglichkeit, dies zu tun. Die Liste der Argumente wird zweimal verwendet, um zuerst den Namen des Hilfsmakros zu bilden und dann die Argumente an dieses Hilfsmakro zu übergeben. Es verwendet einen Standardtrick, um die Anzahl der Argumente für ein Makro zu zählen.

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

Dies erleichtert dem Aufrufer des Makros, nicht aber dem Schreiber.


1
Das ist ziemlich cool, aber ich glaube nicht, dass es funktionieren würde, wenn ich nur PRINT_STRING machen würde. In diesem Fall würde es keinen Standardausdruck geben (und das ist eigentlich der Fall, den ich verwenden möchte). Immer noch +1 für richtig cool.
Cenoc

2
funktioniert für mich in gcc (und es ist sehr klug!) :-) funktioniert aber nicht für mich in Visual Studio :-(
Tim Gradwell

3
@ TimGradwell - Dies liegt an einem Fehler im MSVC-Compiler, den sie erkannt, aber seit fast einem Jahrzehnt nicht mehr behoben haben. Es sind jedoch Problemumgehungen verfügbar .
BeeOnRope

Clever, funktioniert aber nicht für optionale variable Makroargumente, da Sie in "GET_4th_ARG" "Push-out" ausführen.
Searchengine27

wird das PRINT_STRING_MACRO_CHOOSERüberhaupt gebraucht? Kann ich direkt durch seinen inneren Körper ersetzen und das Ganze mit nennen (__VA_ARGS__)?
Herrgott

85

Mit großem Respekt an Derek Ledbetter für seine Antwort - und mit Entschuldigung für die Wiederbelebung einer alten Frage.

Ein Verständnis für das bekommen , was er tat , und an anderer Stelle auf der Fähigkeit , Kommissionierung bis der zu vorausgehen __VA_ARGS__mit ##mir erlaubt , mit einer Variation zu kommen ...

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

Für Nicht-Experten wie mich, die über die Antwort stolpern, aber nicht genau sehen können, wie sie funktioniert, werde ich die eigentliche Verarbeitung schrittweise durchführen, beginnend mit dem folgenden Code ...

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 

Wird...

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

Welches wird nur das sechste Argument ...

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

PS: Entfernen Sie die #define für XXX_0, um einen Kompilierungsfehler zu erhalten [dh wenn eine Option ohne Argument nicht zulässig ist].

PPS: Es wäre schön, wenn die ungültigen Situationen (z. B. 5) dem Programmierer einen klareren Kompilierungsfehler geben würden!

PPPS: Ich bin kein Experte, daher freue ich mich sehr über Kommentare (gut, schlecht oder andere)!


3
Sie könnten einen eindeutigen Kompilierungsfehler erhalten, wenn Sie das ausgewählte Argument, bei dem es sich um einen MACRO-Namen handeln soll, mit # (dem Nummernzeichen) in einen String konvertieren und die ersten n Zeichen mit dem erwarteten Präfix vergleichen. Wenn keine Übereinstimmung vorliegt, drucken Sie eine Information aus Error.
AturSams

1
Wow, ich weiß nicht, ob das funktioniert, aber es ist zumindest sehr kreativ!
Begrenzte Versöhnung

4
Warum ist das erste Argument immer leer? warum können wir es nicht einfach weglassen: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman

2
Das leere erste Argument (Komma) ist wichtig. ## __ VA_ARGS__, wenn ein Komma vorangestellt ist - das Komma wird entfernt, wenn ## __ VA_ARGS__ zu nichts erweitert wird. Sie können es im Beispiel "Becomes ..." sehen, da die erste Zeile (keine Argumente) nur 6 Parameter enthält, der Rest jedoch 7. Dieser Trick stellt sicher, dass die Situation ohne Argumente funktioniert
David Sorkovsky

@Eric - Es liegt an einem Fehler in den Microsoft-Compilern, aber Sie können diese Frage zur Problemumgehung sehen.
BeeOnRope

31

C ++ - Makros haben sich nicht von C geändert. Da C keine Überladungs- und Standardargumente für Funktionen hatte, hatte es diese sicherlich nicht für Makros. Um Ihre Frage zu beantworten: Nein, diese Funktionen gibt es für Makros nicht. Sie können nur mehrere Makros mit unterschiedlichen Namen definieren (oder überhaupt keine Makros verwenden).

Als Randnotiz: In C ++ wird es allgemein als gute Praxis angesehen, sich so weit wie möglich von Makros zu entfernen. Wenn Sie solche Funktionen benötigen, besteht eine gute Chance, dass Sie Makros überbeanspruchen.


4
Beachten Sie, dass der Grund, warum es unmöglich ist, Makros zu "überladen", darin besteht, dass sie keine inhärenten Typen haben. Makros werden einfach erweitert.
mk12

2
Obwohl ich so wenig Makros wie möglich benutze, habe ich festgestellt, dass das Debuggen über Trace-Ausgabe mit Dingen wie __FILE__und __LINE__und so ziemlich einfacher wird ...
Christian Severin

keine gute Antwort. Dies ist eine gute Antwort: stackoverflow.com/q/27049491/893406
v.oddou

Bedingte Kompilierung und Debugging / Protokollierung ist die Domäne, in der Makros wirklich praktisch und legitim sind. Jeder ernsthafte Programmierer weiß es. Es empfiehlt sich, keine Makros zum Definieren von Konstanten zu verwenden und verrückte C-Level-Codierungsaufgaben zum Erstellen von Containervorlagen auszuführen. Ich wünschte, C ++ würde den Makros auch mehr Funktionen hinzufügen. Sie sind orthogonal zu Vorlagen. Am besten wären natürlich Codelets, mit denen ich dem Compiler Generatoren für domänenspezifische Sprachen (Aspekte) hinzufügen kann.
Lothar

1
Ich denke auch, dass dies keine gute Antwort ist, da ein Makro etwas völlig anderes ist als jede C ++ - Sprachoption, da es VOR dem Compiler behandelt wird. Sie können also andere Dinge tun, und kein Compiler oder Linker muss den Code optimieren, da er möglicherweise nicht optimiert werden muss.
Alabamajack

26

Mit größtem Respekt an Derek Ledbetter , David Sorkovsky , Syphorlate für ihre Antworten, zusammen mit der genialen Methode, leere Makroargumente von Jens Gustedt bei zu erkennen

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

Schließlich komme ich mit etwas heraus, das alle Tricks enthält, so dass die Lösung

  1. Verwendet nur Standard-C99- Makros, um eine Funktionsüberladung zu erreichen, ohne dass eine GCC / CLANG / MSVC-Erweiterung beteiligt ist (dh Komma-Schlucken durch den spezifischen Ausdruck , ##__VA_ARGS__für GCC / CLANG und implizites Schlucken durch ##__VA_ARGS__für MSVC). --std=c99Wenn Sie möchten, können Sie das Fehlende an Ihren Compiler weitergeben =)
  2. Funktioniert für Null-Argumente sowie für eine unbegrenzte Anzahl von Argumenten , wenn Sie es weiter erweitern, um es Ihren Anforderungen anzupassen
  3. Funktioniert einigermaßen plattformübergreifend , zumindest getestet

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

Für die Faulen springen Sie einfach zum letzten Teil dieses Beitrags, um die Quelle zu kopieren. Nachfolgend finden Sie eine ausführliche Erklärung, die hoffentlich allen Menschen hilft und sie inspiriert, die nach allgemeinen __VA_ARGS__Lösungen wie mir suchen . =)

So geht's Definieren Sie zunächst den Benutzer sichtbaren überlastete „Funktion“, nannte ich es create, und die tatsächliche Funktionsdefinition verwendet realCreate, und die Makrodefinitionen mit unterschiedlicher Anzahl von Argumenten CREATE_2, CREATE_1, CREATE_0, wie unten dargestellt:

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

Der MACRO_CHOOSER(__VA_ARGS__)Teil wird schließlich in die Makrodefinitionsnamen aufgelöst, und der zweite (__VA_ARGS__)Teil enthält deren Parameterlisten. So ein Anruf des Benutzers zu create(10)Entschlüssen zu CREATE_1(10), das CREATE_1kommt teilweise aus MACRO_CHOOSER(__VA_ARGS__), und das (10)Teil stammt aus dem zweiten(__VA_ARGS__) .

Der MACRO_CHOOSERverwendet den Trick, dass __VA_ARGS__der folgende Ausdruck , wenn er leer ist, vom Präprozessor zu einem gültigen Makroaufruf verkettet wird:

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

Genialerweise können wir diesen resultierenden Makroaufruf als definieren

#define NO_ARG_EXPANDER() ,,CREATE_0

Beachten Sie die beiden Kommas, sie werden bald erklärt. Das nächste nützliche Makro ist

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

so die Anrufe von

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

sind eigentlich erweitert auf

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

Wie der Makroname andeutet, müssen wir später die Anzahl der Argumente zählen. Hier kommt ein weiterer Trick: Der Präprozessor ersetzt nur den einfachen Text. Die Anzahl der Argumente eines Makroaufrufs wird lediglich aus der Anzahl der Kommas in den Klammern abgeleitet. Die durch Kommas getrennten tatsächlichen "Argumente" müssen keine gültige Syntax haben. Sie können ein beliebiger Text sein. Das heißt, im obigen Beispiel NO_ARG_EXPANDER 10 ()wird als 1 Argument für den mittleren Aufruf gezählt. NO_ARG_EXPANDER 20und20 () werden jeweils als 2 Argumente für den untersten Aufruf gezählt.

Wenn wir die folgenden Hilfsmakros verwenden, um sie weiter zu erweitern

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

Das anschließende ,Nach CREATE_1ist eine Problemumgehung für GCC / CLANG, die einen (falsch positiven) Fehler unterdrückt, der besagt, dass dies ISO C99 requires rest arguments to be usedbei der Übergabe -pedantican Ihren Compiler der Fall ist . Das FUNC_RECOMPOSERist eine Behelfslösung für MSVC, oder es kann nicht mehr Argumente zählt (dh Komma) in den Klammern der Makroaufrufe richtig. Die Ergebnisse werden weiter aufgelöst

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

Wie Sie vielleicht gesehen haben, besteht der letzte Schritt darin, einen Standard-Trick zum Zählen von Argumenten anzuwenden, um schließlich die gewünschten Namen der Makroversionen auszuwählen:

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

das löst die Ergebnisse auf

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

und gibt uns sicherlich die gewünschten, tatsächlichen Funktionsaufrufe:

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

Alles in allem ist die gesamte Quelle des Beispiels mit zwei Argumenten hier , wobei einige Anweisungen zur besseren Lesbarkeit neu angeordnet wurden :

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

Obwohl kompliziert, hässlich und den API-Entwickler belastend, gibt es eine Lösung zum Überladen und Festlegen optionaler Parameter von C / C ++ - Funktionen für uns Verrückte. Die Verwendung der ausgehenden überlasteten APIs wird sehr angenehm und angenehm. =)

Wenn es eine weitere mögliche Vereinfachung dieses Ansatzes gibt, lassen Sie es mich bitte unter wissen

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

Nochmals ein besonderer Dank an alle brillanten Menschen, die mich zu dieser Arbeit inspiriert und geführt haben! =)


3
Wie erweitert man dies auf 3 oder 4 Funktionen?
Phylliida

@Phylliida ideone.com/jD0Hm5 - null bis fünf Argumente werden unterstützt.
xx

9

Für alle, die schmerzhaft nach einer VA_NARGS-Lösung suchen, die mit Visual C ++ funktioniert. Das folgende Makro hat in Visual C ++ Express 2010 für mich einwandfrei funktioniert (auch mit Null-Parametern!):

#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

Wenn Sie ein Makro mit optionalen Parametern möchten, können Sie Folgendes tun:

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

Das hat auch bei mir in vc funktioniert. Bei Nullparametern funktioniert dies jedoch nicht.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8

Ich bekommeunresolved external symbol _bool referenced in function _main
Avidan Borisov

Ja, das kann in einigen Fällen passieren. Sie müssen sich bewusst sein, dass bool (#__ VA_ARGS__)? unterscheidet sich von den anderen Makros, da es zur Laufzeit ausgewertet wird. Abhängig von Ihrem Fall können Sie diesen Teil des Codes jedoch weglassen.
Syphorlat

2
Am Ende hatte ich tatsächlich pastebin.com/H3T75dcn, was perfekt funktioniert (auch 0 Argumente).
Avidan Borisov

Danke für den Link, und ja, du kannst es auch mit sizeof machen, aber für mich hat das in einigen Fällen nicht funktioniert, aber das Prinzip ist das gleiche (boolesche Auswertung).
Syphorlate

Können Sie einige Beispiele nennen, bei denen dies fehlschlägt?
Avidan Borisov

7

gcc/ g++unterstützt Varargs-Makros, aber ich denke nicht, dass dies Standard ist. Verwenden Sie es daher auf eigenes Risiko.


4
Sie sind Standard in C99 und werden auch zu C ++ 0x hinzugefügt.
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 */
}

HAFTUNGSAUSSCHLUSS: Meist harmlos.


In Ihrem Code ist ein Fehler aufgetreten. bitte tun :%s/MY_MACRO_/THINK_/g:)
João Portela

Außerdem funktionierte es nicht mit null Argumenten unter Verwendung von g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela

1
Für Variadiac-Makros gibt es keine Null-Argumente, da das leere Token ein gültiger Platzhalter ist.
Paul Fultz II

3

Dafür ist der Präprozessor nicht wirklich ausgelegt.

Wenn Sie jedoch mit einer gewissen Lesbarkeit in den Bereich der ernsthaft herausfordernden Makroprogrammierung einsteigen möchten, sollten Sie sich die Boost-Präprozessor-Bibliothek ansehen . Schließlich wäre es nicht C ++, wenn es nicht drei vollständig Turing-kompatible Programmierebenen gäbe (Präprozessor, Metaprogrammierung von Vorlagen und C ++ auf Basisebene)!


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)

Sie wissen zum Zeitpunkt des Anrufs, wie viele Argumente Sie übergeben werden, sodass keine Überlastung erforderlich ist.


2
Ich habe tatsächlich nach der Existenz des Features gefragt.
Cenoc

3

Prägnantere Version von Derek Ledbetters Code:

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

Als großer Fan von schrecklichen Makromonstern wollte ich Jason Dengs Antwort erweitern und sie tatsächlich nutzbar machen. (Zum Guten oder Schlechten.) Das Original ist nicht sehr gut zu verwenden, da Sie die große Alphabetsuppe jedes Mal ändern müssen, wenn Sie ein neues Makro erstellen möchten, und es ist noch schlimmer, wenn Sie eine andere Anzahl von Argumenten benötigen.

Also habe ich eine Version mit folgenden Funktionen erstellt:

  • 0 Argument Fall funktioniert
  • 1 bis 16 Argumente ohne Änderungen am chaotischen Teil
  • Einfach mehr Makrofunktionen zu schreiben
  • Getestet in gcc 10, clang 9, Visual Studio 2017

Momentan habe ich maximal 16 Argumente gemacht, aber wenn Sie mehr brauchen (wirklich jetzt? Sie werden nur albern ...), können Sie FUNC_CHOOSER und CHOOSE_FROM_ARG_COUNT bearbeiten und dann NO_ARG_EXPANDER einige Kommas hinzufügen.

Weitere Informationen zur Implementierung finden Sie in der hervorragenden Antwort von Jason Deng, aber ich werde den Code einfach hier einfügen:

#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

Sie können BOOST_PP_OVERLOADaus einer boostBibliothek verwenden.

Beispiel aus dem offiziellen Boost-Dokument :

#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

Je nachdem, was Sie benötigen, können Sie dies mit var args mit Makros tun . Nun, optionale Parameter oder Makroüberladung, so etwas gibt es nicht.


-1

Keines der oben genannten Beispiele (von Derek Ledbetter, David Sorkovsky und Joe D) zum Zählen von Argumenten mit Makros hat für mich mit Microsoft VCC 10 funktioniert. Das __VA_ARGS__Argument wird immer als einzelnes Argument betrachtet (Token-Izing mit)## oder nicht) Die Argumentverschiebung, auf die sich diese Beispiele stützen, funktioniert nicht.

Also, kurze Antwort, wie von vielen anderen oben angegeben: Nein, Sie können keine Makros überladen oder optionale Argumente verwenden.


1
Sie können, aber nur in C99 oder C ++ 11 (aufgrund von __VA_ARGS__). VC2010 ist C89 / C ++ 03 (wobei einige Teile von C ++ 11 auftauchen, aber noch nicht).
Puetzk
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.