Gibt es eine Möglichkeit, optionale Parameter mit C ++ - Makros abzurufen? Eine Art Überlastung wäre auch schön.
No you can't
Gibt es eine Möglichkeit, optionale Parameter mit C ++ - Makros abzurufen? Eine Art Überlastung wäre auch schön.
No you can't
Antworten:
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.
PRINT_STRING_MACRO_CHOOSER
überhaupt gebraucht? Kann ich direkt durch seinen inneren Körper ersetzen und das Ganze mit nennen (__VA_ARGS__)
?
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)!
XXX_X(,##__VA_ARGS__,` ...
XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
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.
__FILE__
und __LINE__
und so ziemlich einfacher wird ...
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
, ##__VA_ARGS__
für GCC / CLANG und implizites Schlucken durch ##__VA_ARGS__
für MSVC). --std=c99
Wenn Sie möchten, können Sie das Fehlende an Ihren Compiler weitergeben =)Funktioniert einigermaßen plattformübergreifend , zumindest getestet
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_1
kommt teilweise aus MACRO_CHOOSER(__VA_ARGS__)
, und das (10)
Teil stammt aus dem zweiten(__VA_ARGS__)
.
Der MACRO_CHOOSER
verwendet 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 20
und20 ()
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_1
ist eine Problemumgehung für GCC / CLANG, die einen (falsch positiven) Fehler unterdrückt, der besagt, dass dies ISO C99 requires rest arguments to be used
bei der Übergabe -pedantic
an Ihren Compiler der Fall ist . Das FUNC_RECOMPOSER
ist 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! =)
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
unresolved external symbol _bool referenced in function _main
gcc
/ g++
unterstützt Varargs-Makros, aber ich denke nicht, dass dies Standard ist. Verwenden Sie es daher auf eigenes Risiko.
#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.
:%s/MY_MACRO_/THINK_/g
:)
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
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)!
#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.
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;
}
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:
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;
}
Sie können BOOST_PP_OVERLOAD
aus einer boost
Bibliothek 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
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.