86 votes

## dans les macros

Comme mentionné dans plusieurs de mes questions précédentes, je travaille par le biais de K&R, et je suis actuellement dans le préprocesseur. L'une des choses les plus intéressantes -- quelque chose que je ne connaissait pas avant de toutes de mes tentatives antérieures d'apprendre le C -- est l' ## préprocesseur de l'opérateur. Selon K&R:

Le préprocesseur opérateur ## fournit un moyen pour concaténer réel arguments au cours de macro d'extension. Si un paramètre dans le texte de remplacement est adjacent à un ##, le paramètre est remplacé par l'argument réel, de la ## et blanc autour de l'espace sont supprimé, et le résultat est à nouveau analysés. Par exemple, la macro paste concatène ses deux arguments:

#define paste(front, back) front ## back

donc, paste(name, 1) crée le jeton name1.

Comment et pourquoi quelqu'un de l'utiliser dans le monde réel? Ce sont des exemples concrets de son utilisation, et il y a des pièges à considérer?

Mise à JOUR:

J'ai accepté Brian R. Bondy réponse, mais tiens à remercier tous ceux qui ont contribué -- et nous espérons pouvoir contribuer davantage à l'avenir. Tout ici a été instructif, et m'a aidé à comprendre ce concept un peu mieux. Je doute que je vais l'utiliser n'importe quand bientôt, mais rien que de savoir non seulement ce qu'il fait, mais qu'elle est utilisée, et largement, apparemment -- est énormément utile.

Merci à tous!

49voto

Michael Burr Points 181287

Une chose à prendre en compte lorsque vous utilisez la pâte de jeton ( ' ## ) ou stringizing (' # ) opérateurs prétraiter est que vous devez utiliser un niveau supplémentaire d'indirection pour qu'ils fonctionnent correctement dans tous les cas.

Si vous ne le faites pas et que les éléments transmis à l'opérateur de collage de jetons sont eux-mêmes des macros, vous obtiendrez des résultats qui ne sont probablement pas ce que vous voulez:

 #include <stdio.h>

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x

#define SOME_MACRO function_name

int main() 
{
    printf( "buggy results:\n");
    printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));

    printf( "\n" "desired result:\n");
    printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}
 

Le résultat:

 buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)

desired result:
function_name21
 

46voto

Brian R. Bondy Points 141769

CrashRpt: à l'Aide de ## pour convertir macro multi-chaînes d'octets unicode

Une intéressante utilisation dans CrashRpt (rapport d'incident de la bibliothèque) est la suivante:

#define WIDEN2(x) L ## x 
#define WIDEN(x) WIDEN2(x)
//Note you need a WIDEN2 so that __DATE__ will evaluate first.

Ici, ils veulent utiliser un 2 octet string au lieu d'un 1 octet par caractère de la chaîne.
C'est probablement ce qui ressemble, il est vraiment inutile, mais ils le font pour une bonne raison.

 std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);

Ils l'utilisent avec une autre macro qui renvoie une chaîne de caractères avec la date et l'heure.

Mettre L à côté d'un __ DATE __ vous donnera une erreur de compilation.

--

Windows: à l'Aide de ## pour générique unicode ou multi chaînes d'octets

Windows utilise quelque chose comme ce qui suit:

#ifdef  _UNICODE
    #define _T(x)      L ## x
#else
    #define _T(x) x
#endif

Et _T est utilisé partout dans le code

--

Les différentes bibliothèques, l'aide pour l'assainissement de l'accesseur et de modification des noms:

J'ai aussi vu qu'il est utilisé dans le code de définir des accesseurs et modificateurs:

#define MYLIB_ACCESSOR(name) (Get##name)
#define MYLIB_MODIFIER(name) (Set##name)

De même, vous pouvez utiliser cette même méthode pour tous les autres types de intelligent de création de nom.

--

Les différentes bibliothèques, en l'utilisant pour faire plusieurs déclarations de variables à la fois:

#define CREATE_3_VARS(name) name##1, name##2, name##3
int CREATE_3_VARS(myInts);
myInts1 = 13;
myInts2 = 19;
myInts3 = 77;

14voto

bk1e Points 13737

Voici une chasse aux sorcières que j'ai rencontré lors de la mise à niveau vers une nouvelle version d'un compilateur:

Inutile de le jeton-coller de l'opérateur (##) est non portable et peut générer des indésirables, des espaces, des avertissements ou des erreurs.

Lorsque le résultat de l'jeton-coller de l'opérateur n'est pas valide préprocesseur jeton, le jeton-coller de l'opérateur est inutile et potentiellement dangereux.

Par exemple, on peut essayer de construire des littéraux de chaîne au moment de la compilation en utilisant le jeton-coller de l'opérateur:

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a##+##b)
#define NS(a, b) STRINGIFY(a##::##b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

Sur certains compilateurs, ce sera de sortie le résultat attendu:

1+2 std::vector

Sur d'autres compilateurs, cela comprend les espaces indésirables:

1 + 2 std :: vector

Assez moderne versions de GCC (>=3.3) échoue à compiler ce code:

foo.cpp:16:1: pasting "1" and "+" does not give a valid preprocessing token
foo.cpp:16:1: pasting "+" and "2" does not give a valid preprocessing token
foo.cpp:16:1: pasting "std" and "::" does not give a valid preprocessing token
foo.cpp:16:1: pasting "::" and "vector" does not give a valid preprocessing token

La solution consiste à omettre le jeton-coller de l'opérateur lors de la concaténation de préprocesseur jetons pour le C/C++ opérateurs:

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a+b)
#define NS(a, b) STRINGIFY(a::b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

La GCC RPC documentation chapitre sur la concaténation a plus d'information utile sur le jeton-coller de l'opérateur.

6voto

Vebjorn Ljosa Points 6215

Ceci est utile dans toutes sortes de situations afin de ne pas répéter inutilement. L'exemple suivant est un exemple de l'Emacs de code source. Nous tenons à charger un certain nombre de fonctions à partir d'une bibliothèque. La fonction "foo" doit être attribué fn_foo, et ainsi de suite. Nous définissons la macro suivante:

#define LOAD_IMGLIB_FN(lib,func) {                                      \
    fn_##func = (void *) GetProcAddress (lib, #func);                   \
    if (!fn_##func) return 0;                                           \
  }

On peut alors l'utiliser:

LOAD_IMGLIB_FN (library, XpmFreeAttributes);
LOAD_IMGLIB_FN (library, XpmCreateImageFromBuffer);
LOAD_IMGLIB_FN (library, XpmReadFileToImage);
LOAD_IMGLIB_FN (library, XImageFree);

L'avantage est de ne pas avoir à écrire les deux fn_XpmFreeAttributes et "XpmFreeAttributes" (et le risque de faute de l'un d'eux).

3voto

qrdl Points 17813

Vous pouvez utiliser le collage de jetons lorsque vous devez concaténer le paramètre macro avec quelque chose d'autre.

Il peut être utilisé pour les modèles:

 #define LINKED_LIST(A) struct list##_##A {\
A value; \
struct list##_##A *next; \
};
 

Dans ce cas, LINKED_LIST (int) vous donnerait

 struct list_int {
int value;
struct list_int *next;
};
 

De même, vous pouvez écrire un modèle de fonction pour parcourir la liste.

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