Einfache Frage, auf die ich im Internet keine Antwort finden konnte. Wie kann man in Makros mit variablen Argumenten die Anzahl der Argumente ermitteln? Ich bin mit Boost-Präprozessor einverstanden, wenn er die Lösung hat.
Wenn es einen Unterschied macht, versuche ich, eine variable Anzahl von Makroargumenten zu konvertieren, um die Präprozessorsequenz, -liste oder das Array für die weitere Wiederaufbereitung zu verbessern.
c++
c
c-preprocessor
variadic-macros
Anycorn
quelle
quelle
__typeof__
, um es zumindest auf einigen Compilern zum Laufen zu bringenAntworten:
Dies ist tatsächlich vom Compiler abhängig und wird von keinem Standard unterstützt.
Hier haben Sie jedoch eine Makroimplementierung , die zählt:
quelle
#define EXPAND(x) x
#define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N
#define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0))
`` `PP_NARG()
gibt 0 nicht zurück. DieGET_ARG_COUNT()
&Y_TUPLE_SIZE()
-Lösungen funktionieren.PP_NARG()
0 nicht zurück" ... ist nicht unbedingt ein Problem. Man kann sagen , dassPP_NARG()
sollte 1 aus dem gleichen Grund zurückkehrenPP_NARG(,)
sollte 2. Rückkehr Detecting 0 in der Tat in einigen Fällen nützlich sein kann, aber die Lösungen scheinen entweder weniger allgemein zu sein (erfordert , dass erste Token pasteable zu sein, welche möglicherweise oder möglicherweise nicht in Ordnung sein je nachdem, wofür Sie es verwenden) oder implementierungsspezifisch (z. B. wenn Sie den Trick zum Entfernen von Kommas zum Entfernen und Einfügen von Gnu benötigen).Normalerweise benutze ich dieses Makro, um eine Reihe von Parametern zu finden:
Vollständiges Beispiel:
Es ist ein vollständig gültiger C99-Code. Es hat jedoch einen Nachteil: Sie können das Makro nicht
SUM()
ohne Parameter aufrufen, aber GCC hat eine Lösung dafür - siehe hier .Im Fall von GCC müssen Sie also Makros wie folgt definieren:
und es funktioniert auch mit leerer Parameterliste
quelle
sizeof(int) != sizeof(void *)
?{__VA_ARGS__}
zu besetzeint[]
, ist es nurint[]
, unabhängig vom tatsächlichen Inhalt von__VA_ARGS__
##
wird in VS2017 nicht benötigt, da ein Leerzeichen__VA_ARGS__
automatisch alle vorhergehenden Kommas entfernt.Wenn Sie C ++ 11 verwenden und den Wert als C ++ - Kompilierungszeitkonstante benötigen, ist dies eine sehr elegante Lösung:
Bitte beachten Sie: Die Zählung erfolgt vollständig zur Kompilierungszeit. Der Wert kann immer dann verwendet werden, wenn eine Ganzzahl zur Kompilierungszeit erforderlich ist, z. B. als Vorlagenparameter für std :: array.
quelle
sizeof((int[]){__VA_ARGS__})/sizeof(int)
oben vorgeschlagen, funktioniert es auch dann, wenn nicht alle Argumente verwendet werden könnenint
.#define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
Der Einfachheit halber ist hier eine Implementierung, die für 0 bis 70 Argumente funktioniert und in Visual Studio, GCC und Clang funktioniert . Ich glaube, dass es in Visual Studio 2010 und höher funktionieren wird, habe es aber nur in VS2013 getestet.
quelle
__VA_ARGS__
" (das in C ++ technisch gesehen eine (nahezu universelle, de facto Standard) Compiler-Erweiterung bis C ++ 20 ist) etwas besser ist . Die meisten (? Alle) Compilern erlauben Länge Null, aber Drossel auf dem Komma , wenn die Liste ist leer (und Überlastung##
als proto__VA_OPT__
, das Komma in diesem Fall zu entfernen); MSVC Version der Erweiterung nicht ersticken gerade nicht auf dem Komma (aber wird ersticken die überlastet##
). Vergleichen Sie MSVCunused, __VA_ARGS__
mit Nicht-MSVC0, ## __VA_ARGS__
. beides ist nicht korrekter, das Problem ist, dass sie unterschiedlich sind.Es gibt einige C ++ 11-Lösungen, um die Anzahl der Argumente zur Kompilierungszeit zu ermitteln, aber ich bin überrascht zu sehen, dass niemand etwas so Einfaches vorgeschlagen hat wie:
Dies erfordert auch nicht die Aufnahme des
<tuple>
Headers.quelle
VA_COUNT(&,^,%)
. Wenn Sie über eine Funktion zählen, sehe ich keinen Sinn darin, ein Makro zu erstellen.Dies funktioniert mit 0 Argumenten mit gcc / llvm. [Links sind dumm]
Visual Studio scheint den Operator ## zu ignorieren, mit dem das leere Argument verwendet wird. Sie können das wahrscheinlich mit so etwas umgehen
quelle
##__VA_ARGS__
das Komma vorher zu essen, wenn__VA_ARGS__
es leer ist, ist eine GCC-Erweiterung. Es ist nicht das Standardverhalten.Mit MSVC-Erweiterung:
Funktioniert für 0 - 32 Argumente. Diese Grenze kann leicht erweitert werden.
BEARBEITEN: Vereinfachte Version (funktioniert in VS2015 14.0.25431.01 Update 3 & gcc 7.4.0) bis zu 100 Argumente zum Kopieren und Einfügen:
quelle
Y_TUPLE_SIZE("Hello")
, was es ziemlich unmöglich macht. Ich stimme @osirisgothra zu.Ich gehe davon aus, dass jedes Argument für VA_ARGS durch Kommas getrennt wird. Wenn ja, denke ich, sollte dies ein ziemlich sauberer Weg sein, dies zu tun.
Arbeitete für mich an Godbolt für Clang 4 und GCC 5.1. Dies wird zur Kompilierungszeit berechnet, jedoch nicht für den Präprozessor ausgewertet. Wenn Sie also versuchen, FOR_EACH zu erstellen , funktioniert dies nicht.
quelle
NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=)
!!! Jeder arg String kann nicht haben einige andere Symbole wie','
wennint count = NUMARGS( foo(1, 2) );
2 statt 1 produziert werden. Godbolt.org/z/kpBuOmHier ist eine einfache Möglichkeit, 0 oder mehr Argumente von VA_ARGS zu zählen. In meinem Beispiel werden maximal 5 Variablen angenommen, aber Sie können weitere hinzufügen, wenn Sie möchten.
quelle
VA_ARGS_NUM
er mit einem Makro verwendet wird: wenn ich#define TEST
(dh leerTEST
) habe undVA_ARGS_NUM(TEST)
bei Verwendung in#if
:(Sie können Token fädeln und zählen:
quelle
Boost Preprocessor hat dies tatsächlich ab Boost 1.49, as
BOOST_PP_VARIADIC_SIZE(...)
. Es funktioniert bis zu Größe 64.Unter der Haube ist es im Grunde dasselbe wie die Antwort von Kornel Kisielewicz .
quelle
__VA_OPT__
oder den Compiler-Erweiterungen zum##__VA_ARGS__
Entfernen des vorherigen Kommas behoben werdenIch habe hier noch unvollständige Antworten gefunden.
Die nächste tragbare Implementierung, die ich hier gefunden habe, ist: C ++ - Präprozessor __VA_ARGS__ Anzahl der Argumente
Es funktioniert jedoch nicht mit den Nullargumenten im GCC ohne mindestens
-std=gnu++11
Befehlszeilenparameter.Deshalb habe ich beschlossen, diese Lösung mit der folgenden zusammenzuführen: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
https://godbolt.org/z/3idaKd
c++11
,msvc 2015
,gcc 4.7.1
,clang 3.0
quelle