Concatenação de cadeia de macro C / C ++

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

É possível concatenar ter STR3 == "s1"? Você pode fazer isso passando args para outra função Macro. Mas existe um caminho direto?

tvr
fonte
Não deveria ser #define
STR3 STR1
Também não deve ser porque isso define STR3 como o token de pré-processamento STR1STR2. E passar args para outra função de macro não ajuda, porque os literais de string não podem ser colados juntos - "s" "1" não é um token válido.
Jim Balter

Respostas:

157

Se forem as duas strings, você pode fazer:

#define STR3 STR1 STR2

O pré-processador concatena automaticamente as seqüências adjacentes.

EDITAR:

Como observado abaixo, não é o pré-processador, mas o compilador que faz a concatenação.

Sean
fonte
17
Tecnicamente, a concatenação de cadeias é feita no nível do idioma.
Martin York
47
O pré-processador não faz isso. É a linguagem C propriamente dita que trata literais de string adjacentes como se fossem um único literal de string.
Jim Balter 10/03
7
É mais do que um detalhe técnico - você não pode concatenar L"a"e "b"obter L"ab", mas pode concatenar L"a"e L"b"obter L"ab".
MSalters 10/03/11
115

Você não precisa desse tipo de solução para literais de cadeias, pois elas são concatenadas no nível do idioma e, de qualquer maneira, não funcionariam porque "s" "1" não é um token de pré-processador válido.

[Editar: Em resposta ao comentário incorreto "Just for the record" abaixo, que infelizmente recebeu vários upvotes, reiterarei a declaração acima e observarei que o fragmento do programa

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

produz essa mensagem de erro da fase de pré-processamento do gcc: error: colar "" s "" e "" 1 "" não fornece um token de pré-processamento válido

]

No entanto, para colar o token geral, tente o seguinte:

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

Então, por exemplo, ambos PPCAT_NX(s, 1)e PPCAT(s, 1)produza o identificador s1, a menos que sseja definido como uma macro, caso em que PPCAT(s, 1)produz <macro value of s>1.

Continuando com o tema estão 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)

Então,

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

Por contraste,

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
fonte
8
Apenas para o registro, "s""1"é válido em C (e C ++). São dois tokens (strings literais) que o compilador concatena a si próprio e ameaça como um token.
Shahbaz
4
Você entende mal meu comentário e a linguagem C. Eu disse "s""1" isn't a valid token- isso está correto; são, como você diz, dois tokens. Mas juntá-los com ## os tornaria um único token de pré-processamento, não dois tokens; portanto, o compilador não faria uma concatenação, e o lexer os rejeitaria (a linguagem requer um diagnóstico).
Jim Balter
8
@ mr5 Leia os comentários com atenção. Os nomes de macro passados ​​como argumentos de macro não são expandidos antes de serem passados. Eles são, no entanto, expandidos no corpo da macro. Portanto, se A é definido como FRED, STRINGIZE_NX (A) se expande para "A", mas STRINGIZE (A) se expande para STRINGIZE_NX (FRED), que se expande para "FRED".
Jim Balter
1
@bharath a string resultante é "PPCAT (T1, T2)" - como esperado e desejado. e não o esperado "s1" - nem um pouco esperado. Por que precisamos de um indireção / aninhamento extra? - Leia os comentários do código e meu comentário acima com os 6 votos positivos. Somente os corpos das macros são expandidos; fora dos corpos de macro, os argumentos de macro entre parênteses não são expandidos antes de serem passados ​​para macros. Portanto, STRINGIZE_NX(whatever occurs here)expande para "o que quer que ocorra aqui", independentemente de quaisquer definições de macro para o que quer que ocorra, ou aqui.
Jim Balter
1
@bharath Claro que não imprime "Nome A" - A é o nome do parâmetro, não o argumento para a macro, que é ALEX. Você alegou if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- isso é falso e não é nada parecido com o seu teste. Você está se esforçando para não entender ou entender direito, e eu não vou responder mais a você.
Jim Balter
24

Dica: A STRINGIZEmacro acima é legal, mas se você cometer um erro e seu argumento não for uma macro - você digitou um erro de digitação no nome ou esqueceu #includeo arquivo de cabeçalho -, o compilador colocará o nome da macro no campo string sem erro.

Se você pretende que o argumento to STRINGIZEseja sempre uma macro com um valor C normal,

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

irá expandi-lo uma vez e verificar sua validade, descarte-o e depois expanda-o novamente em uma sequência.

Levei um tempo para descobrir por que STRINGIZE(ENOENT)estava acabando, em "ENOENT"vez de "2"... eu não tinha incluído errno.h.

Jordan Brown
fonte
2
Observação importante e +1 para o uso adequado do ,operador. :)
Jesse Chisholm
2
Não há nenhuma razão específica para o conteúdo da string ser uma expressão C válida. Se você quiser fazer isso, aconselho atribuir um nome diferente, como STRINGIZE_PRESS.
Jim Balter
Esse truque pode ter funcionado isoladamente. Mas impede que o compilador veja uma sequência de seqüências de caracteres que concatenará. (resultando em sequências como em ((1),"1") "." ((2),"2")vez de apenas "1" "." "2")
automorphic
Apenas para esclarecer o que automórfico está dizendo: com a STRINGIZEdefinição original , "The value of ENOENT is " STRINGIZE(ENOENT)funciona, enquanto "The value of ENOENT is" STRINGIZE_EXPR(X)produz um erro.
Jim Balter