Comment afficher la valeur d'un #define au moment de la compilation?

123

J'essaie de comprendre quelle version de Boost mon code pense qu'il utilise. Je veux faire quelque chose comme ça:

#error BOOST_VERSION

mais le préprocesseur ne développe pas BOOST_VERSION.

Je sais que je pourrais l'imprimer au moment de l'exécution à partir du programme, et je sais que je pourrais regarder la sortie du préprocesseur pour trouver la réponse. J'ai le sentiment qu'avoir un moyen de faire cela pendant la compilation pourrait être utile.

Jim Hunziker
la source
7
Pour les futurs visiteurs ... Chris Barry fournit la solution généralisée à la fin (sans substance spécifique à Boost).
jww

Réponses:

117

Je sais que c'est longtemps après la requête d'origine, mais cela peut encore être utile.

Cela peut être fait dans GCC en utilisant l'opérateur stringify "#", mais cela nécessite deux étapes.

#define XSTR(x) STR(x)
#define STR(x) #x

La valeur d'une macro peut alors être affichée avec:

#pragma message "The value of ABC: " XSTR(ABC)

Voir: 3.4 Stringification dans la documentation en ligne de gcc.

Comment ça fonctionne:

Le préprocesseur comprend les chaînes entre guillemets et les gère différemment du texte normal. La concaténation de chaînes est un exemple de ce traitement spécial. Le pragma de message nécessite un argument qui est une chaîne entre guillemets. Lorsqu'il y a plus d'un composant à l'argument, ils doivent tous être des chaînes pour que la concaténation de chaînes puisse être appliquée. Le préprocesseur ne peut jamais supposer qu'une chaîne sans guillemets doit être traitée comme si elle était entre guillemets. Si c'est le cas, alors:

#define ABC 123
int n = ABC;

ne compilerait pas.

Considérez maintenant:

#define ABC abc
#pragma message "The value of ABC is: " ABC

ce qui équivaut à

#pragma message "The value of ABC is: " abc

Cela provoque un avertissement du préprocesseur car abc (sans guillemets) ne peut pas être concaténé avec la chaîne précédente.

