171 votes

Préprocesseur et concaténation

Je suis en train d'écrire un code, d'où le nom de fonctions dépendent de la valeur d'un certain macro variable. Pour être plus précis, je suis en train d'écrire une macro comme ceci:

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

int NAME(some_function)(int a);

Malheureusement, le NOM de la macro() tourne que dans

int some_function_VARIABLE(int a);

plutôt que de

int some_function_3(int a);

donc la c'est clairement de la mauvaise façon d'aller à ce sujet. Heureusement, le nombre de valeurs possibles pour la VARIABLE est petit, de sorte que je peux tout simplement faire un #si la VARIABLE == n et la liste de tous les cas séparément, mais je me demandais si il ya un moyen astucieux de le faire.

244voto

Jonathan Leffler Points 299946
$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y)  PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)

extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"





extern void mine_3(char *x);
$


Cade Roux a demandé pourquoi ce besoin de deux niveaux d'indirection. Le désinvolte réponse est parce que la norme impose de le travail; vous avez tendance à trouver vous avez besoin de l'équivalent truc avec la stringizing opérateur de trop.

L'article 6.10.3 du standard C99 couvre macro "remplacement", et 6.10.3.1 couvre l'argument de substitution".

Après les arguments en faveur de l'invocation d'une fonction-comme macro ont été identifiés, l'argument de la substitution a lieu. Un paramètre dans la liste de remplacement, que si elle est précédée par un # ou ## prétraitement jeton ou suivie par un ## prétraitement jeton (voir ci-dessous), est remplacé par l'argument correspondant après toutes les macros qui y sont contenues ont été élargi. Avant d'être remplacé chaque argument du prétraitement des jetons complètement macro remplacé comme si elles constituaient le reste de la prétraitement fichier; pas d'autres prétraitement des jetons sont disponibles.

Dans l'invocation NAME(mine), l'argument est "mien"; il est complètement déployée "mien"; il est alors remplacé par la chaîne de remplacement:

EVALUATOR(mine, VARIABLE)

Maintenant la macro ÉVALUATEUR est découvert, et les arguments sont isolés comme "mien" et "VARIABLE"; ce dernier est ensuite élargi à '3', et remplacé par la chaîne de remplacement:

PASTER(mine, 3)

Le fonctionnement de ce qui est couvert par d'autres règles (6.10.3.3 '# # opérateur"):

Si, dans la liste de remplacement d'une fonction-comme macro, un paramètre est immédiatement précédée ou suivie par un ## prétraitement jeton, le paramètre est remplacé par le correspondant l'argument de prétraitement jeton de séquence; [...]

Pour à la fois objet et la fonction-comme macro invocations, avant le remplacement de la liste est revu pour plus de noms de macro pour remplacer, chaque instance d'un ## prétraitement jeton dans la liste de remplacement (pas d'argument) est supprimé et le précédent prétraitement jeton est concaténé avec la suite de prétraitement jeton.

Ainsi, la liste de remplacement contient x suivie par ## et également ## suivie par y; nous avons donc:

mine ## _ ## 3

et l'élimination de l' ## des jetons et de la concaténation des jetons sur chaque côté combine 'mine' avec '_' et '3', pour obtenir:

mine_3

C'est le résultat souhaité.


Si nous examinons la question d'origine, le code a été adapté à l'usage "mine" au lieu de "une_fonction'):

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

NAME(mine)

L'argument de NOM est clairement le "mien" et qui est entièrement développée.
En suivant les règles de 6.10.3.3, nous trouvons:

mine ## _ ## VARIABLE

qui, lorsque l' ## des opérateurs sont éliminés, les cartes à:

mine_VARIABLE

exactement comme indiqué dans la question.

34voto

Stephen Canon Points 58003
 #define VARIABLE 3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun) NAME1(fun,VARIABLE)

int NAME(some_function)(int a);
 

Honnêtement, vous ne voulez pas savoir pourquoi cela fonctionne. Si vous savez pourquoi cela fonctionne, vous deviendrez ce type au travail qui connaît ce genre de choses et tout le monde viendra vous poser des questions. =)

Edit: si vous voulez réellement savoir pourquoi cela fonctionne, je publierai volontiers une explication, en supposant que personne ne me bat.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X