Ich versuche, ein Programm zu schreiben, bei dem die Namen einiger Funktionen vom Wert einer bestimmten Makrovariablen mit einem Makro wie diesem abhängen:
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
int NAME(some_function)(int a);
Leider das Makro NAME()
verwandelt das in
int some_function_VARIABLE(int a);
statt
int some_function_3(int a);
Das ist also eindeutig der falsche Weg. Glücklicherweise ist die Anzahl der verschiedenen möglichen Werte für VARIABLE gering, sodass ich einfach eine machen kann #if VARIABLE == n
und alle Fälle separat auflisten, aber gibt es einen cleveren Weg, dies zu tun?
Standard-C-Präprozessor
$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)
extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
extern void mine_3(char *x);
$
Zwei Ebenen der Indirektion
In einem Kommentar zu einer anderen Antwort fragte Cade Roux, warum dies zwei Ebenen der Indirektion erfordert. Die leichtfertige Antwort ist, weil der Standard es so verlangt; Sie werden feststellen, dass Sie auch mit dem Zeichenfolgenoperator den entsprechenden Trick benötigen.
Abschnitt 6.10.3 des C99-Standards behandelt “Makroersetzung”, und 6.10.3.1 behandelt “Argumentersetzung”.
Nachdem die Argumente für den Aufruf eines funktionsähnlichen Makros identifiziert wurden, findet eine Argumentsubstitution statt. Ein Parameter in der Ersetzungsliste, sofern ihm nicht ein vorangestellt ist #
oder ##
Vorverarbeitungstoken oder gefolgt von a ##
Vorverarbeitungstoken (siehe unten), wird durch das entsprechende Argument ersetzt, nachdem alle darin enthaltenen Makros expandiert wurden. Vor dem Ersetzen werden die Vorverarbeitungstoken jedes Arguments vollständig durch Makros ersetzt, als ob sie den Rest der Vorverarbeitungsdatei bilden würden. Es sind keine anderen Vorverarbeitungstoken verfügbar.
Im Aufruf NAME(mine)
, das Argument ist ‘mein’; es ist vollständig auf „meins“ erweitert; es wird dann in die Ersetzungszeichenfolge eingesetzt:
EVALUATOR(mine, VARIABLE)
Jetzt wird das Makro EVALUATOR entdeckt und die Argumente werden als „mein“ und „VARIABLE“ isoliert; Letzteres wird dann vollständig auf ‘3’ erweitert und in die Ersatzzeichenfolge eingesetzt:
PASTER(mine, 3)
Die Funktionsweise wird durch andere Regeln abgedeckt (6.10.3.3 „Der ##-Operator“):
Wenn in der Ersetzungsliste eines funktionsähnlichen Makros unmittelbar vor oder nach einem Parameter ein a steht ##
Vorverarbeitungstoken, der Parameter wird durch die Vorverarbeitungstokensequenz des entsprechenden Arguments ersetzt; […]
Sowohl für objektartige als auch funktionsartige Makroaufrufe wird jede Instanz von a ##
Vorverarbeitungstoken in der Ersetzungsliste (nicht aus einem Argument) wird gelöscht und das vorhergehende Vorverarbeitungstoken wird mit dem folgenden Vorverarbeitungstoken verkettet.
So enthält die Ersatzliste x
gefolgt von ##
und auch ##
gefolgt von y
; also haben wir:
mine ## _ ## 3
und Beseitigung der ##
Tokens und das Verketten der Tokens auf beiden Seiten kombiniert ‘mine’ mit ‘_’ und ‘3’, um Folgendes zu ergeben:
mine_3
Dies ist das gewünschte Ergebnis.
Wenn wir uns die ursprüngliche Frage ansehen, lautete der Code (angepasst, um „mine“ anstelle von „some_function“ zu verwenden):
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
Das Argument für NAME ist eindeutig „meins“, und das ist vollständig erweitert.
Nach den Regeln von 6.10.3.3 finden wir:
mine ## _ ## VARIABLE
was, wenn die ##
Operatoren werden eliminiert, werden abgebildet auf:
mine_VARIABLE
genau wie in der Frage angegeben.
Traditioneller C-Präprozessor
Robert Rüger fragt:
Gibt es eine Möglichkeit, dies mit dem traditionellen C-Präprozessor zu tun, der keinen Token-Einfügeoperator hat ##
?
Vielleicht, vielleicht auch nicht – das hängt vom Präprozessor ab. Einer der Vorteile des Standard-Präprozessors ist, dass er über diese Funktion verfügt, die zuverlässig funktioniert, während es für Prä-Standard-Präprozessoren unterschiedliche Implementierungen gab. Eine Anforderung besteht darin, dass der Präprozessor, wenn er einen Kommentar ersetzt, kein Leerzeichen erzeugt, wie es der ANSI-Präprozessor tun muss. Der GCC (6.3.0) C-Präprozessor erfüllt diese Anforderung; der Clang-Präprozessor von XCode 8.2.1 nicht.
Wenn es funktioniert, macht dies den Job (x-paste.c
):
#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
Beachten Sie, dass kein Leerzeichen dazwischen steht fun,
und VARIABLE
– das ist wichtig, denn wenn vorhanden, wird es in die Ausgabe kopiert, und Sie erhalten am Ende mine_ 3
wie der Name, der natürlich nicht syntaktisch gültig ist. (Kann ich jetzt bitte meine Haare zurück haben?)
Mit GCC 6.3.0 (läuft cpp -traditional x-paste.c
), Ich bekomme:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_3(char *x);
Mit Clang von XCode 8.2.1 erhalte ich:
# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2
extern void mine _ 3(char *x);
Diese Räume verderben alles. Ich stelle fest, dass beide Präprozessoren korrekt sind; verschiedene Pre-Standard-Präprozessoren zeigten beide Verhaltensweisen, was das Einfügen von Token zu einem äußerst lästigen und unzuverlässigen Prozess beim Versuch, Code zu portieren, machte. Der Standard mit der ##
Notation vereinfacht das radikal.
Es könnte andere Möglichkeiten geben, dies zu tun. Dies funktioniert jedoch nicht:
#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
GCC generiert:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_VARIABLE(char *x);
Schließen, aber keine Würfel. YMMV natürlich, abhängig von dem Pre-Standard-Präprozessor, den Sie verwenden. Ehrlich gesagt, wenn Sie mit einem Präprozessor festsitzen, der nicht kooperiert, wäre es wahrscheinlich einfacher, einen Standard-C-Präprozessor anstelle des Prä-Standard-Präprozessors zu verwenden (normalerweise gibt es eine Möglichkeit, den Compiler entsprechend zu konfigurieren). verbringen viel Zeit damit, einen Weg zu finden, die Arbeit zu erledigen.
Verwenden:
#define VARIABLE 3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun) NAME1(fun,VARIABLE)
int NAME(some_function)(int a);
Ehrlich gesagt wollen Sie nicht wissen, warum das funktioniert. Wenn Sie wissen, warum es funktioniert, werden Sie es werden dieser Kerl bei der Arbeit, der so etwas weiß, und jeder wird kommen und dir Fragen stellen. =)
Leicht verständliche Erklärung der EVALUATOR
zweistufiges Muster
Ich habe nicht jedes Wort des C-Standards vollständig verstanden, aber ich denke, dies ist ein vernünftiges Arbeitsmodell dafür, wie die in Jonathan Lefflers Antwort gezeigte Lösung funktioniert, etwas ausführlicher erklärt. Lassen Sie mich wissen, wenn mein Verständnis falsch ist, hoffentlich mit einem Minimalbeispiel, das meine Theorie widerlegt.
Für unsere Zwecke können wir uns die Makroerweiterung in drei Schritten vorstellen:
- (Prescan) Makroargumente werden ersetzt:
- Wenn sie Teil einer Verkettung oder Stringifizierung sind, werden sie genau so ersetzt wie die beim Makroaufruf angegebene Zeichenfolge, ohne dass sie erweitert werden
- Andernfalls werden sie zuerst vollständig ausgebaut und erst dann ersetzt
- Stringifikation und Verkettung passieren
- Alle definierten Makros werden expandiert
Schritt-für-Schritt-Beispiel ohne Umleitung
Haupt c
#define CAT(x) pref_ ## x
#define Y a
CAT(Y)
und erweitere es mit:
gcc -E main.c
wir bekommen:
pref_Y
Weil:
Schritt 1: Y
ist ein Makroargument von CAT
.
x
erscheint in einer Stringifizierung pref_ ## x
. Deswegen, Y
wird so eingefügt, wie es ist, ohne dass die Erweiterung Folgendes gibt:
pref_ ## Y
Schritt 2: Die Verkettung erfolgt und wir haben übrig:
pref_Y
Schritt 3: Jede weitere Makroersetzung findet statt. Aber pref_Y
ist kein bekanntes Makro, also wird es in Ruhe gelassen.
Wir können diese Theorie bestätigen, indem wir tatsächlich eine Definition hinzufügen pref_Y
:
#define CAT(x) pref_ ## x
#define Y a
#define pref_Y asdf
CAT(Y)
und jetzt wäre das Ergebnis:
asdf
weil auf Schritt 3 oben pref_Y
ist jetzt als Makro definiert und wird daher erweitert.
Schritt-für-Schritt-Beispiel mit Indirektion
Wenn wir jedoch das Zwei-Schritt-Muster verwenden:
#define CAT2(x) pref_ ## x
#define CAT(x) CAT2(x)
#define Y a
CAT(Y)
wir bekommen:
pref_a
Schritt 1: CAT
ausgewertet wird.
CAT(x)
ist definiert als CAT2(x)
also Argument x
von CAT
bei der Definition taucht bei einer Stringifizierung nicht auf: die Stringifizierung findet erst danach statt CAT2
erweitert wird, was in diesem Schritt nicht zu sehen ist.
Deswegen, Y
vollständig erweitert wird, bevor es ersetzt wird, wobei die Schritte 1, 2 und 3 durchlaufen werden, die wir hier weglassen, weil es trivial erweitert wird a
. Also setzen wir a
in CAT2(x)
geben:
CAT2(a)
Schritt 2: Es muss keine Stringifizierung durchgeführt werden
Schritt 3: Erweitern Sie alle vorhandenen Makros. Wir haben das Makro CAT2(a)
und so fahren wir fort, das zu erweitern.
Schritt 3.1: das Argument x
von CAT2
erscheint in einer Stringifizierung pref_ ## x
. Fügen Sie daher die Eingabezeichenfolge ein a
wie es ist, geben:
pref_ ## a
Schritt 3.2: stringifizieren:
pref_a
Schritt 3: Erweitern Sie alle weiteren Makros. pref_a
ist kein Makro, also sind wir fertig.
GCC-Argument Prescan-Dokumentation
Lesenswert ist auch die Dokumentation von GCC zu diesem Thema: https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html
Bonus: Wie diese Regeln verhindern, dass verschachtelte Aufrufe unendlich werden
Überlegen Sie nun:
#define f(x) (x + 1)
f(f(a))
was sich erweitert zu:
((a + 1) + 1)
anstatt unendlich zu werden.
Lassen Sie es uns aufschlüsseln:
Schritt 1: das Äußere f
wird mit Argument aufgerufen x = f(a)
.
In der Definition von f
das Argument x
ist nicht Teil einer Verkettung in der Definition (x + 1)
von f
. Deshalb wird es erst komplett ausgebaut, bevor es ausgetauscht wird.
Schritt 1.1.: Wir erweitern das Argument vollständig x = f(1)
gemäß den Schritten 1, 2 und 3 geben x = (a + 1)
.
Jetzt zurück in Schritt 1 nehmen wir das vollständig erweitert x
Argument gleich (a + 1)
und fügen Sie es in die Definition von ein f
Geben:
((a + 1) + 1)
Schritte 2 und 3: Es passiert nicht viel, da wir keine Stringifizierung und keine weiteren Makros zum Erweitern haben.
Sind Sie sicher, dass Sie stattdessen keine Funktionszeiger verwenden möchten?
– György Andrasek
29. September 2009 um 0:26 Uhr
@Jurily – Funktionszeiger arbeiten zur Laufzeit, Präprozessor arbeitet zur (vor) Kompilierzeit. Es gibt einen Unterschied, auch wenn beide für die gleiche Aufgabe verwendet werden können.
– Chris Lutz
29. September 2009 um 0:29 Uhr
Der Punkt ist, dass es in einer schnellen Berechnungsgeometriebibliothek verwendet wird, die für eine bestimmte Dimension fest verdrahtet ist. Manchmal möchte jedoch jemand in der Lage sein, es mit ein paar verschiedenen Dimensionen (z. B. 2 und 3) zu verwenden, und daher bräuchte man eine einfache Möglichkeit, Code mit dimensionsabhängigen Funktions- und Typnamen zu generieren. Außerdem ist der Code in ANSI C geschrieben, sodass das abgefahrene C++-Zeug mit Vorlagen und Spezialisierungen hier nicht anwendbar ist.
– JJ.
29. September 2009 um 3:33 Uhr
Stimmen Sie für die Wiedereröffnung ab, da diese Frage spezifisch für die rekursive Makroerweiterung ist und stackoverflow.com/questions/216875/using-in-macros ein allgemeines “Wofür ist es gut” ist. Der Titel dieser Frage sollte präzisiert werden.
– Ciro Santilli OurBigBook.com
31. Mai 2015 um 23:26 Uhr
Ich wünschte, dieses Beispiel wäre minimiert worden: Das gleiche passiert auf
#define A 0 \n #define M a ## A
: zwei haben##
ist nicht der Schlüssel.– Ciro Santilli OurBigBook.com
21. Juni 2015 um 9:49 Uhr