#ifdef vs #if - quelle est la méthode la plus efficace / la plus sûre pour activer / désactiver la compilation de sections de code particulières?

StackOverflow https://stackoverflow.com/questions/135069

Question

Cela peut être une question de style, mais notre équipe de développement est un peu divisée et je me demandais si quelqu'un d'autre avait des idées à ce sujet ...

En gros, certaines instructions d’impression de débogage sont désactivées au cours du développement normal. Personnellement, je préfère faire ce qui suit:

//---- SomeSourceFile.cpp ----

#define DEBUG_ENABLED (0)

...

SomeFunction()
{
    int someVariable = 5;

#if(DEBUG_ENABLED)
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

Certains membres de l'équipe préfèrent cependant ce qui suit:

// #define DEBUG_ENABLED

...

SomeFunction()
{
    int someVariable = 5;

#ifdef DEBUG_ENABLED
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

... laquelle de ces méthodes vous convient le mieux et pourquoi? Mon sentiment est que le premier est plus sûr car il y a toujours quelque chose de défini et il n'y a aucun danger qu'il puisse détruire d'autres définis ailleurs.

Était-ce utile?

La solution

Ma première réaction a été #ifdef , bien sûr , mais je pense que #if présente en fait des avantages importants pour cela - voici pourquoi:

Tout d'abord, vous pouvez utiliser DEBUG_ENABLED dans les tests compilés du préprocesseur et . Exemple - Souvent, je souhaite des délais plus longs lorsque le débogage est activé. Par conséquent, en utilisant #if , je peux écrire ceci

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... au lieu de ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif

Deuxièmement, vous êtes mieux placé si vous souhaitez migrer d'un #define à une constante globale. Les #define sont généralement mal vus par la plupart des programmeurs C ++.

Et, troisièmement, vous dites que votre équipe est divisée. Je suppose que cela signifie que différents membres ont déjà adopté différentes approches et que vous devez normaliser. Déterminer que #if est le choix préféré signifie que le code utilisant #ifdef sera compilé et exécuté même lorsque DEBUG_ENABLED est faux. Et il est beaucoup beaucoup plus facile de localiser et de supprimer les résultats de débogage qui sont produits alors que cela ne devrait pas être que l'inverse.

Oh, et un point de lisibilité mineur. Vous devriez pouvoir utiliser true / false plutôt que 0/1 dans votre #define , et comme la valeur est un seul jeton lexical, c'est la seule fois où vous n'avez pas besoin de parenthèses.

#define DEBUG_ENABLED true

au lieu de

#define DEBUG_ENABLED (1)

Autres conseils

Ils sont tous les deux hideux. Au lieu de cela, procédez comme suit:

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif

Ensuite, chaque fois que vous avez besoin de code de débogage, insérez-le dans D (); . Et votre programme n'est pas pollué par des labyrinthes hideux de #ifdef .

#ifdef vérifie simplement si un jeton est défini, étant donné

#define FOO 0

puis

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"

Nous avons eu le même problème dans plusieurs fichiers et il y a toujours un problème: les gens oublient d'inclure un "indicateur de fonctionnalités". fichier (avec une base de code de > 41 000 fichiers, il est facile à faire).

Si vous aviez feature.h:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H

Mais vous avez oublié d'inclure le fichier d'en-tête dans le fichier.cpp:

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif

Ensuite, vous avez un problème, le compilateur interprète COOL_FEATURE comme étant non défini comme un "faux". dans ce cas et ne parvient pas à inclure le code. Oui, gcc prend en charge un indicateur qui provoque une erreur pour les macros non définies ... mais la plupart du code tiers définit ou ne définit pas les fonctionnalités afin que ce ne soit pas portable.

Nous avons adopté un moyen portable de corriger ce cas ainsi que de tester l'état d'une fonctionnalité: les macros de fonction.

si vous avez modifié la fonctionnalité ci-dessus.h en:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H

Mais vous avez encore une fois oublié d'inclure le fichier d'en-tête dans fichier.cpp:

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif

Le préprocesseur se serait égaré à cause de l'utilisation d'une macro de fonction non définie.

Aux fins de la compilation conditionnelle, #if et #ifdef sont presque identiques, mais pas tout à fait. Si votre compilation conditionnelle dépend de deux symboles, alors #ifdef ne fonctionnera pas aussi bien. Par exemple, supposons que vous ayez deux symboles de compilation conditionnels, PRO_VERSION et TRIAL_VERSION, vous pourriez avoir quelque chose comme ceci:

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif

L'utilisation de #ifdef devient beaucoup plus compliquée, en particulier pour faire fonctionner la partie #else.

Je travaille sur du code qui utilise intensivement la compilation conditionnelle et nous avons un mélange de #if & amp; #ifdef. Nous avons tendance à utiliser # ifdef / # ifndef pour le cas simple et #if chaque fois que deux ou plusieurs symboles sont en cours d'évaluation.

Je pense que c'est entièrement une question de style. Ni l'un ni l'autre n'a vraiment d'avantage évident sur l'autre.

La cohérence est plus importante que l’un ou l’autre choix. Nous vous recommandons donc de vous réunir avec votre équipe, de choisir un style et de vous y tenir.

Je préfère moi-même:

#if defined(DEBUG_ENABLED)

Comme il est plus facile de créer du code qui recherche la condition opposée, il est beaucoup plus facile de la repérer:

#if !defined(DEBUG_ENABLED)

vs.

#ifndef(DEBUG_ENABLED)

C'est une question de style. Mais je recommande une méthode plus concise:

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);

Vous faites cela une fois, puis utilisez toujours debug_print () pour imprimer ou ne rien faire. (Oui, cela compilera dans les deux cas.) Ainsi, votre code ne sera pas corrompu par les directives du préprocesseur.

Si vous obtenez l'avertissement "l'expression n'a pas d'effet" et vous voulez vous en débarrasser, voici une alternative:

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);

