web-dev-qa-db-fra.com

Qu'est-ce que ## dans un #define signifie?

Qu'est-ce que cette ligne signifie? Surtout, qu'est-ce que ## moyenne?

[.____] # définir l'analyse (variable, drapeau) ((quelque chose. ## variable) & (drapeau)) [.____]

Éditer:

Un peu confus encore. Quel sera le résultat sans ##?

33
Dante May Code

Un peu confus encore. Que sera le résultat sans ##?

Habituellement, vous ne remarquerez aucune différence. Mais là est une différence. Supposons que Something est de type:

struct X { int x; };
X Something;

Et regarde:

int X::*p = &X::x;
ANALYZE(x, flag)
ANALYZE(*p, flag)

Sans opérateur de concaténation jeton ##, il se développe à:

#define ANALYZE(variable, flag)     ((Something.variable) & (flag))

((Something. x) & (flag))
((Something. *p) & (flag)) // . and * are not concatenated to one token. syntax error!

Avec une concaténation jeton, il se développe à:

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))

((Something.x) & (flag))
((Something.*p) & (flag)) // .* is a newly generated token, now it works!

Il est important de se rappeler que le préprocesseur fonctionne sur des jetons de préprocesseur, non sur texte. Donc, si vous voulez concaténer deux jetons, vous devez explicitement le dire.

17
ybungalobill

## s'appelle la concaténation de jetons, utilisée pour concaténer deux jetons dans une invocation macro.

Regarde ça:

12
Nawaz

Une partie très importante est que cette concaténation de jeton suit des règles très spéciales:

par exemple. IBM DOC:

  • La concaténation a lieu avant l'élargissement des macros des arguments.
  • Si le résultat d'une concaténation est un nom de macro valide, il est disponible pour un remplacement supplémentaire même s'il apparaît dans un contexte dans lequel il ne serait normalement pas disponible.
  • Si -plus d'un ## Opérateur et/ou # Opérateur apparaît dans la liste de remplacement d'une définition de macro, L'ordre d'évaluation des opérateurs n'est pas défini.

Des exemples sont également très auto-expliqués

#define ArgArg(x, y)          x##y
#define ArgText(x)            x##TEXT
#define TextArg(x)            TEXT##x
#define TextText              TEXT##text
#define Jitter                1
#define bug                   2
#define Jitterbug             3

Avec sortie:

ArgArg(lady, bug)   "ladybug"
ArgText(con)    "conTEXT"
TextArg(book)   "TEXTbook"
TextText    "TEXTtext"
ArgArg(Jitter, bug)     3

La source est la documentation IBM. Peut varier avec d'autres compilateurs.

à votre ligne :

Il concaténe l'attribut variable au "quelque chose". et adresse une variable logiquement andé qui donne en résultat si quelque chose.Viaable a un jeu de drapeaux.

Donc, un exemple de mon dernier commentaire et de votre question (compiler avec g ++):

// this one fails with a compiler error
// #define ANALYZE1(variable, flag)     ((Something.##variable) & (flag))
// this one will address Something.a (struct)
#define ANALYZE2(variable, flag)     ((Something.variable) & (flag))
// this one will be Somethinga (global)
#define ANALYZE3(variable, flag)     ((Something##variable) & (flag))
#include <iostream>
using namespace std;

struct something{
int a;
};

int Somethinga = 0;

int main()
{
something Something;
Something.a = 1;

if (ANALYZE2(a,1))
    cout << "Something.a is 1" << endl;
if (!ANALYZE3(a,1))
    cout << "Somethinga is 0" << endl;
        return 1;
};
9
fyr

permet de considérer un exemple différent:

envisager

#define MYMACRO(x,y) x##y

sans le ##, clairement le préprocesseur ne peut pas voir x et y comme jetons séparés, peut-il?

Dans votre exemple,

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))

## n'est tout simplement pas nécessaire car vous ne faites aucun nouvel identifiant. En fait, des problèmes de compilateur "Erreur: coller". "Et" variable "ne donne pas de jeton de prétraitement valide"

3
Chethan

Ce n'est pas une réponse à votre question, juste un message CW avec quelques conseils pour vous aider à explorer vous-même le préprocesseur.

L'étape de prétraitement est réellement effectuée avant tout code réel compilé. En d'autres termes, lorsque le compilateur commence à construire votre code, non # Définir Des déclarations ou quelque chose comme ça est laissé.

Un bon moyen de comprendre ce que le préprocesseur fait à votre code est de faire la maintien de la sortie prétraitée et de le regarder.

Voici comment le faire pour Windows :

Créez un fichier simple appelé test.cpp et mettez-le dans un dossier, dites c:\temp. Le mien ressemble à ceci:

#define dog_suffix( variable_name ) variable_name##dog

int main()
{
  int dog_suffix( my_int ) = 0;
  char dog_suffix( my_char ) = 'a';

  return 0;
}

Pas très utile, mais simple. Ouvrez l'invite de commande Visual Studio, accédez au dossier et exécutez la commande suivante:

c:\temp>cl test.cpp /P

Donc, c'est le compilateur de votre fonctionnement (CL.EXE), avec votre fichier et l'option/P indique au compilateur de stocker la sortie prétraitée dans un fichier.

Maintenant, dans le dossier à côté de Test.CPP, vous trouverez Test.I, lequel pour moi ressemble à ceci:

#line 1 "test.cpp"


int main()
{
  int my_intdog = 0;
  char my_chardog = 'a';

  return 0;
}

Comme vous pouvez le voir, non # Définir à gauche, seul le code qu'il s'est étendu à.

3
sharkin

Selon Wikipedia

La concaténation de jetons, également appelée jeton collant, est l'un des caractéristiques les plus subtiles - et les plus faciles à abuser du préprocesseur de macro. Deux arguments peuvent être "collés" ensemble en utilisant l'opérateur de préprocesseur; Cela permet à deux jetons d'être concaténés dans le code prétraité. Cela peut être utilisé pour construire des macros élaborées qui agissent comme une version brute de modèles C++.

Chèque concaténation de jeton

3
Balanivash