Ich frage mich, ob es möglich ist, ein Makro foreach für Makroargumente zu schreiben. Hier ist, was Sie tun möchten:
#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL(...) ? ? ? THE PROBLEM ? ? ?
Und mögliche Verwendung:
int a = 1, b = 3, d = 0;
PRINT_ALL(a,b,d);
Hier ist, was ich bisher erreicht habe
#define FIRST_ARG(arg,...) arg
#define AFTER_FIRST_ARG(arg,...) , ##__VA_ARGS__
#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL PRINT(FIRST_ARG(__VA_ARGS__)); PRINT_ALL(AFTER_FIRST_ARG(__VA_ARGS__))
Dies ist ein rekursives Makro, das illegal ist. Und ein weiteres Problem damit ist stop condition
der Rekursion.

William Swanson
Ja, rekursive Makros sind in C mit einem ausgefallenen Workaround möglich. Das Endziel ist die Erstellung eines MAP
Makro, das so funktioniert:
#define PRINT(a) printf(#a": %d", a)
MAP(PRINT, a, b, c) /* Apply PRINT to a, b, and c */
Einfache Rekursion
Zuerst brauchen wir eine Technik, um etwas auszugeben, das wie ein Makroaufruf aussieht, es aber noch nicht ist:
#define MAP_OUT
Stellen Sie sich vor, wir haben die folgenden Makros:
#define A(x) x B MAP_OUT (x)
#define B(x) x A MAP_OUT (x)
Auswertung des Makros A (blah)
erzeugt den Ausgabetext:
blah B (blah)
Der Präprozessor sieht keine Rekursion, da die B (blah)
call ist an dieser Stelle nur Klartext, und B
ist nicht einmal der Name des aktuellen Makros. Das Zurückführen dieses Textes in den Präprozessor erweitert den Aufruf und erzeugt die Ausgabe:
blah blah A (blah)
Eine dritte Auswertung der Ausgabe erweitert die A (blah)
Makro, das den Rekursionskreis trägt. Die Rekursion wird fortgesetzt, solange der Aufrufer den Ausgabetext weiterhin in den Präprozessor einspeist.
Um diese wiederholten Bewertungen durchzuführen, das Folgende EVAL
Makro übergibt seine Argumente an einen Baum von Makroaufrufen:
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))
#define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))
#define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
#define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
#define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
Jedes Level multipliziert den Aufwand des vorherigen Levels und wertet die Eingabe insgesamt 365 Mal aus. Mit anderen Worten, anrufen EVAL (A (blah))
würde 365 Kopien des Wortes produzieren blah
gefolgt von einem letzten unbewerteten B (blah)
. Dies liefert zumindest innerhalb einer bestimmten Stack-Tiefe das Grundgerüst für die Rekursion.
Erkennung beenden
Die nächste Herausforderung besteht darin, die Rekursion zu stoppen, wenn sie das Ende der Liste erreicht.
Die Grundidee besteht darin, anstelle des normalen rekursiven Makros den folgenden Makronamen auszugeben, wenn die Zeit zum Beenden gekommen ist:
#define MAP_END(...)
Das Auswerten dieses Makros bewirkt nichts, wodurch die Rekursion beendet wird.
Um tatsächlich zwischen den beiden Makros zu wählen, folgendes MAP_NEXT
-Makro vergleicht ein einzelnes Listenelement mit der speziellen Listenende-Markierung
()
. Das Makro kehrt zurück MAP_END
wenn der Artikel übereinstimmt, oder die next
Parameter, wenn das Element etwas anderes ist:
#define MAP_GET_END() 0, MAP_END
#define MAP_NEXT0(item, next, ...) next MAP_OUT
#define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0)
#define MAP_NEXT(item, next) MAP_NEXT1 (MAP_GET_END item, next)
Dieses Makro funktioniert, indem das Element neben dem platziert wird MAP_GET_END
Makro. Wenn dies einen Makroaufruf bildet, wird alles um einen Slot in verschoben
MAP_NEXT0
Parameterliste, Änderung der Ausgabe. Das MAP_OUT
Trick verhindert, dass der Präprozessor das Endergebnis auswertet.
Alles zusammenfügen
Mit diesen Teilen ist es nun möglich, nützliche Versionen von zu implementieren A
und B
Makros aus obigem Beispiel:
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
Diese Makros wenden die Operation an f
zum aktuellen Listeneintrag x
. Dann untersuchen sie den nächsten Listenpunkt, peek
um zu sehen, ob sie fortfahren sollen oder nicht.
Der letzte Schritt besteht darin, alles auf einer obersten Ebene zusammenzufassen MAP
Makro:
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
Dieses Makro platziert a ()
Markierung am Ende der Liste, sowie ein Extra
0
für ANSI-Konformität (andernfalls hätte die letzte Iteration eine unzulässige Liste der Länge 0). Dann durchläuft er das Ganze EVAL
und gibt das Ergebnis zurück.
Ich habe diesen Code als hochgeladen Bibliothek auf github Für Ihren Komfort.
Verwenden PPNARG
, habe ich eine Reihe von Makros geschrieben, um ein Makro auf jedes Argument in einem Makro anzuwenden. Ich nenne es ein variadisches X-Makro.
/*
* The PP_NARG macro evaluates to the number of arguments that have been
* passed to it.
*
* Laurent Deniau, "__VA_NARG__," 17 January 2006, <comp.std.c> (29 November 2007).
*/
#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
PPNARG
lässt uns zählen, wie viele Argumente es gibt. Dann hängen wir diese Nummer an den Makronamen an und rufen es mit den ursprünglichen Argumenten auf.
/* need extra level to force extra eval */
#define Paste(a,b) a ## b
#define XPASTE(a,b) Paste(a,b)
/* APPLYXn variadic X-Macro by M Joshua Ryan */
/* Free for all uses. Don't be a jerk. */
/* I got bored after typing 15 of these. */
/* You could keep going upto 64 (PPNARG's limit). */
#define APPLYX1(a) X(a)
#define APPLYX2(a,b) X(a) X(b)
#define APPLYX3(a,b,c) X(a) X(b) X(c)
#define APPLYX4(a,b,c,d) X(a) X(b) X(c) X(d)
#define APPLYX5(a,b,c,d,e) X(a) X(b) X(c) X(d) X(e)
#define APPLYX6(a,b,c,d,e,f) X(a) X(b) X(c) X(d) X(e) X(f)
#define APPLYX7(a,b,c,d,e,f,g) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g)
#define APPLYX8(a,b,c,d,e,f,g,h) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h)
#define APPLYX9(a,b,c,d,e,f,g,h,i) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i)
#define APPLYX10(a,b,c,d,e,f,g,h,i,j) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j)
#define APPLYX11(a,b,c,d,e,f,g,h,i,j,k) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k)
#define APPLYX12(a,b,c,d,e,f,g,h,i,j,k,l) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l)
#define APPLYX13(a,b,c,d,e,f,g,h,i,j,k,l,m) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m)
#define APPLYX14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m) X(n)
#define APPLYX15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \
X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m) X(n) X(o)
#define APPLYX_(M, ...) M(__VA_ARGS__)
#define APPLYXn(...) APPLYX_(XPASTE(APPLYX, PP_NARG(__VA_ARGS__)), __VA_ARGS__)
Und hier sind einige Beispiele mit der Ausgabe von gcc -E
in Kommentaren.
/* Example */
#define X(a) #a,
char *list[] = {
APPLYXn(sugar,coffee,drink,smoke)
};
#undef X
/* Produces (gcc -E)
char *list[] = {
"sugar", "coffee", "drink", "smoke",
};
*/
#define c1(a) case a:
#define c2(a,b) c1(a) c1(b)
#define c3(a,b,c) c1(a) c2(b,c)
#define c4(a,b,c,d) c1(a) c3(b,c,d)
#define c_(M, ...) M(__VA_ARGS__)
#define cases(...) c_(XPASTE(c, PP_NARG(__VA_ARGS__)), __VA_ARGS__)
//cases(3,4,5,6,7)
//produces
//case 3: case 4: case 5: case 6:
#define r_(a,b) range(a,b)
#define range(a,b) a,r_(a+1,b-1)
//range(3,4)
#define ps1(a) O ## a ();
#define ps2(a,b) ps1(a) ps1(b)
#define ps3(a,b,c) ps1(a) ps2(b,c)
#define ps4(a,b,c,d) ps1(a) ps3(b,c,d)
#define ps_(M, ...) M(__VA_ARGS__)
#define ps(...) ps_(XPASTE(ps, PP_NARG(__VA_ARGS__)), __VA_ARGS__)
//ps(dup,add,sub)
Letzteres war das Motiv für das Ganze. Aber es stellte sich als nicht sehr nützlich heraus.
Da Sie akzeptieren, dass der Präprozessor VA_ARGS hat (in C99, aber nicht im aktuellen C++-Standard), können Sie mit gehen P99. Es hat genau das, wonach Sie fragen: P99_FOR. Es funktioniert ohne Rohöl ()()()
Syntax von BOOST. Die Schnittstelle ist einfach
P99_FOR(NAME, N, OP, FUNC,...)
und Sie können es mit etwas wie verwenden
#define P00_SEP(NAME, I, REC, RES) REC; RES
#define P00_VASSIGN(NAME, X, I) X = (NAME)[I]
#define MYASSIGN(NAME, ...) P99_FOR(NAME, P99_NARG(__VA_ARGS__), P00_SEP, P00_VASSIGN, __VA_ARGS__)
MYASSIGN(A, toto, tutu);

