2009-07-15 10 views
17

J'écris un tas de macros de préprocesseur liées, dont l'une génère des étiquettes sur lesquelles l'autre saute. Je les utilise de cette façon:Comment puis-je générer des valeurs uniques dans le préprocesseur C?

MAKE_FUNNY_JUMPING_LOOP(
    MAKE_LABEL(); 
    MAKE_LABEL(); 
) 

je besoin d'un moyen de générer des étiquettes uniques, un pour chaque appel MAKE_LABEL intérieure, avec le préprocesseur. J'ai essayé d'utiliser __LINE__, mais depuis que j'appelle MAKE_LABEL à l'intérieur d'une autre macro, ils ont tous la même ligne et les étiquettes entrent en collision.

Ce que je voudrais que cela est étendre à quelque chose comme:

MAKE_FUNNY_JUMPING_LOOP(
    my_cool_label_1: // from first inner macro 
    ... 
    my_cool_label_2: // from second inner macro 
    ... 
) 

Y at-il un moyen de générer hash ou entiers auto-incrémenter avec le préprocesseur?

+0

Quel compilateur utilisez-vous? –

+0

J'utilise ICC (le prototype STM 3.0, basé sur la v11, je pense). –

+0

Pour C++: http://stackoverflow.com/questions/9949532/generate-unique-numbers-at-compile-time?lq=1 –

Répondre

15

Comme d'autres ont noté, __COUNTER__ est le moyen facile, mais non standard de faire cela.

Si vous avez besoin d'une portabilité supplémentaire, ou pour d'autres astuces de préprocesseur, le Boost Preprocessor library (qui fonctionne pour C ainsi que C++) fonctionnera. Par exemple, le fichier d'en-tête suivant affichera une étiquette unique partout où elle est incluse.

#include <boost/preprocessor/arithmetic/inc.hpp> 
#include <boost/preprocessor/slot/slot.hpp> 

#if !defined(UNIQUE_LABEL) 
#define UNIQUE_LABEL 
#define BOOST_PP_VALUE 1 
#include BOOST_PP_ASSIGN_SLOT(1) 
#undef BOOST_PP_VALUE 
#else 
#define BOOST_PP_VALUE BOOST_PP_INC(BOOST_PP_SLOT(1)) 
#include BOOST_PP_ASSIGN_SLOT(1) 
#undef BOOST_PP_VALUE 
#endif 


BOOST_PP_CAT(my_cool_label_, BOOST_PP_SLOT(1)): 

Exemple:

int main(int argc, char *argv[]) { 
    #include "unique_label.h" 
    printf("%x\n", 1234); 
    #include "unique_label.h" 
    printf("%x\n", 1234); 
    #include "unique_label.h" 
    return 0; 
} 

prétraitements à

int main(int argc, char *argv[]) { 
    my_cool_label_1: 
    printf("%x\n", 1234); 
    my_cool_label_2: 
    printf("%x\n", 1234); 
    my_cool_label_3: 
    return 0; 
} 
+0

C'est une bonne réponse! Merci pour le pointeur; J'avoue que je trouve la documentation Boost.Preprocessor un peu dense. –

+0

Je le trouve aussi dense. J'ai l'habitude de bidouiller un peu avec la syntaxe pour trouver quelque chose qui fonctionne, mais c'est cool quand c'est le cas. –

+5

Boost.Preprocessor est une magie ridicule. –

7

Je ne peux pas penser à une façon de les générer automatiquement mais vous pouvez passer un paramètre à MAKE_LABEL:

#define MAKE_LABEL(n) my_cool_label_##n: 

Alors ...

MAKE_FUNNY_JUMPING_LOOP(
    MAKE_LABEL(0); 
    MAKE_LABEL(1); 
) 
+3

Bien sûr, il peut également inclure la macro __LINE__ dans le cadre de la définition MAKE_LABEL() vos autres macros peuvent encore l'utiliser (tant que vous n'utilisez pas ces macros plus d'une fois dans une autre macro ...) –

+0

Bon appel. Je le fais déjà pour la macro 'MAKE_FUNNY_JUMPING_LOOP', car il y en a relativement peu et ils sont faciles à décrire. Je me suis dit que si c'était juste un, je pourrais faire face à cela. Répandre la méthode manuelle à chaque macro comme ceci, cependant, est trop pour moi. –

-2

Il ne semble pas possible avec un préprocesseur standard, bien que vous puissiez vous tromper en mettant des paramètres dans MAKE_LABEL ou MAKE_FUNNY_JUMPING_LOOP, et utiliser le collage de jetons pour créer l'étiquette.

Rien ne vous empêche de créer votre propre script de prétraitement qui effectue l'incrément automatique pour vous. Cependant, ce ne sera pas un fichier C/C++ standard dans ce cas.

Une liste des commandes disponibles: http://www.cppreference.com/wiki/preprocessor/start

17

Si vous utilisez GCC ou MSVC, il y a __COUNTER__.

Autre que cela, vous pourriez faire vomir digne quelque chose, comme:

#ifndef USED_1 
#define USED_1 
1 
#else 
#ifndef USED_2 
#define USED_2 
2 
/* many many more */ 
#endif 
#endif 
+1

Belle trouvaille. Apprendre quelque chose de nouveau chaque jour. –

+0

Aurait-il besoin de faire attention à ce qu'il ne soit utilisé dans aucune autre unité de compilation? Sinon, il se peut que des numéros soient sautés de façon inattendue. –

+4

@Jesse: Chaque unité de compilation obtient sa propre exécution de préprocesseur, donc __COUNTER__ doit toujours commencer à 0. Cependant, il est tout à fait possible que quelqu'un d'autre puisse utiliser __COUNTER__ dans la même unité de compilation, entraînant des trous dans la séquence. – derobert

0

Vous pouvez le faire:

#define MAKE_LABEL() \ 
do {     \ 
my_cool_label:  \ 
/* some stuff */; \ 
goto my_cool_label; \ 
/* other stuff */; \ 
} while (0) 

Cela permet de maintenir la portée de l'étiquette locale, ce qui permet un certain nombre de les dans la macro primaire.

Si vous souhaitez que les libellés soient accessibles plus globalement, il n'est pas clair comment votre macro référence ces libellés. Peux-tu expliquer?

15

J'utilise ceci:

#define MERGE_(a,b) a##b 
#define LABEL_(a) MERGE_(unique_name_, a) 
#define UNIQUE_NAME LABEL_(__LINE__) 

int main() 
{ 
    int UNIQUE_NAME = 1; 
    return 0; 
} 

... et obtenir ce qui suit:

int main() 
{ 
    int unique_name_8 = 1; 
    return 0; 
} 
+1

Et comment utilisez-vous réellement la variable? Tu ne peux pas. – this

+2

.... vous devez ajouter un nom non unique dans la macro pour accéder à ce nom unique, ce qui vous ramène à la case départ ... il semble que nous devenons trop fous avec le préprocesseur et commençons à penser aux façons dont il peut faire tout le travail pour nous, peu importe comment irréaliste .. sans parler de prendre du temps. (moi, je tape quelque chose d'unique et continue ... heh) mais je pense que la solution COUNTER pour MSVC/GCC est bonne, et si vous utilisez quelque chose d'autre, utilisez des trucs boost ... les deux premières réponses sont bonnes – osirisgothra

Questions connexes