Création d'une macro C avec ## et __LINE__ (concaténation de jetons avec macro de positionnement)

107

Je souhaite créer une macro C qui crée une fonction avec un nom basé sur le numéro de ligne. Je pensais que je pourrais faire quelque chose comme (la fonction réelle aurait des déclarations entre accolades):

#define UNIQUE static void Unique_##__LINE__(void) {}

Ce que j'espérais étendre à quelque chose comme:

static void Unique_23(void) {}

Cela ne marche pas. Avec la concaténation de jetons, les macros de positionnement sont traitées littéralement, finissant par s'étendre en:

static void Unique___LINE__(void) {}

Est-ce possible?

(Oui, il y a une vraie raison pour laquelle je veux faire cela, même si cela semble inutile).

DD.
la source
Je pense que vous pouvez faire en sorte que cela fonctionne avec une expansion macro indirecte .
Ben Stiglitz
4
duplication possible de Comment concaténer deux fois avec le préprocesseur C et développer une macro comme dans "arg ## _ ## MACRO"? Il en va de même pour toutes les macros __LINE__(bien que ce soit un cas d'utilisation courant.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

176

Le problème est que lorsque vous avez un remplacement de macro, le préprocesseur ne développera les macros de manière récursive que si ni l'opérateur de stringizing #ni l'opérateur de collage de jetons ne lui ##sont appliqués. Donc, vous devez utiliser des couches supplémentaires d'indirection, vous pouvez utiliser l'opérateur de collage de jetons avec un argument étendu de manière récursive:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}

Ensuite, __LINE__obtient étendu au numéro de ligne lors de l'expansion de UNIQUE(puisqu'il n'est pas impliqué soit #ou ##), puis le coller jeton se produit lors de l'expansion de TOKENPASTE.

Il convient également de noter qu'il existe également la __COUNTER__macro, qui se développe en un nouvel entier à chaque fois qu'elle est évaluée, au cas où vous auriez besoin de plusieurs instanciations de la UNIQUEmacro sur la même ligne. Remarque: __COUNTER__est pris en charge par MS Visual Studio, GCC (depuis V4.3) et Clang, mais n'est pas standard C.

Adam Rosenfield
la source
3
J'ai bien peur que cela ne fonctionne pas avec GNU cpp. TOKENPASTE utilise LINE comme littéral. TOKENPASTE (Unique_, LINE ) devient Unique___LINE__
DD.
3
@DD: D'oh, corrigé maintenant. Il a besoin de 2 couches d'indirection, pas de 1.
Adam Rosenfield
La __COUNTER__macro ne fonctionnait pas pour moi dans gcc; bien que celui- __LINE__ci ait fonctionné comme annoncé.
Tyler
2
Peu d'informations supplémentaires pour tous ceux qui essaient COUNTER , selon msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx, il s'agit d'une macro spécifique à Microsoft.
Elva
3
Une explication de pourquoi vous avez besoin de 2 niveaux d'indirection? Je l'ai essayé avec un seul, absent de # et ##, et cela ne l'étend pas sur VS2017. Apparemment, la même chose est vraie pour GCC. Mais si vous ajoutez un 2ème niveau d'indirection, alors il se développe. La magie?
Gabe Halsmer
-2

GCC ne nécessite pas de "wrapping" (ou de réalisation) sauf si le résultat doit être "stringifié". Gcc a des fonctionnalités mais TOUT peut être fait avec la version 1 en C (et certains affirment que Berkeley 4.3 C est tellement plus rapide qu'il vaut la peine d'apprendre à l'utiliser).

** Clang (llvm) NE FAIT PAS CORRECTEMENT L'ESPACE BLANC pour l'expansion des macros - il ajoute des espaces (ce qui détruit certainement le résultat comme étant un identificateur C pour un prétraitement ultérieur) **, clang ne fait tout simplement pas # ou * l'expansion des macros comme un préprocesseur C devrait le faire pendant des décennies. Le premier exemple est la compilation de X11, la macro "Concat3" est cassée, son résultat est maintenant MISNAMED C Identifier, qui échoue bien sûr à construire. et je commence à faire des échecs de construction.

Je pense que la réponse ici est "le nouveau C qui enfreint les normes est mauvais C", ces hacks choisissent toujours de (écraser les espaces de noms), ils changent les valeurs par défaut sans raison mais ne "améliorent pas vraiment C" (sauf à leur propre avis: ce que je disons qu'un engin est fait pour expliquer pourquoi ils s'en sortent avec tous les bris dont personne ne les a encore rendus responsables).


Ce n'est pas un problème que les préprocesseurs C précédents ne supportaient pas UNIq_ () __ car ils supportaient #pragma qui permet au "piratage de la marque du compilateur dans le code d'être signalé comme du piratage" et de fonctionner aussi bien SANS affecter les normes: tout comme changer les valeurs par défaut sont inutiles, et tout comme changer ce qu'une fonction fait tout en utilisant le même nom (coupure d'espace de noms) est ... un malware à mon avis

personne n'est biaisé
la source