Disons que nous avons une base de code qui est utilisée pour de nombreux clients différents, et que nous avons du code qui n'est pertinent que pour les clients de type X. Est-il préférable d'utiliser des directives de préprocesseur pour inclure ce code uniquement dans le client de type X, ou utiliser des instructions if? Pour être plus clair:
// some code
#if TYPE_X_COSTUMER = 1
// do some things
#endif
// rest of the code
ou
if(TYPE_X_COSTUMER) {
// do some things
}
Les arguments auxquels je peux penser sont:
- La directive du préprocesseur entraîne une empreinte de code plus petite et moins de branches (sur les compilateurs non optimisants)
- Si les instructions résultent avec du code qui se compile toujours, par exemple si quelqu'un fait une erreur qui endommagera le code non pertinent pour le projet sur lequel il travaille, l'erreur apparaîtra toujours et il ne corrompra pas la base de code. Sinon, il ne sera pas au courant de la corruption.
- On m'a toujours dit de préférer l'utilisation du processeur à l'utilisation du préprocesseur (si c'est un argument du tout ...)
Qu'est-ce qui est préférable - quand on parle d'une base de code pour de nombreux clients différents?
TYPE_X_CUSTOMER
toujours une macro de préprocesseur?Réponses:
Je pense qu'il y a un avantage à utiliser un #define que vous n'avez pas mentionné, et c'est le fait que vous pouvez définir la valeur sur la ligne de commande (ainsi, définissez-la à partir de votre script de construction en une étape).
En dehors de cela, il est généralement préférable d'éviter les macros. Ils ne respectent aucune portée et cela peut poser des problèmes. Seuls les compilateurs très stupides ne peuvent pas optimiser une condition basée sur une constante de temps de compilation. Je ne sais pas si c'est une préoccupation pour votre produit (par exemple, il pourrait être important de garder le code petit sur les plates-formes embarquées).
la source
Comme pour de nombreuses questions, la réponse à cette question est que cela dépend . Au lieu de dire ce qui est mieux, j'ai plutôt donné des exemples et des objectifs où l'un est meilleur que l'autre.
Le préprocesseur et la constante ont leurs propres emplacements d'utilisation appropriés.
En cas de pré-processeur, le code est supprimé avant l'heure de compilation. Par conséquent, il est mieux adapté aux situations où le code ne devrait pas être compilé . Cela peut affecter la structure du module, les dépendances et permettre de sélectionner les meilleurs segments de code pour les aspects de performances. Dans les cas suivants, il faut diviser le code en utilisant uniquement le préprocesseur.
Code multiplateforme:
par exemple, lorsque le code est compilé sur différentes plates-formes, lorsque le code dépend de numéros de version spécifiques du système d'exploitation (ou même de la version du compilateur - bien que cela soit très rare). Par exemple, lorsque vous traitez avec des contreparties de code petit-endien big-endien - elles doivent être séparées avec des préprocesseurs plutôt qu'avec des constantes. Ou si vous compilez du code pour Windows ainsi que Linux et certains appels système sont très différents.
Patchs expérimentaux:
Un autre cas où cela fait est d'avoir un code expérimental justifié qui est risqué ou certains modules majeurs qui doivent être omis qui auront une liaison significative ou une différence de performance. La raison pour laquelle on voudrait désactiver le code via un préprocesseur plutôt que de se cacher sous if () est parce que nous ne pouvons pas être sûrs des bogues introduits par cet ensemble de modifications spécifique et que nous fonctionnons sous une base expérimentale. S'il échoue, nous ne devons rien faire d'autre que désactiver ce code en production que réécrire. Quelque temps, il est idéal d'utiliser
#if 0
pour commenter le code entier.Gérer les dépendances: une
autre raison pour laquelle vous voudrez peut-être générer Par exemple, si vous ne voulez pas prendre en charge les images JPEG, vous pouvez vous débarrasser de la compilation de ce module / stub et éventuellement la bibliothèque ne sera pas (statiquement ou dynamiquement) liée à cela module. Parfois, les packages s'exécutent
./configure
pour identifier la disponibilité d'une telle dépendance et si les bibliothèques ne sont pas présentes (ou l'utilisateur ne veut pas l'activer), cette fonctionnalité est désactivée automatiquement sans lien avec cette bibliothèque. Ici, il est toujours avantageux que ces directives soient générées automatiquement.Licence:
un exemple très intéressant de directive de préprocesseur est ffmpeg . Il possède des codecs qui peuvent potentiellement enfreindre les brevets par son utilisation. Si vous téléchargez la source et compilez pour l'installer, il vous demande si vous voulez ou éloignez ce genre de choses. Garder les codes cachés sous certaines conditions si les conditions peuvent encore vous amener en cour!
Copier-coller de code:
macros Aka. Ce n'est pas un conseil pour une utilisation excessive des macros - juste que les macros ont un moyen beaucoup plus puissant d'appliquer l' équivalent copié- collé. Mais utilisez-le avec grand soin; et utilisez-le si vous savez ce que vous faites. Les constantes, bien sûr, ne peuvent pas faire cela. Mais on peut aussi utiliser des
inline
fonctions si c'est facile à faire.Alors, quand utilisez-vous des constantes?
Presque partout ailleurs.
Flux de code plus net:
en général, lorsque vous utilisez des constantes, il est presque impossible de les distinguer des variables régulières et, par conséquent, il s'agit d'un code mieux lisible. Si vous écrivez une routine de 75 lignes, ayez 3 ou 4 lignes toutes les 10 lignes avec #ifdef est TRÈS incapable de lire . Probablement donné une constante primaire régie par #ifdef, et utilisez-la dans un flux naturel partout.
Code bien indenté: Toutes les directives de préprocesseur, ne fonctionnent jamais bien avec du code autrement bien indenté . Même si votre compilateur autorise l'indentation de #def, le préprocesseur pré-ANSI C ne permettait pas d'espace entre le début d'une ligne et le caractère "#"; le "#" de tête devait toujours être placé dans la première colonne.
Configuration:
Une autre raison pour laquelle les constantes / ou les variables ont un sens est qu'elles peuvent facilement évoluer en étant liées à des globales ou à l'avenir peuvent être étendues pour être dérivées de fichiers de configuration.
Une dernière chose:
ne jamais utiliser de directives de préprocesseur
#ifdef
pour#endif
traverser la portée ou{ ... }
. c'est-à-dire début#ifdef
ou fin de#endif
différents côtés de{ ... }
. C'est extrêmement mauvais; cela peut être déroutant, parfois dangereux.Bien sûr, ce n'est pas une liste exhaustive, mais vous montre une nette différence, où la méthode est plus apte à utiliser. Il ne s'agit pas vraiment de savoir ce qui est le mieux , c'est toujours plus dont on est plus naturel à utiliser dans un contexte donné.
la source
Vos clients ont-ils accès à votre code? Si oui, le préprocesseur pourrait être une meilleure option. Nous avons quelque chose de similaire et nous utilisons des indicateurs de temps de compilation pour divers clients (ou des fonctionnalités spécifiques au client). Ensuite, un script pourrait extraire le code spécifique au client et nous expédions ce code. Les clients ne connaissent pas les autres clients ou d'autres fonctionnalités spécifiques au client.
la source
Quelques points supplémentaires dignes de mention:
Le compilateur peut traiter certains types d'expressions constantes que le préprocesseur ne peut pas. Par exemple, le préprocesseur n'aura généralement aucun moyen d'évaluer
sizeof()
les types non primitifs. Cela peut forcer l'utilisation deif()
dans certains cas.Le compilateur ignorera la plupart des problèmes de syntaxe dans le code qui est ignoré
#if
(certains problèmes liés au préprocesseur peuvent toujours causer des problèmes) mais insistera sur l'exactitude syntaxique du code ignoréif()
. Ainsi, si le code qui est ignoré pour certaines versions mais pas pour d'autres devient invalide à la suite de modifications ailleurs dans le fichier (par exemple, les identifiants renommés), il squawk généralement sur toutes les versions s'il est désactivé avecif()
mais pas s'il est désactivé par#if
. Selon la raison pour laquelle le code est ignoré, cela peut ou non être une bonne chose.Certains compilateurs omettent le code inaccessible tandis que d'autres non; cependant, les déclarations de durée de stockage statique dans le code inaccessible, y compris les littéraux de chaîne, peuvent allouer de l'espace même si aucun code ne peut jamais utiliser l'espace ainsi alloué.
Les macros peuvent utiliser des
if()
tests en leur sein, mais il n'y a hélas aucun mécanisme permettant au préprocesseur d'exécuter une logique conditionnelle au sein d'une macro [notez que pour une utilisation dans des macros, l'? :
opérateur peut souvent être meilleur queif
, mais les mêmes principes s'appliquent].La
#if
directive peut être utilisée pour contrôler les macros définies.Je pense que
if()
c'est souvent plus propre pour la plupart des cas, sauf ceux qui impliquent de prendre des décisions en fonction de ce que le compilateur utilise ou des macros à définir; certains des problèmes ci-dessus peuvent nécessiter l'utilisation#if
, cependant, même dans les cas oùif
cela semble plus propre.la source
Pour faire court: les craintes concernant les directives de préprocesseur sont essentiellement 2, de multiples inclusions et peut-être du code moins lisible et une logique plus cryptique.
Si votre projet est petit avec presque aucun grand plan pour l'avenir, je pense que les deux options offrent le même rapport entre les avantages et les inconvénients, mais si votre projet va être énorme, envisagez autre chose comme l'écriture d'un fichier d'en-tête séparé si vous voulez toujours utiliser des directives de préprocesseur, mais gardez à l'esprit que ce type d'approche rend généralement le code moins lisible et assurez-vous que le nom de ces constantes signifie quelque chose pour la logique métier du programme lui-même.
la source