Concaténation de chaînes de macro C / C ++

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

Est-il possible de concaténer ont STR3 == "s1"? Vous pouvez le faire en passant des arguments à une autre fonction Macro. Mais y a-t-il un moyen direct?

tvr
la source
Ne devrait-il pas être #define STR3 STR1 ## STR2
Shrinidhi
Cela ne devrait pas non plus être parce que cela définit STR3 comme étant le jeton de prétraitement STR1STR2. Et passer des arguments à une autre fonction macro n'aide pas, car les chaînes littérales ne peuvent pas être collées ensemble - "s" "1" n'est pas un jeton valide.
Jim Balter

Réponses:

157

Si ce sont les deux chaînes, vous pouvez simplement faire:

#define STR3 STR1 STR2

Le préprocesseur concatène automatiquement les chaînes adjacentes.

ÉDITER:

Comme indiqué ci-dessous, ce n'est pas le préprocesseur mais le compilateur qui effectue la concaténation.

Sean
la source
17
Techniquement, la concaténation des chaînes est effectuée au niveau de la langue.
Martin York
47
Le préprocesseur ne fait rien de tel. C'est le langage C proprement dit qui traite les littéraux de chaîne adjacents comme s'il s'agissait d'un seul littéral de chaîne.
Jim Balter
7
C'est plus qu'une technicité - vous ne pouvez pas concaténer L"a"et "b"obtenir L"ab", mais vous pouvez concaténer L"a"et L"b"obtenir L"ab".
MSalters
115

Vous n'avez pas besoin de ce genre de solution pour les chaînes littérales, puisqu'elles sont concaténées au niveau de la langue, et cela ne fonctionnerait pas de toute façon parce que "s" "1" n'est pas un jeton de préprocesseur valide.

[Edit: En réponse au commentaire incorrect "Juste pour mémoire" ci-dessous qui a malheureusement reçu plusieurs votes positifs, je vais réitérer la déclaration ci-dessus et observer que le fragment de programme

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

produit ce message d'erreur à partir de la phase de prétraitement de gcc: error: coller "" s "" et "" 1 "" ne donne pas un jeton de prétraitement valide

]

Cependant, pour le collage général de jetons, essayez ceci:

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

Ensuite, par exemple, les deux PPCAT_NX(s, 1)et PPCAT(s, 1)produisent l'identifiant s1, sauf s'il sest défini comme une macro, auquel cas PPCAT(s, 1)produit <macro value of s>1.

Continuant sur le thème sont ces 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)

Ensuite,

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

Par contre,

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
la source
8
Pour mémoire, "s""1"est valide en C (et C ++). Ce sont deux jetons (littéraux de chaîne) que le compilateur concatre et menace comme un seul jeton.
Shahbaz
4
Vous avez mal compris mon commentaire et le langage C. J'ai dit "s""1" isn't a valid token- c'est exact; il s'agit, comme vous le dites, de deux jetons. Mais les assembler avec ## en ferait un seul jeton de prétraitement, pas deux jetons, et ainsi le compilateur ne ferait pas de concaténation, mais plutôt le lexeur les rejetterait (le langage nécessite un diagnostic).
Jim Balter
8
@ mr5 Lisez attentivement les commentaires. Les noms de macro passés en tant qu'arguments de macro ne sont pas développés avant d'être passés. Ils sont cependant développés dans le corps de la macro. Donc, si A est défini comme FRED, STRINGIZE_NX (A) se développe en "A" mais STRINGIZE (A) se développe en STRINGIZE_NX (FRED) qui se développe en "FRED".
Jim Balter
1
@bharath la chaîne résultante est "PPCAT (T1, T2)" - comme prévu et souhaité. et pas le "s1" attendu - pas du tout prévu. Pourquoi avons-nous besoin d'une indirection / imbrication supplémentaire? - Lisez les commentaires du code, et mon commentaire ci-dessus avec les 6 votes positifs. Seuls les corps des macros sont développés; en dehors des corps de macro, les arguments de macro entre parenthèses ne sont pas développés avant d'être passés aux macros. STRINGIZE_NX(whatever occurs here)S'étend donc à "tout ce qui se passe ici", quelles que soient les définitions de macro pour ce qui se passe ou ici.
Jim Balter
1
@bharath Bien sûr, il n'imprime pas "Nom A" - A est le nom du paramètre, pas l'argument de la macro, qui est ALEX. Vous avez affirmé if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- c'est faux et cela n'a rien à voir avec votre test. Vous vous efforcez de ne pas comprendre ou de ne pas comprendre cela correctement, et je ne vais pas vous répondre davantage.
Jim Balter
24

Astuce: La STRINGIZEmacro ci-dessus est cool, mais si vous faites une erreur et que son argument n'est pas une macro - vous avez eu une faute de frappe dans le nom, ou vous avez oublié #includele fichier d'en-tête - alors le compilateur mettra volontiers le nom de la macro dans le chaîne sans erreur.

Si vous souhaitez que l'argument de STRINGIZEsoit toujours une macro avec une valeur C normale, alors

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

le développera une fois et vérifiera sa validité, le rejettera, puis le développera à nouveau en une chaîne.

Il m'a fallu un certain temps pour comprendre pourquoi STRINGIZE(ENOENT)finissait comme "ENOENT"au lieu de "2"... je n'avais pas inclus errno.h.

Jordan Marron
la source
2
Remarque importante, et +1 pour une utilisation correcte de l' ,opérateur. :)
Jesse Chisholm
2
Il n'y a aucune raison particulière pour laquelle le contenu de la chaîne doit être une expression C valide. Si vous voulez faire cela, je vous conseille de lui donner un nom différent, comme STRINGIZE_EXPR.
Jim Balter
Cette astuce a peut-être fonctionné de manière isolée. Mais cela empêche le compilateur de voir une séquence de chaînes qu'il concaténera. (résultant en des séquences comme ((1),"1") "." ((2),"2")au lieu de seulement "1" "." "2")
automorphique
Juste pour clarifier ce que dit automorphique: avec la STRINGIZEdéfinition originale , "The value of ENOENT is " STRINGIZE(ENOENT)fonctionne, alors que "The value of ENOENT is" STRINGIZE_EXPR(X)produit une erreur.
Jim Balter le