Ich versuche, ein Programm zu schreiben, bei dem die Namen einiger Funktionen vom Wert einer bestimmten Makrovariablen mit einem Makro wie dem folgenden abhängen:
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
int NAME(some_function)(int a);
Leider sind die Makro - NAME()
Kurven , dass in
int some_function_VARIABLE(int a);
eher, als
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, so dass ich einfach #if VARIABLE == n
alle Fälle separat auflisten kann , aber ich habe mich gefragt, ob es eine clevere Möglichkeit gibt, dies zu tun.
#define A 0 \n #define M a ## A
: Zwei zu haben##
ist nicht der Schlüssel.Antworten:
Standard C Präprozessor
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".
Im Aufruf
NAME(mine)
ist das Argument 'meins'; es ist vollständig zu 'mir' erweitert; es wird dann in die Ersatzzeichenfolge eingesetzt: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:
Die Funktionsweise wird durch andere Regeln abgedeckt (6.10.3.3 'Der ## Operator'):
Die Ersatzliste enthält also
x
gefolgt von##
und##
gefolgt vony
; also haben wir:und das Eliminieren der
##
Token und das Verketten der Token auf beiden Seiten kombiniert 'meins' mit '_' und '3', um Folgendes zu ergeben: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):
Das Argument zu NAME ist eindeutig "meins" und das ist vollständig erweitert.
Nach den Regeln von 6.10.3.3 finden wir:
Wenn die
##
Operatoren eliminiert sind, werden folgende Karten zugeordnet:genau wie in der Frage angegeben.
Traditioneller C-Präprozessor
Robert Rüger fragt :
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
):Beachten Sie, dass zwischen
fun,
und kein LeerzeichenVARIABLE
steht. Dies ist wichtig, da es, falls vorhanden, in die Ausgabe kopiert wird und Siemine_ 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:Mit Clang von XCode 8.2.1 bekomme ich:
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:
GCC generiert:
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.
quelle
cpp -traditional
. Beachten Sie, dass es keine endgültige Antwort gibt - dies hängt vom Präprozessor ab, den Sie haben.Ehrlich gesagt möchten Sie nicht wissen, warum dies funktioniert. Wenn Sie wissen, warum es funktioniert, werden Sie der Typ bei der Arbeit, der so etwas weiß, und jeder wird kommen und Ihnen Fragen stellen. =)
Bearbeiten: Wenn Sie tatsächlich wissen möchten, warum es funktioniert, werde ich gerne eine Erklärung veröffentlichen, vorausgesetzt, niemand schlägt mich dazu.
quelle