Considérons maintenant le préprocesseur stringize (qui était autrefois appelé stringification, les liens dans la documentation ont été modifiés pour refléter la terminologie révisée. (Les deux termes, d'ailleurs, sont tout aussi détestables. Le terme correct est, bien sûr, stringifaction. Soyez prêt à mettre à jour vos liens.)) opérateur. Cela n'agit que sur les arguments d'une macro et remplace l'argument non développé par l'argument entre guillemets. Donc:

#define STR(x) #x
char *s1 = "abc";
char *s2 = STR(abc);

attribuera des valeurs identiques à s1 et s2. Si vous exécutez gcc -E, vous pouvez le voir dans la sortie. Peut-être que STR serait mieux nommé quelque chose comme ENQUOTE.

Cela résout le problème de placer des guillemets autour d'un élément sans guillemets, le problème est maintenant que, si l'argument est une macro, la macro ne sera pas développée. C'est pourquoi la deuxième macro est nécessaire. XSTR développe son argument, puis appelle STR pour mettre la valeur développée entre guillemets.

Chris Barry
la source
3
Je suis curieux de savoir pourquoi cela nécessite deux étapes
Vincent Fourmond
4
@VincentFourmond Sans l'étape XSTR, la macro n'est pas développée. Donc, si vous avez #define ABC 42 \ n STR (ABC), vous obtiendrez "ABC". Voir gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c
Cela fonctionne également très bien avec Xcode 8, par exemple en remplaçant ABC par __IPHONE_9_3.
funroll
La terminologie GCC semble avoir changé, et avec elle l'URL, qui est maintenant https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry
119

BOOST_PP_STRINGIZE semble une excellente solution pour C ++, mais pas pour C.

Voici ma solution pour GNU CPP:

/* Some test definition here */
#define DEFINED_BUT_NO_VALUE
#define DEFINED_INT 3
#define DEFINED_STR "ABC"

/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "="  VALUE(var)

/* Some example here */
#pragma message(VAR_NAME_VALUE(NOT_DEFINED))
#pragma message(VAR_NAME_VALUE(DEFINED_BUT_NO_VALUE))
#pragma message(VAR_NAME_VALUE(DEFINED_INT))
#pragma message(VAR_NAME_VALUE(DEFINED_STR))

Les définitions ci-dessus donnent:

test.c:10:9: note: #pragma message: NOT_DEFINED=NOT_DEFINED
test.c:11:9: note: #pragma message: DEFINED_BUT_NO_VALUE=
test.c:12:9: note: #pragma message: DEFINED_INT=3
test.c:13:9: note: #pragma message: DEFINED_STR="ABC"

Pour les variables "défini comme entier" , "défini comme chaîne" et "défini mais pas de valeur" , ils fonctionnent très bien. Uniquement pour la variable "non définie" , ils affichent exactement le même nom que le nom de la variable d'origine. Vous devez vous y habituer - ou peut-être que quelqu'un peut fournir une meilleure solution.

Jackie Yeh
la source
excellent! Des expériences avec ARM RVCT? il semble ne pas avoir de fonction de "stringification" comme GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…
xdan
2
Excellente solution. Cependant, si je souhaite afficher la taille d'une valeur calculée au moment de la compilation, par exemple la taille d'une structure complexe, est-ce possible? La méthode suggérée dans cette réponse semble générer DEFINED_INT=(sizeof(MY_STRUCT)), sans que l' sizeofopérateur ne soit évalué.
Carl
(Ajout de commentaire: pas inattendu, car c'est le compilateur plutôt que le pré-processeur qui évaluera sizeof, cependant, toujours curieux de savoir s'il existe un moyen intelligent d'y parvenir.)
Carl
@xdan Bonne solution, malheureusement, elle ne convient pas à des choses comme#define masks {0xff, 0xaf, 0x0f}
Simon Bagley
59

Si vous utilisez Visual C ++, vous pouvez utiliser #pragma message:

#include <boost/preprocessor/stringize.hpp>
#pragma message("BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION))

Edit: Merci à LB pour le lien

Apparemment, l'équivalent GCC est (non testé):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)
Bojan Resnik
la source
5
Cela s'appelle des pragmas de diagnostic, gcc.gnu.org/onlinedocs/gcc/…
LB40
4
Ce serait bien si vous incluiez la définitionBOOST_PP_STRINGIZE qui est belle et courte et copiable / collable.
Timmmm
Fonctionne bien sous gcc :)
Thomas Legris
14

Autant que je sache, «#error» n'imprimera que des chaînes, en fait, vous n'avez même pas besoin d'utiliser des guillemets .

Avez-vous essayé d'écrire divers codes intentionnellement incorrects en utilisant "BOOST_VERSION"? Peut-être quelque chose comme "bla [BOOST_VERSION] = foo;" vous dira quelque chose comme "la chaîne littérale 1.2.1 ne peut pas être utilisée comme adresse de tableau". Ce ne sera pas un joli message d'erreur, mais au moins, il vous montrera la valeur pertinente. Vous pouvez jouer jusqu'à ce que vous trouviez une erreur de compilation qui vous indique la valeur.

KeyserSoze
la source
Cela n'a pas fonctionné, puisque BOOST_VERSION est un entier, mais j'ai pu le voir avec cette instruction: std::vector<BOOST_VERSION>;dans gcc 4.4.1. Merci!
Jim Hunziker
Notez qu'avec Visual C ++, vous devrez utiliser la réponse de Bojan Resnik.
Raphaël Saint-Pierre
J'ai essayé de faire fonctionner cela, mais le message d'erreur que GCC m'a donné n'était malheureusement pas descriptif. Mais +1 pour l'avoir mentionné.
Chris Lutz
14

Sans boost:

  1. redéfinissez la même macro et le compilateur HIMSELF donnera un avertissement.

  2. De l'avertissement, vous pouvez voir l'emplacement de la définition précédente.

  3. vi fichier de définition précédente.

ambarish@axiom:~/cpp$ g++ shiftOper.cpp
shiftOper.cpp:7:1: warning: "LINUX_VERSION_CODE" redefined
shiftOper.cpp:6:1: warning: this is the location of the previous definition

#define LINUX_VERSION_CODE 265216
#define LINUX_VERSION_CODE 666

int main ()
{

}
Ambarish Kumar Shivam
la source
Celui-ci est plus simple et plus simple.
Tmx
1
lui - même : les compilateurs n'ont pas de sexe
Sky
Cela ne fonctionne pas avec les macros prédéfinies, telles que __cplusplus.
ManuelAtWork
10

Dans Microsoft C / C ++, vous pouvez utiliser la fonction intégrée _CRT_STRINGIZE()pour imprimer des constantes. Beaucoup de mes stdafx.hfichiers contiennent une combinaison de ceux-ci:

#pragma message("_MSC_VER      is " _CRT_STRINGIZE(_MSC_VER))
#pragma message("_MFC_VER      is " _CRT_STRINGIZE(_MFC_VER))
#pragma message("_ATL_VER      is " _CRT_STRINGIZE(_ATL_VER))
#pragma message("WINVER        is " _CRT_STRINGIZE(WINVER))
#pragma message("_WIN32_WINNT  is " _CRT_STRINGIZE(_WIN32_WINNT))
#pragma message("_WIN32_IE     is " _CRT_STRINGIZE(_WIN32_IE))
#pragma message("NTDDI_VERSION is " _CRT_STRINGIZE(NTDDI_VERSION)) 

et produit quelque chose comme ceci:

_MSC_VER      is 1915
_MFC_VER      is 0x0E00
_ATL_VER      is 0x0E00
WINVER        is 0x0600
_WIN32_WINNT  is 0x0600
_WIN32_IE     is 0x0700
NTDDI_VERSION is 0x06000000
UweBaemayr
la source
5
#define a <::BOOST_VERSION>
#include a
MSVC2015 : erreur fatale C1083: impossible d'ouvrir le fichier d'inclusion: ':: 106200': aucun fichier ou répertoire de ce type

Fonctionne même si preprocess to fileest activé, même si des jetons non valides sont présents:

#define a <::'*/`#>
#include a
MSVC2015 : erreur fatale C1083: Impossible d'ouvrir le fichier d'inclusion: '::' * / `# ': aucun fichier ou répertoire de ce type
GCC4.x : avertissement: caractère de terminaison manquant [-Winvalid-pp-token]
#define a <:: '* / `#>
Andry
la source
Le mien dit juste Build error: #include expects "FILENAME" or <FILENAME>. Soupir.
endolith
@endolith quel compilateur et quelle version?
Andry
DP8051 Keil 9.51 :)
endolith
@endolith Il semble que ce compilateur soit très limité en prétraitement: keil.com/support/man/docs/c51/c51_pp_directives.htm Mais, de mon côté, cela fonctionne presque comme prévu, je viens de supprimer certains caractères invalides comme ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry
Merci, cela m'a sauvé parce que le message pragma n'était pas implémenté dans le compilateur que j'utilisais.
CodeMonkey
3

Vous pouvez également prétraiter le fichier source et voir ce que la valeur du préprocesseur évalue.

fbrereto
la source
2

Cherchez-vous

#if BOOST_VERSION != "1.2"
#error "Bad version"
#endif

Pas génial si BOOST_VERSION est une chaîne, comme je l'ai supposé, mais il peut aussi y avoir des entiers individuels définis pour les numéros majeur, mineur et de révision.

user47559
la source
Je pense que le soumissionnaire ne veut pas (juste) appliquer une valeur particulière, il veut voir quelle est la valeur actuelle.
KeyserSoze
C'est la seule chose qui fonctionne pour moi. Je peux changer l' #if VARIABLE == 123instruction à la volée et la coloration syntaxique me dit si c'est la valeur que je pense ou non ...
endolith
2

Regarder la sortie du préprocesseur est la chose la plus proche de la réponse que vous demandez.

Je sais que vous avez exclu cela (et d'autres moyens), mais je ne sais pas pourquoi. Vous avez un problème assez spécifique à résoudre, mais vous n'avez pas expliqué pourquoi aucune des méthodes "normales" ne fonctionne bien pour vous.

dwc
la source
C'est probablement la bonne réponse au problème général.
jww
1

Vous pouvez écrire un programme qui s'imprime BOOST_VERSION, le compile et l'exécute dans le cadre de votre système de construction. Sinon, je pense que vous n'avez pas de chance.

Chris Lutz
la source
Dans le cas d'une version logicielle définie dans un en-tête, vous êtes probablement en sécurité (et c'est une bonne réponse). Mais en tant que solution générale, un inconvénient possible serait que votre application de test et votre application réelle aient la même valeur que #define - en fonction de leurs chemins d'inclusion, d'autres #defines pouvant être utilisées pour définir la valeur de celle-ci , le CFLAGS passé au compilateur, etc.
KeyserSoze
Imprimez-le à partir de votre programme réel. S'il est graphique, placez-le dans la boîte de dialogue "à propos". Si vous utilisez la ligne de commande, faites-en une option (partie de --version, peut-être). S'il s'agit d'un démon, écrivez-le dans un fichier journal. S'il est intégré, trouvez un autre moyen.
divegeek
@swillden - L'OP le voulait au moment de la compilation, pas au moment de l'exécution.
Chris Lutz
Cela a également tendance à casser les versions basées sur des compilateurs croisés
Craig Ringer
1

BOOST_VERSION est défini dans le fichier d'en-tête boost version.hpp.

David Harris
la source
1

Jetez également un œil à la documentation Boost, concernant la façon dont vous utilisez la macro:

En référence à BOOST_VERSION, à partir de http://www.boost.org/doc/libs/1_37_0/libs/config/doc/html/boost_config/boost_macro_reference.html#boost_config.boost_macro_reference.boost_helper_macros :

Décrit le numéro de version boost au format XXYYZZ tel que: (BOOST_VERSION % 100)est la version sous-mineure, est la version mineure et est la version majeure.((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)

bn.
la source
0

Au lieu de #error, essayez de redéfinir la macro juste avant qu'elle ne soit utilisée. La compilation échouera et le compilateur fournira la valeur actuelle qu'il pense s'appliquer à la macro.

#define BOOST_VERSION bla

tecMav
la source