Wie kann man ein Makro definieren, das mit den Argumenten arbeitet, die an ein anderes Makro übergeben werden? Berücksichtigen Sie insbesondere Folgendes:
#define PRINT(a) printf(#a": %d", a) #define PRINT_ALL(...) ? ? ? THE PROBLEM ? ? ?
Gewünschte Verwendung:
int a = 1, b = 3, d = 0; PRINT_ALL(a,b,d);
Rekursive Makros sind in C mithilfe einer unkonventionellen Problemumgehung möglich. Das Ziel besteht darin, ein MAP-Makro zu erstellen, das wie folgt aussieht:
#define PRINT(a) printf(#a": %d", a) MAP(PRINT, a, b, c) /* Apply PRINT to a, b, and c */
Wir beginnen damit, eine Möglichkeit zu schaffen, etwas auszugeben, das einem Makroaufruf ähnelt, der nicht ausgewertet wird:
#define MAP_OUT
Unter Berücksichtigung der folgenden Makros:
#define A(x) x B MAP_OUT (x) #define B(x) x A MAP_OUT (x)
Die Auswertung von A(blah) erzeugt den Text:
blah B (blah)
Der Präprozessor erkennt hier keine Rekursion, da der B (bla) Anruf ist zu diesem Zeitpunkt lediglich Text. Wenn Sie diesen Text zurück in den Präprozessor einspeisen, wird er erweitert:
blah blah A (blah)
Durch die erneute Auswertung dieser Ausgabe wird das Makro A (bla) erweitert und die Rekursion abgeschlossen. Dieser Prozess wird fortgesetzt, bis die Ausgabe erneut in den Präprozessor eingespeist wird.
Um mehrere Auswertungen zu ermöglichen, verwenden wir das EVAL-Makro, um Argumente über eine Kette von Makroaufrufen weiterzuleiten:
#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__)))
Jeder level verstärkt den Aufwand seines Vorgängers und wertet die Eingabe letztendlich 365 Mal aus. Beispielsweise ergibt EVAL(A(blah)) 365 Kopien von blah, gefolgt von einem nicht ausgewerteten B (blah). Dieses Setup bietet die Grundlagen für die Rekursion innerhalb bestimmter Stapeltiefenbeschränkungen.
Die nächste Hürde besteht darin, zu bestimmen, wann die Rekursion beendet werden soll.
Wir definieren ein MAP_END-Makro als die End-of-List-Markierung:
#define MAP_END(...)
Die Auswertung dieses Makros bewirkt nichts und signalisiert das Ende der Rekursion.
Um zwischen den beiden Makros zu wählen, verwenden wir MAP_NEXT, das eine Liste vergleicht Element mit der speziellen End-of-List-Markierung ():
#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)
MAP_NEXT bestimmt, ob basierend auf dem Listenelement und dem nächsten Element fortgefahren oder gestoppt werden soll. Es gibt MAP_END zurück, wenn sie übereinstimmen, oder andernfalls den nächsten Parameter.
Mit diesen Komponenten können wir verwendbare Versionen von A und B konstruieren:
#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 eine Operation f auf das aktuelle Listenelement x an und untersuchen dann das nächste Listenelement, peek, um zu bestimmen, ob fortgefahren werden soll oder nicht.
Abschließend fügen wir alles im MAP-Makro der obersten Ebene zusammen:
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
Dieses Makro fügt am Ende der Liste eine ()-Markierung hinzu, leitet das Ganze durch EVAL und gibt das Ergebnis zurück.
Der Code ist als Bibliothek auf GitHub für verfügbar Ihre Bequemlichkeit.
Das obige ist der detaillierte Inhalt vonWie definiere ich ein Makro, das mit Argumenten arbeitet, die an ein anderes Makro in C übergeben werden?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!