Georg Fritzsche
In C++ ohne Erweiterungen könnte man gehen Boost.Präprozessor und seine Sequenzen:
PRINT_ALL((a)(b)(c));
Durch die Nutzung BOOST_PP_SEQ_FOR_EACH()
In der Sequenz können Sie sie iterieren und einfach Code generieren, der sie druckt.
Ungetestetes einfaches Beispiel:
#define DO_PRINT(elem) std::cout << BOOST_PP_STRINGIZE(elem) << "=" << (elem) << "\n";
#define PRINT_ALL(seq) { BOOST_PP_SEQ_FOR_EACH(DO_PRINT, _, seq) }
Alte Frage, aber ich dachte, ich würde eine Lösung anbringen, die ich mir ausgedacht habe, um Boost.Preprocessor ohne das Hässliche zu verwenden (a)(b)
Syntax.
Header:
#include <iostream>
#include <boost\preprocessor.hpp>
#define _PPSTUFF_OUTVAR1(_var) BOOST_PP_STRINGIZE(_var) " = " << (_var) << std::endl
#define _PPSTUFF_OUTVAR2(r, d, _var) << _PPSTUFF_OUTVAR1(_var)
#define _PPSTUFF_OUTVAR_SEQ(vseq) _PPSTUFF_OUTVAR1(BOOST_PP_SEQ_HEAD(vseq)) \
BOOST_PP_SEQ_FOR_EACH(_PPSTUFF_OUTVAR2,,BOOST_PP_SEQ_TAIL(vseq))
#define OUTVAR(...) _PPSTUFF_OUTVAR_SEQ(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
Verwendungszweck:
int a = 3;
char b[] = "foo";
std::cout << OUTVAR(a);
// Expands to:
//
// std::cout << "a" " = " << (a ) << std::endl ;
//
// Output:
//
// a = 3
std::cout << OUTVAR(a, b);
// Expands to:
//
// std::cout << "a" " = " << (a ) << std::endl << "b" " = " << (b) << std::endl ;
//
// Output:
//
// a = 3
// b = foo
Schön und sauber.
Natürlich kann man die ersetzen std::endl
mit einem Komma oder so, wenn Sie alles in einer Zeile haben möchten.
Sie können Boost.PP verwenden (nach dem Hinzufügen Schub‘s boost
Ordner zu Ihrer Liste der Include-Verzeichnisse hinzufügen), um Makros dafür zu erhalten. Hier ist ein Beispiel (getestet mit GCC 8.1.0):
#include <iostream>
#include <limits.h>
#include <boost/preprocessor.hpp>
#define WRITER(number,middle,elem) std::cout << \
number << BOOST_PP_STRINGIZE(middle) << elem << "\n";
#define PRINT_ALL(...) \
BOOST_PP_SEQ_FOR_EACH(WRITER, =>, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
int main (int argc, char *argv[])
{
PRINT_ALL(INT_MAX, 123, "Hello, world!");
}
Ausgabe:
2=>2147483647
3=>123
4=>Hello, world!
Das BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)
part wandelt die Variablenargumentliste in die traditionelle Art von Boost um, mehrere Argumente als ein einziges Argument auszudrücken, was wie folgt aussieht: (item1)(item2)(item3)
.
Ich bin mir nicht sicher, warum die Argumente bei zwei nummeriert werden. Das Dokumentation beschreibt den ersten Parameter lediglich als „den nächsten verfügbaren BOOST_PP_FOR Wiederholung”.
Hier ist ein weiteres Beispiel, das an definiert enum
mit der Fähigkeit, es als Zeichenfolge in eine zu schreiben ostream
was auch Boosts aktiviert lexical_cast<string>
:
#define ENUM_WITH_TO_STRING(ENUMTYPE, ...) \
enum ENUMTYPE { \
__VA_ARGS__ \
}; \
inline const char* to_string(ENUMTYPE value) { \
switch (value) { \
BOOST_PP_SEQ_FOR_EACH(_ENUM_TO_STRING_CASE, _, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
default: return nullptr; \
} \
} \
inline std::ostream& operator<<(std::ostream& os, ENUMTYPE v)\
{ return os << to_string(v); }
#define _ENUM_TO_STRING_CASE(_,__,elem) \
case elem: return BOOST_PP_STRINGIZE(elem);
ENUM_WITH_TO_STRING(Color, Red, Green, Blue)
int main (int argc, char *argv[])
{
std::cout << Red << Green << std::endl;
std::cout << boost::lexical_cast<string>(Blue) << std::endl;
}
Ausgabe:
RedGreen
Blue

Friedrich Rabe
Der Präprozessor ist nicht leistungsfähig genug, um solche Dinge zu tun. Allerdings braucht man den Präprozessor nicht unbedingt. Wenn Sie nur Variablennamen und ihre Werte auf bequeme Weise ausgeben möchten. Sie könnten zwei einfache Makros haben:
#define PRINT(x) \
{ \
std::ostringstream stream; \
stream << x; \
std::cout << stream.str() << std::endl; \
}
#define VAR(v) #v << ": " << v << ", "
Sie könnten dann fast Ihre beabsichtigte Verwendung verwenden:
int a = 1, b = 3, d = 0;
PRINT(VAR(a) << VAR(b) << VAR(d))
Das druckt
a: 1, b: 3, d: 0,
Es gibt viele Möglichkeiten, dies leistungsfähiger zu machen, aber das funktioniert, ermöglicht es Ihnen, nicht ganzzahlige Werte gut zu drucken, und es ist eine ziemlich einfache Lösung.
10132000cookie-checkForeach-Makro auf Makroargumenteyes
## in AFTER_FIRST_ARG ist eine GNU-Compiler-Erweiterung von C
– kokosing
15. Juli 2011 um 12:48 Uhr
Siehe stackoverflow.com/questions/824639/…
– sehen
15. Juli 2011 um 13:11 Uhr
Sie können dieses Problem in C++ auch mit dem Komma-Operator auf schönere Weise lösen.
– Johannes Schaub – litb
15. Juli 2011 um 13:31 Uhr
@Johannes: Ohne die Zeichenfolgenvariablennamen (oder Ausdrücke oder …).
– Georg Fritzsche
15. Juli 2011 um 13:38 Uhr