Concatenación de cadenas de macros C / C ++

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

¿Es posible concatenar tiene STR3 == "s1"? Puede hacer esto pasando argumentos a otra función de macro. Pero, ¿existe una forma directa?

tvr
fuente
¿No debería ser #define STR3 STR1 ## STR2
Shrinidhi
Tampoco debería ser porque eso define a STR3 como el token de preprocesamiento STR1STR2. Y pasar argumentos a otra función de macro no ayuda, porque los literales de cadena no se pueden pegar juntos - "s" "1" no es un token válido.
Jim Balter

Respuestas:

157

Si son ambas cadenas, puede hacer:

#define STR3 STR1 STR2

El preprocesador concatena automáticamente cadenas adyacentes.

EDITAR:

Como se indica a continuación, no es el preprocesador sino el compilador el que realiza la concatenación.

Sean
fuente
17
Técnicamente, la concatenación de cadenas se realiza a nivel de lenguaje.
Martin York
47
El preprocesador no hace tal cosa. Es el lenguaje C propiamente dicho el que trata los literales de cadena adyacentes como si fueran un solo literal de cadena.
Jim Balter
7
Es más que un tecnicismo: no puede concatenar L"a"y "b"obtener L"ab", pero puede concatenar L"a"y L"b"obtener L"ab".
MSalters
115

No necesita ese tipo de solución para los literales de cadena, ya que están concatenados en el nivel del lenguaje y no funcionaría de todos modos porque "s" "1" no es un token de preprocesador válido.

[Editar: En respuesta al comentario incorrecto "Solo para el registro" a continuación que, lamentablemente, recibió varios votos a favor, reiteraré la declaración anterior y observaré que el fragmento del programa

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

produce este mensaje de error de la fase de preprocesamiento de gcc: error: pegar "" s "" y "" 1 "" no da un token de preprocesamiento válido

]

Sin embargo, para pegar tokens en general, intente esto:

/*
 * 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)

Entonces, por ejemplo, ambos PPCAT_NX(s, 1)y PPCAT(s, 1)producen el identificador s1, a menos que sse defina como una macro, en cuyo caso PPCAT(s, 1)produce <macro value of s>1.

Continuando con el tema están estas macros:

/*
 * 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)

Luego,

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

Por el contrario,

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
fuente
8
Solo para que conste, "s""1"es válido en C (y C ++). Son dos tokens (literales de cadena) que el compilador concatena y amenaza como un token.
Shahbaz
4
No entiende tanto mi comentario como el lenguaje C. Dije "s""1" isn't a valid token- eso es correcto; son, como dices, dos fichas. Pero agregarlos junto con ## los convertiría en un solo token de preprocesamiento, no dos tokens, por lo que el compilador no haría una concatenación, sino que el lexer los rechazaría (el lenguaje requiere un diagnóstico).
Jim Balter
8
@ mr5 Lea los comentarios con atención. Los nombres de macro que se pasan como argumentos de macro no se expanden antes de pasar. Sin embargo, se expanden en el cuerpo de la macro. Entonces, si A se define como FRED, STRINGIZE_NX (A) se expande a "A" pero STRINGIZE (A) se expande a STRINGIZE_NX (FRED) que se expande a "FRED".
Jim Balter
1
@bharath la cadena resultante es "PPCAT (T1, T2)" , como se esperaba y se deseaba. y no el esperado "s1" - no esperado en absoluto. ¿Por qué necesitamos una indirección / anidación adicional? - Lea los comentarios del código y mi comentario anterior con los 6 votos a favor. Solo se expanden los cuerpos de macros; fuera de los cuerpos de macro, los argumentos de macro entre paréntesis no se expanden antes de pasar a las macros. Entonces se STRINGIZE_NX(whatever occurs here)expande a "lo que ocurra aquí", independientemente de las definiciones de macro para lo que sea, ocurra o aquí.
Jim Balter
1
@bharath Por supuesto que no imprime "Nombre A" - A es el nombre del parámetro, no el argumento de la macro, que es ALEX. Afirmó if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"que eso es falso y no se parece en nada a su prueba. Te estás esforzando por no entender o hacer esto bien, y no voy a responderte más.
Jim Balter
24

Sugerencia: la STRINGIZEmacro anterior es genial, pero si comete un error y su argumento no es una macro, tuvo un error tipográfico en el nombre o se olvidó #includedel archivo de encabezado, entonces el compilador pondrá felizmente el nombre de la macro supuesta en el cadena sin error.

Si tiene la intención de que el argumento de STRINGIZEsea ​​siempre una macro con un valor C normal, entonces

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

lo expandirá una vez y verificará su validez, lo descartará y luego lo expandirá nuevamente en una cadena.

Me tomó un tiempo descubrir por qué STRINGIZE(ENOENT)terminaba como en "ENOENT"lugar de "2"... no había incluidoerrno.h .

Jordan Brown
fuente
2
Observación importante y +1 para el uso adecuado del ,operador. :)
Jesse Chisholm
2
No hay ninguna razón en particular por la que el contenido de la cadena deba ser una expresión C válida. Si desea hacer esto, le aconsejo que le dé un nombre diferente, como STRINGIZE_EXPR.
Jim Balter
Ese truco puede haber funcionado de forma aislada. Pero evita que el compilador vea una secuencia de cadenas que concatenará. (resultando en secuencias como en ((1),"1") "." ((2),"2")lugar de solo "1" "." "2")
Automórfico
Solo para aclarar lo que dice automorphic: con la STRINGIZEdefinición original , "The value of ENOENT is " STRINGIZE(ENOENT)funciona, mientras que "The value of ENOENT is" STRINGIZE_EXPR(X)produce un error.
Jim Balter