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 Indirektionsebenen
In einem Kommentar zu einer anderen Antwort fragte Cade Roux , warum dies zwei Indirektionsebenen erfordert. Die leichtfertige Antwort lautet, dass der Standard dies so erfordert. Sie neigen dazu, den entsprechenden Trick auch mit dem Stringing-Operator zu 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, erfolgt die Argumentersetzung. Ein Parameter in der Ersetzungsliste wird durch das entsprechende Argument ersetzt , sofern nicht ein #
oder ein ##
Vorverarbeitungstoken oder ein ##
Vorverarbeitungstoken (siehe unten) gefolgt ist, nachdem alle darin enthaltenen Makros erweitert 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 weiteren Vorverarbeitungstoken verfügbar.
Im Aufruf NAME(mine)
ist das Argument 'meins'; es ist vollständig zu 'mir' erweitert; es wird dann in die Ersatzzeichenfolge eingesetzt:
EVALUATOR(mine, VARIABLE)
Jetzt wird das Makro EVALUATOR entdeckt und die Argumente werden als 'meins' und 'VARIABL' 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 einem Parameter unmittelbar ein ##
Vorverarbeitungstoken vorangestellt oder gefolgt wird , wird der Parameter durch die Vorverarbeitungstokensequenz des entsprechenden Arguments ersetzt. [...]
Sowohl für objektähnliche als auch für funktionsähnliche Makroaufrufe wird jede Instanz eines ##
Vorverarbeitungstokens in der Ersetzungsliste (nicht aus einem Argument) gelöscht und das vorhergehende Vorverarbeitungstoken verkettet , bevor die Ersetzungsliste erneut überprüft wird, um weitere Makronamen zu ersetzen mit dem folgenden Vorverarbeitungstoken.
Die Ersatzliste enthält also x
gefolgt von ##
und ##
gefolgt von y
; also haben wir:
mine ## _ ## 3
und das Eliminieren der ##
Token und das Verketten der Token auf beiden Seiten kombiniert 'meins' mit '_' und '3', um Folgendes zu ergeben:
mine_3
Dies ist das gewünschte Ergebnis.
Wenn wir uns die ursprüngliche Frage ansehen, war der Code (angepasst, um 'meine' anstelle von 'some_function' zu verwenden):
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
Das Argument zu NAME ist eindeutig "meins" und das ist vollständig erweitert.
Nach den Regeln von 6.10.3.3 finden wir:
mine ## _ ## VARIABLE
Wenn die ##
Operatoren eliminiert sind, werden folgende Karten zugeordnet:
mine_VARIABLE
genau wie in der Frage angegeben.
Traditioneller C-Präprozessor
Robert Rüger fragt :
Gibt es eine Möglichkeit, dies mit dem herkömmlichen C-Präprozessor zu tun, der nicht über den Token-Einfügeoperator verfügt ##
?
Vielleicht und vielleicht auch nicht - es hängt vom Präprozessor ab. Einer der Vorteile des Standard-Präprozessors besteht darin, dass er über diese Einrichtung verfügt, die zuverlässig arbeitet, 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, keinen Speicherplatz generiert, wie dies der ANSI-Präprozessor tun muss. Der GCC (6.3.0) C-Präprozessor erfüllt diese Anforderung. Der Clang-Präprozessor aus XCode 8.2.1 funktioniert nicht.
Wenn es funktioniert, erledigt 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 zwischen fun,
und kein Leerzeichen VARIABLE
steht. Dies ist wichtig, da es, falls vorhanden, in die Ausgabe kopiert wird und Sie mine_ 3
den Namen erhalten, der natürlich syntaktisch nicht gültig ist. (Kann ich jetzt bitte meine Haare zurück haben?)
Mit GCC 6.3.0 (läuft cpp -traditional x-paste.c
) bekomme ich:
# 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 bekomme 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 ärgerlichen und unzuverlässigen Prozess machte, wenn versucht wurde, Code zu portieren. Der Standard mit der ##
Notation vereinfacht das radikal.
Es gibt möglicherweise andere Möglichkeiten, 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);
Nah dran, aber keine Würfel. YMMV natürlich abhängig vom verwendeten Preprozessor-Präprozessor. Ehrlich gesagt, wenn Sie mit einem Präprozessor feststecken, der nicht kooperiert, ist es wahrscheinlich einfacher, einen Standard-C-Präprozessor anstelle des Prä-Standard-Präprozessors zu verwenden (es gibt normalerweise eine Möglichkeit, den Compiler entsprechend zu konfigurieren) als zu Verbringen Sie viel Zeit damit, einen Weg zu finden, um die Arbeit zu erledigen.