Verkettung von C / C ++ - Makrozeichenfolgen

121
#define STR1      "s"
#define STR2      "1"
#define STR3      STR1 ## STR2

Ist es möglich, STR3 == "s1" zu verketten? Sie können dies tun, indem Sie Argumente an eine andere Makrofunktion übergeben. Aber gibt es einen direkten Weg?

tvr
quelle
Sollte es nicht #define STR3 STR1
##
Dies sollte auch nicht der Fall sein, da dadurch STR3 als Vorverarbeitungstoken STR1STR2 definiert wird. Und das Übergeben von Argumenten an eine andere Makrofunktion hilft nicht, da Zeichenfolgenliterale nicht zusammengefügt werden können - "s" "1" ist kein gültiges Token.
Jim Balter

Antworten:

157

Wenn beide Zeichenfolgen sind, können Sie einfach Folgendes tun:

#define STR3 STR1 STR2

Der Präprozessor verkettet automatisch benachbarte Zeichenfolgen.

BEARBEITEN:

Wie unten erwähnt, ist es nicht der Präprozessor, sondern der Compiler, der die Verkettung vornimmt.

Sean
quelle
17
Technisch gesehen erfolgt die Verkettung von Zeichenfolgen auf Sprachebene.
Martin York
47
Der Präprozessor macht so etwas nicht. Es ist die eigentliche C-Sprache, die benachbarte String-Literale so behandelt, als wären sie ein einzelnes String-Literal.
Jim Balter
7
Es ist mehr als eine technische Angelegenheit - man kann nicht verketten L"a"und "b"bekommen L"ab", aber man kann verketten L"a"und L"b"bekommen L"ab".
MSalters
115

Sie benötigen diese Art von Lösung für Zeichenfolgenliterale nicht, da sie auf Sprachebene verkettet sind und es sowieso nicht funktionieren würde, da "s" "1" kein gültiges Präprozessor-Token ist.

[Bearbeiten: Als Antwort auf den falschen Kommentar "Nur für den Datensatz" unten, der leider mehrere positive Stimmen erhalten hat, werde ich die obige Aussage wiederholen und feststellen, dass das Programm fragmentiert ist

#define PPCAT_NX(A, B) A ## B
PPCAT_NX("s", "1")

erzeugt diese Fehlermeldung aus der Vorverarbeitungsphase von gcc: error: Das Einfügen von "" s "" und "" 1 "" ergibt kein gültiges Vorverarbeitungstoken

]]

Versuchen Sie jedoch für das allgemeine Einfügen von Token Folgendes:

/*
 * Concatenate preprocessor tokens A and B without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define PPCAT_NX(A, B) A ## B

/*
 * Concatenate preprocessor tokens A and B after macro-expanding them.
 */
#define PPCAT(A, B) PPCAT_NX(A, B)

Dann zB beide PPCAT_NX(s, 1)und PPCAT(s, 1)erzeugen den Bezeichner s1, sofern nicht sals Makro definiert, in welchem ​​Fall PPCAT(s, 1)erzeugt wird <macro value of s>1.

Weiter zum Thema sind diese Makros:

/*
 * Turn A into a string literal without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define STRINGIZE_NX(A) #A

/*
 * Turn A into a string literal after macro-expanding it.
 */
#define STRINGIZE(A) STRINGIZE_NX(A)

Dann,

#define T1 s
#define T2 1
STRINGIZE(PPCAT(T1, T2)) // produces "s1"

Im Gegensatz,

STRINGIZE(PPCAT_NX(T1, T2)) // produces "T1T2"
STRINGIZE_NX(PPCAT_NX(T1, T2)) // produces "PPCAT_NX(T1, T2)"

