44 votes

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

En utilisant les directives du préprocesseur C++, est-il possible de tester si un symbole du préprocesseur a été défini mais n'a pas de valeur ? Quelque chose comme ça :

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

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

46voto

Loki Astari Points 116129

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 la chaîne vide :

g++ -DMYVARIABLE= <file>

Le problème de la citation est 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);

14voto

Ian Brackenbury Points 149

Je n'ai pas vu cette solution au problème mais je suis surpris qu'elle ne soit pas d'usage courant. . Elle semble fonctionner dans Xcode Objc. Distinguez entre "defined with no value" et "defined set 0".

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

12voto

Georg Fritzsche Points 59185

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

Bien que je vérifierais ceci dans une étape de construction précédente, vous pouvez le faire au moment de la compilation. J'utilise Boost pour des raisons 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

8voto

db-inf Points 79

La réponse de Mehrad doit être élargie pour qu'elle fonctionne. De même, son commentaire

/* MYVARI(A)BLE est indéfini ici */

n'est pas correct ; pour tester une variable indéfinie, il y a le simple test #ifndef MYVARIABLE .

Après ce test, cependant, son expression conduit à une solution correcte de la question initiale. J'ai testé que ce code fonctionne, pour des valeurs indéfinies, définies mais vides, et non vides de la macro MYVARIABLE :

#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

El #elif déclaration ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1 fonctionne comme suit :

  • Lorsque MYVARIABLE est défini mais vide, il s'étend à ~(~+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 comme 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 évalue à n==0 . Mais avec n==0 le côté droit s'évalue à ~(~0+1)==1 avec ~0 étant -1 à ~(-1+1)==1 entonces ~0==1 et enfin -1==1 ce qui est évidemment faux.
  • Lorsque MYVARIABLE est défini à une valeur non numérique, le précompilateur réduit tous les symboles inconnus à 0, et nous obtenons à nouveau le cas précédent avec n==0.

Mon code de test complet (enregistré dans 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 le résultat de la compilation et de l'exécution (sous certains 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)
...

Dans la dernière exécution, où MYVARIABLE est défini comme 'a', l'erreur n'est pas une erreur dans la définition de la macro ; la macro est correctement conduite 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 façon, le dernier cas est un très bon exemple de la raison pour laquelle l'intention de la question originale 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 qu'un tel code n'est pas introduit, nécessite beaucoup plus de vérification de la macro sur les valeurs valides. Probablement un script complet est nécessaire, au lieu de laisser cette tâche au prétraitement. Et dans ce cas, quelle est l'utilité de le vérifier aussi dans le prétraitement ?

3voto

mister why Points 829

Vous pouvez utiliser le BOOST_PP_IS_EMPTY macro comme ça :

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

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

Cela a fait l'affaire pour moi. Je dois ajouter que cette macro n'est pas documentée, donc à utiliser avec précaution.

Source : preprocessor-missing-IS-EMPTY-documentation

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