web-dev-qa-db-fra.com

Comment tester si le symbole du préprocesseur est # define'd mais n'a pas de valeur?

À l'aide des directives de préprocesseur C++, est-il possible de vérifier si un symbole de préprocesseur a été défini mais n'a pas de valeur? Quelque chose comme ca:

#define MYVARIABLE
#if !defined(MYVARIABLE) || #MYVARIABLE == ""
... blablabla ...
#endif

EDIT: La raison pour laquelle je le fais est parce que le projet sur lequel je travaille est supposé prendre une chaîne de l'environnement via /DMYSTR=$(MYENVSTR), et cette chaîne peut être vide. Je veux m'assurer que le projet ne se compilera pas si l'utilisateur a oublié de définir cette chaîne.

27
galets

Soma macro magic:

#define DO_EXPAND(VAL)  VAL ## 1
#define EXPAND(VAL)     DO_EXPAND(VAL)

#if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1)

Only here if MYVARIABLE is not defined
OR MYVARIABLE is the empty string

#endif

Notez que si vous définissez MYVARIABLE sur la ligne de commande, la valeur par défaut est 1.

g++ -DMYVARIABLE <file>

Ici la valeur de MYVARIABLE est 1

g++ -DMYVARIABLE= <file>

Ici la valeur de MYVARIABLE est la chaîne vide

Le problème de citation résolu:

#define DO_QUOTE(X)        #X
#define QUOTE(X)           DO_QUOTE(X)

#define MY_QUOTED_VAR      QUOTE(MYVARIABLE)

std::string x = MY_QUOTED_VAR;
std::string p = QUOTE(MYVARIABLE);
34
Martin York

Je veux m'assurer que le projet ne se compilera pas si l'utilisateur a oublié de définir cette chaîne.

Bien que je vérifie cela dans une étape de construction précédente, vous pouvez le faire au moment de la compilation. Utiliser Boost par souci de brièveté:

#define A "a"
#define B
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(A)) > 1); // succeeds
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(B)) > 1); // fails
10
Georg Fritzsche

Je n'ai pas vu cette solution au problème mais je suis surpris qu'il ne soit pas couramment utilisé . Cela semble fonctionner dans Xcode Objc. Distinguer entre "défini sans valeur" et "défini 0"

#define TRACE
#if defined(TRACE) && (7-TRACE-7 == 14)
#error TRACE is defined with no value
#endif
9
Ian Brackenbury

Vous pouvez utiliser la macro BOOST_PP_IS_EMPTY comme suit:

#include <boost/preprocessor/facilities/is_empty.hpp>

#define MYVARIABLE
#if !defined(MYVARIABLE) || !BOOST_PP_IS_EMPTY(MYVARIABLE)
    // ... blablabla ...
#endif

Cela a fait le tour pour moi. J'ajouterai que cette macro n'est pas documentée, donc utilisez-la avec prudence.

Source: préprocesseur-manquant-IS-EMPTY-documentation

3
mister why

La réponse de Mehrad doit être élargie pour que cela fonctionne. Aussi son commentaire

/ * MYVARI (A) BLE n'est pas défini ici * /

n'est pas correcte; pour tester une variable non définie, il existe le test simple #ifndef MYVARIABLE.

Après un tel test, son expression mène à une solution correcte de la question initiale. J'ai testé le fonctionnement de ce code pour les valeurs non définies de la macro MYVARIABLE: non définies, définies mais vides et non vides:

#ifndef MYVARIABLE
    /* MYVARIABLE is undefined here */
#Elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
    /* MYVARIABLE is defined with no value here */
#else
    /* MYVARIABLE is defined here */
#endif

L'instruction #Elif~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1 fonctionne comme suit:

  • Lorsque MYVARIABLE est défini mais vide, il se développe en ~(~+0) == 0 && ~(~+1) == 1, ce qui donne 0==0 && 1==1 (la double négation ~~ étant un opérateur d'identité). 
  • Lorsque MYVARIABLE est défini sur une valeur numérique, disons n, il se développe en ~(~n+0)==0 && ~(~n+1)==1. Sur le côté gauche de &&, l'expression ~(~n+0)==0 est évaluée à n==0. Mais avec n==0, le côté droit est évalué à ~(~0+1)==1, avec ~ 0 étant -1 à ~(-1+1)==1, puis ~0==1 et enfin -1==1, ce qui est évidemment faux.
  • Lorsque MYVARIABLE est défini avec une valeur non numérique, le précompilateur réduit tous les symboles inconnus à 0 et nous obtenons le cas précédent avec n == 0 une fois de plus.

Mon code de test complet (enregistrer sous le fichier test.c):

#include <stdio.h>

int main() {
    printf("MYVARIABLE is "
#ifndef MYVARIABLE
     "undefined"
#Elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
     "defined without a value"
#else 
     "defined with this value : %i", MYVARIABLE
#endif
    );
    printf("\n");
}

Avec le préprocesseur GNU cpp , vous pouvez expérimenter pour voir quel code est produit:

# undefined
cpp test.c
#defined without a value
cpp -DMYVARIABLE= test.c
#defined wit an implicit value 1
cpp -DMYVARIABLE test.c
#defined wit an explicit value 1
cpp -DMYVARIABLE=1 test.c
#defined wit an explicit value a
cpp -DMYVARIABLE=a test.c

ou sortie de compilation et d'exécution (sous un certain linux)

$ gcc -o test test.c ; ./test
MYVARIABLE is undefined
$ gcc -DMYVARIABLE= -o test test.c ; ./test
MYVARIABLE is defined without a value
$ gcc -DMYVARIABLE -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=1 -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=a -o test test.c ; ./test
test.c: In function ‘main’:
<command-line>:0:12: error: ‘a’ undeclared (first use in this function)
...

Lors de la dernière exécution, où MYVARIABLE est défini comme "a", l'erreur n'est pas une erreur dans la définition de macro. la macro est correctement amenée au dernier cas, "défini avec cette valeur ...". Mais cette valeur étant 'a', et 'a' n'étant pas défini dans le code, le compilateur ou le cours doit le signaler. 

De cette manière, le dernier cas est un très bon exemple de la raison pour laquelle l'intention de la question initiale est très dangereuse: via une macro, l'utilisateur peut introduire n'importe quelle séquence de lignes de programme dans le code à compiler. Vérifier que ce code n’est pas introduit nécessite beaucoup plus de vérification de la macro sur les valeurs valides. Un script complet est probablement nécessaire, au lieu de laisser cette tâche au prétraitement. Et dans ce cas, à quoi sert-il également de le vérifier dans le prétraitement?

2
db-inf

Je ne pense pas que cela puisse être fait. Cela étant dit, je n'en vois pas la nécessité. Lorsque vous créez un symbole #define du préprocesseur, vous devez définir une convention selon laquelle vous le définissez comme 1 ou 0 pour l'utiliser dans #if ou vous le laissez vide.

2
Reinderien

Vous ne pouvez pas, puisque le préprocesseur peut uniquement rechercher une valeur numérique. Votre comparaison de chaîne n'est pas couverte par la syntaxe du préprocesseur.

2
harper

#if MYVARIABLE==0 Ma réponse doit être au moins de 30 caractères, cela devrait donc le faire!

0
Kris Morness

Pour les macros entières uniquement ...

Vous pouvez utiliser un hack sans macros supplémentaires:

#if ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
/* MYVARIBLE is undefined here */
#endif
0
Mehrdad