#define T1T2 visit the zoo
STRINGIZE(PPCAT_NX(T1, T2)) // produces "visit the zoo"
STRINGIZE_NX(PPCAT(T1, T2)) // produces "PPCAT(T1, T2)"
Jim Balter
quelle
8
Nur für den Datensatz, "s""1"ist in C (und C ++) gültig. Dies sind zwei Token (String-Literale), die der Compiler selbst zusammenfassen und als ein Token bedrohen würde.
Shahbaz
4
Sie verstehen sowohl meinen Kommentar als auch die C-Sprache falsch. Ich sagte "s""1" isn't a valid token- das ist richtig; Es sind, wie Sie sagen, zwei Token. Wenn Sie sie jedoch zusammen mit ## anheften, werden sie zu einem einzigen Vorverarbeitungstoken, nicht zu zwei Token, und der Compiler führt keine Verkettung durch, sondern der Lexer lehnt sie ab (die Sprache erfordert eine Diagnose).
Jim Balter
8
@ mr5 Lesen Sie die Kommentare sorgfältig durch. Als Makroargumente übergebene Makronamen werden vor der Übergabe nicht erweitert. Sie werden jedoch im Hauptteil des Makros erweitert. Wenn also A als FRED definiert ist, wird STRINGIZE_NX (A) zu "A" erweitert, STRINGIZE (A) jedoch zu STRINGIZE_NX (FRED), das zu "FRED" erweitert wird.
Jim Balter
1
@bharath die resultierende Zeichenfolge ist "PPCAT (T1, T2)" - wie erwartet und gewünscht. und nicht das erwartete "s1" - überhaupt nicht erwartet. Warum brauchen wir eine zusätzliche Indirektion / Verschachtelung? - Lesen Sie die Codekommentare und meinen Kommentar oben mit den 6 positiven Stimmen. Nur die Körper von Makros werden erweitert; Außerhalb von Makrokörpern werden Makroargumente zwischen Klammern nicht erweitert, bevor sie an Makros übergeben werden. Erweitert STRINGIZE_NX(whatever occurs here)sich also zu "was auch immer hier vorkommt", unabhängig von Makrodefinitionen für was auch immer, vorkommt oder hier.
Jim Balter
1
@bharath Natürlich wird nicht "Name A" ausgegeben - A ist der Parametername, nicht das Argument für das Makro, das ALEX ist. Sie haben behauptet if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- das ist falsch und nichts mit Ihrem Test zu tun. Sie bemühen sich, dies nicht zu verstehen oder richtig zu machen, und ich werde Ihnen nicht weiter antworten.
Jim Balter
24

Hinweis: Das STRINGIZEobige Makro ist cool, aber wenn Sie einen Fehler machen und sein Argument kein Makro ist - Sie hatten einen Tippfehler im Namen oder haben #includedie Header-Datei vergessen -, fügt der Compiler den angeblichen Makronamen gerne in das ein Zeichenfolge ohne Fehler.

Wenn Sie beabsichtigen, dass das Argument to STRINGIZEimmer ein Makro mit einem normalen C-Wert ist, dann

#define STRINGIZE(A) ((A),STRINGIZE_NX(A))

wird es einmal erweitern und auf Gültigkeit prüfen, verwerfen und dann erneut zu einer Zeichenfolge erweitern.

Es dauerte eine Weile, um herauszufinden , warum STRINGIZE(ENOENT)wurde enden wie "ENOENT"statt "2"... Ich hatte nicht im Preis enthalten errno.h.

Jordan Brown
quelle
2
Wichtige Beobachtung und +1 für die ordnungsgemäße Verwendung des ,Bedieners. :)
Jesse Chisholm
2
Es gibt keinen besonderen Grund, warum der Inhalt der Zeichenfolge ein gültiger C-Ausdruck sein sollte. Wenn Sie dies tun möchten, empfehle ich, ihm einen anderen Namen zu geben, z. B. STRINGIZE_EXPR.
Jim Balter
Dieser Trick hat möglicherweise isoliert funktioniert. Es verhindert jedoch, dass der Compiler eine Folge von Zeichenfolgen sieht, die er verketten wird. (was zu Sequenzen wie ((1),"1") "." ((2),"2")statt nur "1". "" 2 "führt)
automorph
Nur um zu verdeutlichen, was automorphic sagt: Mit der ursprünglichen STRINGIZEDefinition "The value of ENOENT is " STRINGIZE(ENOENT)funktioniert, während "The value of ENOENT is" STRINGIZE_EXPR(X)ein Fehler erzeugt.
Jim Balter