web-dev-qa-db-fra.com

Est-il possible de vérifier qu'une macro est définie et qu'elle est égale à une certaine valeur en même temps

J'utilise régulièrement des macros de préprocesseur de type objet comme indicateurs booléens dansCcode pour activer ou désactiver des sections de code.

Par exemple

#define DEBUG_PRINT 1

Et puis l'utiliser comme

#if(DEBUG_PRINT == 1)
    printf("%s", "Testing");
#endif

Cependant, cela pose un problème si le fichier d’en-tête contenant le #define est oublié d’être inclus dans le code source. Étant donné que la macro n'est pas déclarée, le préprocesseur la traite comme si elle était égale à 0 et l'instruction #if ne s'exécutait jamais.

Lorsque le fichier d'en-tête est oublié pour être inclus, un comportement non attendu et indiscipliné peut se produire.  

Idéalement, j'aimerais pouvoir vérifier à la fois qu'une macro est définie et qu'elle correspond à une certaine valeur, sur une ligne. S'il n'est pas défini, le préprocesseur renvoie une erreur (ou un avertissement).

Je cherche quelque chose comme:

#if-def-and-true-else-throw-error(DEBUG_PRINT)
    ...
#endif

C'est comme une combinaison de #ifdef et #if, et si elle n'existe pas, utilise #error.

J'ai exploré quelques pistes, cependant, les directives de préprocesseur ne peuvent pas être utilisées dans un bloc #define, et autant que je sache, il n'y a pas d'option de préprocesseur pour générer des erreurs/avertissements si une macro n'est pas définie lorsqu'elle est utilisée dans un #if déclaration.

13
gbmhunter

Cela peut ne pas fonctionner dans le cas général (je ne pense pas qu'il existe une solution générale à ce que vous demandez), mais pour votre exemple spécifique, vous pourriez envisager de changer cette séquence de code:

#if(DEBUG_PRINT == 1)
    printf("%s", "Testing");
#endif

à:

if (DEBUG_PRINT == 1) {
    printf("%s", "Testing");
}

Il n'y a plus de commentaires et échouera à la compilation si DEBUG_PRINT n'est pas défini ou s'il est défini comme quelque chose qui ne peut être comparé à 1.

7
Michael Burr

pour autant que je sache, il n'y a pas d'option de préprocesseur pour générer des erreurs/avertissements si une macro n'est pas définie lorsqu'elle est utilisée dans une instruction #if.

Cela ne peut pas être une erreur car le standard C spécifie que le comportement est légal. À partir de la section 6.10.1/3 de la norme ISO C99:

Une fois que tous les remplacements dus à l’expansion des macros et à l’opérateur defined unaire Ont été effectués, tous les identificateurs restants sont remplacés par le numéro de pp 0....

Comme Jim Balter le note dans le commentaire ci-dessous, certains compilateurs (tels que gcc) peuvent émettre des avertissements à ce sujet. Cependant, puisque substituer 0 à des jetons de préprocesseur non reconnus est légal (et souvent souhaitable), je m'attendrais à ce que l'activation de tels avertissements génère beaucoup de bruit.

Il n'y a aucun moyen de faire exactement ce que vous voulez. Si vous souhaitez générer un échec de compilation si la macro n'est pas définie, vous devrez le faire explicitement.

#if !defined DEBUG_PRINT
#error DEBUG_PRINT is not defined.
#endif

pour chaque fichier source concerné. Vous pouvez également convertir votre macro en une macro de type fonction et éviter d'utiliser #if. Par exemple, vous pouvez définir une macro DEBUG_PRINT qui se développe en un appel printf pour les versions de débogage, mais qui ne comporte plus rien pour les versions non-déboguées. Tout fichier qui omet d'inclure l'en-tête définissant la macro échouera lors de la compilation.

9
jamesdlin

Plutôt que d'utiliser DEBUG_PRINT directement dans vos fichiers source, insérez ceci dans le fichier d'en-tête:

#if !defined(DEBUG_PRINT)
    #error DEBUG_PRINT is not defined
#endif

#if DEBUG_PRINT
    #define PrintDebug([args]) [definition]
#else
    #define PrintDebug
#endif

Tout fichier source qui utilise PrintDebug mais n'inclut pas le fichier d'en-tête échouera lors de la compilation.

Si vous avez besoin que du code autre que les appels à PrintDebug soit compilé sur la base de DEBUG_PRINT, utilisez la suggestion de Michael Burr consistant à utiliser plain if plutôt que #if (oui, l'optimiseur ne générera pas de code dans un test de constante constant).

Edit: Et vous pouvez généraliser PrintDebug ci-dessus pour inclure ou exclure du code arbitraire tant que vous ne disposez pas de virgules ressemblant à des arguments de macro:

#if !defined(IF_DEBUG)
    #error IF_DEBUG is not defined
#endif

#if IF_DEBUG
    #define IfDebug(code) code
#else
    #define IfDebug(code)
#endif

Ensuite, vous pouvez écrire des choses comme

IfDebug(int count1;)  // IfDebug(int count1, count2;) won't work
IfDebug(int count2;)
...
IfDebug(count1++; count2++;)
8
Jim Balter

Oui, vous pouvez vérifier les deux:

#if defined DEBUG  &&  DEBUG == 1
#  define D(...) printf(__VA_ARGS__)
#else
#  define D(...)
#endif

Dans cet exemple, même lorsque #define DEBUG 0 est différent de 1, rien ne sera imprimé.

Vous pouvez même faire ceci:

#if defined DEBUG  &&  DEBUG
#  define D(...) printf(__VA_ARGS__)
#else
#  define D(...)
#endif

Ici, si vous #define DEBUG 0 puis D(1,2,3), rien ne sera imprimé

DOC

2
Eugen Konkov

Créez simplement une macro DEBUG_PRINT qui effectue l'impression réelle:

#define DEBUG_PRINT(n, str)    \
                               \
  if(n == 1)                   \
  {                            \
    printf("%s", str);         \
  }                            \
  else if(n == 2)              \
  {                            \
    do_something_else();       \
  }                            \
                               \
#endif


#include <stdio.h>

int main()
{
  DEBUG_PRINT(1, "testing");
}

Si la macro n'est pas définie, vous obtiendrez une erreur du compilateur car le symbole n'est pas reconnu.

0
Lundin
#if 0 // 0/1
#define DEBUG_PRINT printf("%s", "Testing")
#else
#define DEBUG_PRINT printf("%s")
#endif

Donc, quand "si 0" ça ne fait rien et quand "si 1" ça va exécuter la macro définie.

0
Sam Keith