#if vous permet de le définir sur 0 pour désactiver la fonctionnalité, tout en détectant que le commutateur est présent.
Personnellement je toujours #define DEBUG 1 afin que je puisse l'attraper avec un #if ou #ifdef

#if et #define MY_MACRO (0)

Utiliser #if signifie que vous avez créé un " définir " macro, c’est-à-dire quelque chose qui sera recherché dans le code à remplacer par "(0)". C’est le "macro enfer" Je déteste voir en C ++, car il pollue le code avec des modifications potentielles.

Par exemple:

#define MY_MACRO (0)

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

donne l'erreur suivante sur g ++:

main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|

Une seule une erreur.

Ce qui signifie que votre macro a bien interagi avec votre code C ++: L'appel à la fonction a abouti. Dans ce cas simple, c'est amusant. Mais ma propre expérience avec les macros jouant en silence avec mon code n’est pas pleine de joie et de réalisation, alors ...

#ifdef et #define MY_MACRO

Utiliser #ifdef signifie que vous "définissez". quelque chose. Pas que vous lui donniez une valeur. Il est toujours polluant, mais au moins, il sera "remplacé par rien" et ne sera pas considéré par le code C ++ comme une instruction de code lagitimate. Le même code ci-dessus, avec un simple définir, il:

#define MY_MACRO

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

Donne les avertissements suivants:

main.cpp||In function ‘int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function ‘int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|

Alors ...

Conclusion

Je préfère vivre sans macros dans mon code, mais pour plusieurs raisons (définir des gardes d'en-tête ou des macros de débogage), je ne peux pas.

Mais au moins, j'aime les rendre le moins interactif possible avec mon code C ++ légitime. Ce qui signifie utiliser #define sans valeur, en utilisant #ifdef et #ifndef (ou même #if défini comme suggéré par Jim Buck), et surtout, en leur donnant des noms si longs et si étranges que personne dans son bon esprit n'utilisera "par hasard", et cela n'affectera en aucun cas le code C ++ légitime.

Post Scriptum

Maintenant, alors que je relis mon message, je me demande si je ne devrais pas essayer de trouver une valeur qui ne sera jamais un correct C ++ à ajouter à ma définition. Quelque chose comme

#define MY_MACRO @@@@@@@@@@@@@@@@@@

qui pourrait être utilisé avec #ifdef et #ifndef, mais pas laisser le code compiler s'il est utilisé dans une fonction ... J'ai essayé avec succès sur g ++, et cela donnait l'erreur:

main.cpp|410|error: stray ‘@’ in program|

Intéressant. : -)

