Erstellen eines C-Makros mit ## und __LINE__ (Token-Verkettung mit Positionierungsmakro)

107

Ich möchte ein C-Makro erstellen, das eine Funktion mit einem Namen basierend auf der Zeilennummer erstellt. Ich dachte, ich könnte so etwas tun (die eigentliche Funktion hätte Aussagen in geschweiften Klammern):

#define UNIQUE static void Unique_##__LINE__(void) {}

Was ich hoffte, würde sich auf so etwas wie Folgendes ausweiten:

static void Unique_23(void) {}

Das geht nicht Bei der Token-Verkettung werden die Positionierungsmakros buchstäblich behandelt und schließlich erweitert auf:

static void Unique___LINE__(void) {}

Ist das möglich?

(Ja, es gibt einen wirklichen Grund, warum ich das tun möchte, egal wie nutzlos dies scheint).

DD.
quelle
Ich denke, Sie können dies mit indirekter Makroerweiterung zum Laufen bringen .
Ben Stiglitz
4
mögliches Duplikat von Wie kann man zweimal mit dem C-Präprozessor verketten und ein Makro wie in "arg ## _ ## MACRO" erweitern? Das gleiche gilt für alle anderen Makros __LINE__(obwohl dies ein häufiger Anwendungsfall ist.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

Antworten:

176

Das Problem ist, dass der Präprozessor bei einer Makroersetzung die Makros nur dann rekursiv erweitert, wenn weder der Stringisierungsoperator #noch der Token- Einfügeoperator darauf ##angewendet werden. Wenn Sie also einige zusätzliche Indirektionsebenen verwenden müssen, können Sie den Token-Pasting-Operator mit einem rekursiv erweiterten Argument verwenden:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}

Wird __LINE__dann während der Erweiterung von auf die Zeilennummer erweitert UNIQUE(da es nicht an entweder #oder beteiligt ist ##), und dann erfolgt das Einfügen des Tokens während der Erweiterung von TOKENPASTE.

Es sollte auch beachtet werden, dass es auch das __COUNTER__Makro gibt, das bei jeder Auswertung auf eine neue Ganzzahl erweitert wird, falls Sie mehrere Instanziierungen des UNIQUEMakros in derselben Zeile benötigen . Hinweis: __COUNTER__Wird von MS Visual Studio, GCC (seit V4.3) und Clang unterstützt, ist jedoch nicht Standard C.

Adam Rosenfield
quelle
3
Ich fürchte, das funktioniert nicht mit GNU cpp. TOKENPASTE verwendet LINE als Literal. TOKENPASTE (Unique_, LINE ) wird zu Unique___LINE__
DD erweitert.
3
@DD: D'oh, jetzt behoben. Es braucht 2 Ebenen der Indirektion, nicht 1.
Adam Rosenfield
Das __COUNTER__Makro hat bei mir in gcc nicht funktioniert; obwohl der __LINE__wie angekündigt funktioniert hat.
Tyler
2
Bit von zusätzlichen Informationen für jeden , der versucht COUNTER , nach msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx es ist ein Makro , die spezifisch für Microsoft.
Elva
3
Gibt es eine Erklärung, warum Sie zwei Indirektionsebenen benötigen? Ich habe es mit nur einem versucht, ohne # und ##, und das erweitert es nicht auf VS2017. Anscheinend gilt das Gleiche für GCC. Wenn Sie jedoch eine zweite Indirektionsebene hinzufügen, wird diese erweitert. Magie?
Gabe Halsmer
-2

GCC erfordert kein "Wrapping" (oder Realisieren), es sei denn, das Ergebnis muss "stringifiziert" werden. Gcc hat Funktionen, aber ALLES kann mit C-Version 1 durchgeführt werden (und einige behaupten, Berkeley 4.3 C sei so viel schneller, dass es sich lohnt, die Verwendung zu lernen).

** Clang (llvm) MACHT WEISSEN RAUM NICHT RICHTIG für die Makroerweiterung - es fügt Leerzeichen hinzu (was sicherlich das Ergebnis als C-Kennung für die weitere Vorverarbeitung zerstört) **, Clang führt einfach keine # oder * Makroerweiterung durch als C-Präprozessor wird für Jahrzehnte erwartet. Das beste Beispiel ist das Kompilieren von X11, das Makro "Concat3" ist kaputt, das Ergebnis ist jetzt MISNAMED C Identifier, das natürlich nicht erstellt werden kann. und ich fange an, Dinge zu bauen, die scheitern, sind ihr Beruf.

Ich denke, die Antwort hier ist "Neues C, das gegen Standards verstößt, ist schlechtes C". Diese Hacks entscheiden sich immer dafür (Clobber-Namespaces), die Standardeinstellungen ohne Grund zu ändern, aber C nicht wirklich "zu verbessern" (außer nach eigenem Ermessen: was ich Sagen wir, es wurde eine Erfindung gemacht, um zu erklären, warum sie mit all dem Bruch davonkommen, für den sie noch niemand verantwortlich gemacht hat.


Es ist kein Problem, dass die früheren C-Vorprozessoren UNIq_ () __ nicht unterstützten, da sie #pragma unterstützten, wodurch "Hacker-Compiler-Marken im Code als Hackery gekennzeichnet werden können" und ebenso gut funktionieren, OHNE Standards zu beeinflussen: genauso wie Änderungen Standardeinstellungen sind nutzlose Wonton-Brüche, und genauso wie das Ändern der Funktionsweise einer Funktion unter Verwendung des gleichen Namens (Namespace-Clobbering) meiner Meinung nach ... Malware ist

Niemand ist voreingenommen
quelle