Un programmeur que je respecte a dit que dans le code C, #if
et #ifdef
devraient être évités à tout prix, sauf éventuellement dans les fichiers d'en-tête. Pourquoi serait-il considéré comme une mauvaise pratique de programmation d'utiliser #ifdef
dans un fichier .c?
Réponses
Trop de publicités?Dur à maintenir. Mieux utiliser les interfaces abstraites de plate-forme spécifique code d'abuser de compilation conditionnelle par la dispersion #ifdef
s tous les dessus de votre mise en œuvre.
E. g.
void foo() {
#ifdef WIN32
// do Windows stuff
#else
// do Posix stuff
#endif
// do general stuff
}
N'est pas agréable. Au lieu de fichiers foo_w32.c
et foo_psx.c
avec
foo_w32.c:
void foo() {
// windows implementation
}
foo_psx.c:
void foo() {
// posix implementation
}
foo.h:
void foo(); // common interface
Alors 2 makefiles1: Makefile.win
, Makefile.psx
, avec chaque compilation du appropriés .c
fichier et en le liant à l'encontre de la droite de l'objet.
Modification mineure:
Si foo()
's la mise en œuvre dépend d'un code qui s'affiche sur toutes les plateformes, E. g. common_stuff()
2, appelez simplement que dans votre foo()
des implémentations.
E. g.
commun.h:
void common_stuff(); // May be implemented in common.c, or maybe has multiple
// implementations in common_{A, B, ...} for platforms
// { A, B, ... }. Irrelevant.
foo_{w32, psx}.c:
void foo() { // Win32/Posix implementation
// Stuff
...
if (bar) {
common_stuff();
}
}
Alors que vous pourriez être la répétition d'un appel de fonction à l' common_stuff()
, vous ne pouvez pas paramétrer votre définition de l' foo()
par la plate-forme, sauf s'il suit un modèle très particulier. Généralement, les différences de plate-forme nécessite complètement différentes implémentations et ne suivez pas ces modèles.
- Les Makefiles sont utilisés ici illustratively. Votre système de construction peut pas utiliser
make
à tous, comme si vous utilisez Visual Studio, CMake, Scons, etc. - Même si
common_stuff()
a plusieurs implémentations, variant par la plate-forme.
(Un peu hors de la question posée)
J'ai vu une astuce une fois, ce qui suggère l'utilisation de l' #if(n)def/#endif
blocs pour une utilisation dans le débogage/isoler le code, au lieu de commenter.
Il a été suggéré pour aider à éviter des situations dans lesquelles la section commentaire déjà eu des commentaires de documentation et d'une solution comme les suivants devraient être mis en œuvre:
/* <-- begin debug cmnt if (condition) /* comment */
/* <-- restart debug cmnt {
....
}
*/ <-- end debug cmnt
Au lieu de cela, ce serait:
#ifdef IS_DEBUGGED_SECTION_X
if (condition) /* comment */
{
....
}
#endif
Semblait être une bonne idée pour moi. Souhaite que je pourrais rappeler la source afin que je puisse le lien :(
Mon interprétation de cette règle: la logique de votre programme (algorithmique) ne doit pas être influencée par les définitions du préprocesseur. Le fonctionnement de votre code doit toujours être concis. Toute autre forme de logique (plate-forme, débogage) doit pouvoir être résumée dans les fichiers d'en-tête.
Il s'agit plus d'une ligne directrice que d'une règle stricte, à mon humble avis. Mais je suis d'accord que les solutions basées sur la syntaxe c sont préférées à la magie du préprocesseur.
Un objectif raisonnable, mais pas si grand comme une règle stricte
Les conseils d'essayer et de garder préprocesseur des instructions conditionnelles dans les fichiers d'en-tête est bonne, car elle vous permet de sélectionner des interfaces conditionnellement mais pas la litière avec le code source de confusion et laid préprocesseur logique.
Cependant, il y a des tas et des tas de code qui ressemble à l'exemple ci-dessous, et je ne pense pas qu'il y est une meilleure alternative. Je pense que vous avez cité une indication raisonnable, mais pas un grand or-tablette-commandement.
#if defined(SOME_IOCTL)
case SOME_IOCTL:
...
#endif
#if defined(SOME_OTHER_IOCTL)
case SOME_OTHER_IOCTL:
...
#endif
#if defined(YET_ANOTHER_IOCTL)
case YET_ANOTHER_IOCTL:
...
#endif