Le premier me semble plus clair. Cela semble plus naturel d'en faire un drapeau par rapport à défini / non défini.

Les deux sont exactement équivalents. En utilisation idiomatique, #ifdef est utilisé uniquement pour vérifier la définition (et ce que j'utiliserais dans votre exemple), alors que #if est utilisé dans des expressions plus complexes, telles que #if define (A) & amp; & amp; ! defini (B).

Un petit OT, mais activer / désactiver la journalisation avec le préprocesseur est nettement sous-optimal en C ++. Il existe de bons outils de journalisation, tels que les log4cxx d'Apache, qui sont open source et ne sont pas restreints. comment vous distribuez votre application. Ils vous permettent également de modifier les niveaux de journalisation sans recompilation, d’avoir une surcharge très faible si vous désactivez la journalisation et de vous permettre de désactiver complètement la journalisation en production.

Ce n’est pas une question de style. Aussi la question est malheureusement fausse. Vous ne pouvez pas comparer ces directives de préprocesseur dans le sens de mieux ou plus sûr.

#ifdef macro

signifie "si la macro est définie" ou "si la macro existe". La valeur de la macro n'a pas d'importance ici. Ce peut être n'importe quoi.

#if macro

si toujours comparer à une valeur. Dans l'exemple ci-dessus, il s'agit de la comparaison implicite standard:

#if macro !=0

exemple d'utilisation de #if

#if CFLAG_EDITION == 0
    return EDITION_FREE;
#elif CFLAG_EDITION == 1
    return EDITION_BASIC;
#else
    return EDITION_PRO;
#endif

vous pouvez maintenant mettre la définition de CFLAG_EDITION soit dans votre code

#define CFLAG_EDITION 1 

ou vous pouvez définir la macro comme indicateur de compilateur. voir aussi .

J'avais l'habitude d'utiliser #ifdef , mais lorsque je suis passé à Doxygen pour la documentation, j'ai constaté que les macros commentées ne pouvaient pas être documentées (ou que Doxygen produisait un avertissement). Cela signifie que je ne peux pas documenter les macros de sélecteurs de fonctions qui ne sont pas actuellement activés.

Bien qu'il soit possible de définir les macros uniquement pour Doxygen, cela signifie que les macros des parties non actives du code seront également documentées. Personnellement, je veux montrer les commutateurs de fonctions et sinon ne documenter que ce qui est actuellement sélectionné. De plus, cela rend le code assez confus si de nombreuses macros doivent être définies uniquement lorsque Doxygen traite le fichier.

Par conséquent, dans ce cas, il est préférable de toujours définir les macros et d'utiliser #if .

J'ai toujours utilisé #ifdef et les drapeaux du compilateur pour le définir ...

Vous pouvez également déclarer une constante globale et utiliser C ++ si, au lieu du préprocesseur #if. Le compilateur doit optimiser les branches inutilisées et votre code sera plus propre.

= X & oi = book_result & resnum = 1 & ct = résultat # PPA69, M1 "rel =" aucune erreur "> C ++ Gotchas par Stephen C. Dewhurst explique qu'il est préférable d'utiliser # if.

Il existe une différence dans le cas d'une manière différente de spécifier une définition conditionnelle au pilote:

diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )

sortie:

344c344
< #define A 
---
> #define A 1

Cela signifie que -DA est synonyme de -DA = 1 et que la valeur est omise, cela peut entraîner des problèmes en cas de #if Un usage.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top