Mes en-têtes de C ressemblent généralement au style suivant pour éviter toute inclusion: Cependant, dans son Notes sur la programmation en C , Rob Pike fait l'argument suivant sur les fichiers d'en-tête: P> Il y a une petite danse impliquant D'une part, Pike est le seul programmeur que j'admire réellement. D'autre part, mettre plusieurs Quelle est la meilleure façon de gérer le problème de l'inclusion multiple? P> p>
#Ifdef code> qui peut empêcher un fichier à lire deux fois, mais il est généralement fait mal dans la pratique - le
#Ifdef code> Le fichier lui-même, pas le fichier qui comprend. Le résultat est souvent des milliers de lignes de code inutile traversant l'analyseur lexical, qui est (dans de bons compilateurs) la phase la plus chère. P>
BlockQuote>
#ifdef code> s dans plusieurs fichiers source au lieu de mettre un
#Ifdef code> dans un seul fichier d'en-tête se sent inutilement maladroit. P>
7 Réponses :
Continuez à faire ce que vous faites - il est clair, moins de bug-suone et bien connu par les écrivains compilateurs, donc pas aussi inefficace que c'était peut-être une décennie ou deux. P>
Vous pouvez utiliser le #pragma non standard une fois code> - Si vous recherchez, il y a probablement au moins la valeur d'une bibliothèque d'inclure les gardes VS Pragma une fois la discussion, donc je ne vais pas recommander un sur l'autre. p>
Si GCC n'a pas sucé, cela détecterait l'idiome standard la première fois que l'en-tête était inclus, rappelez-vous du nom de fichier et ignorez toutes les demandes futures d'inclure ce nom de fichier. C'est purement un cas de paresse compilateur.
@R. Ça ne va toujours pas? J'aurais pensé que chaque compilateur majeur aurait cela il y a une décennie. Encore une fois, lisez un fichier deux fois n'est pas vraiment le porc de la ressource qu'il était en 89 lorsque le brochet écrit ces notes.
@R ..: il prétend le faire - GCC.GNU.ORG/ONLINDOCS /cppinternals/guard-macros.html . Cependant, il existe certaines restrictions qu'il utilise pour que l'optimisation soit valide, et il pourrait y avoir de faux négatifs lorsque les restrictions sont cassées, mais néanmoins, l'optimisation serait valable dans ce cas.
Le lire deux fois est une ressource porc. Juste un syscall ne prend plus de temps sur des machines modernes que d'analyser un petit fichier d'en-tête.
À mon avis, utilisez la méthode nécessitant moins de votre temps (ce qui signifie probablement mettre les #Ifdefs dans les fichiers d'en-tête). Je ne me dérange pas vraiment si le compilateur doit travailler plus fort si mon code résultant est plus propre. Si, peut-être, vous travaillez sur une base de code de ligne de plusieurs millions de lignes que vous avez constamment à reconstruire complètement, peut-être que les économies supplémentaires en valent la peine. Mais dans la plupart des cas, je soupçonne que le coût supplémentaire n'est généralement pas perceptible. P>
Je trouve cette réponse très utile. Thing drôle, c'est que je suis d'accord de manière plus ironique: si l'ordinateur peut faire mon travail, pourquoi je le ferai? ;)
La façon dont vous le faites actuellement est la manière commune. La méthode de Pike coupe un peu le temps de compilation, mais avec des compilateurs modernes, probablement pas beaucoup (lorsque le brochet a écrit ses notes, les compilateurs n'étaient pas optimisants), il clutter des modules et son bug-sujet. P>
Vous pouvez toujours réduire l'inclusion multi-inclusion en n'incluant pas les en-têtes d'en-têtes, mais les documenter à la place de "Inclure
Je vous recommande de les mettre dans le fichier source lui-même. Pas besoin de se plaindre de quelques milliers de mille lignes de code analysées inutiles avec des PC réels. P>
En outre, il est beaucoup plus de travail et source si vous vérifiez chaque en-tête de chaque fichier source qui inclut l'en-tête. P>
Et vous devriez gérer vos fichiers d'en-tête différents des en-têtes tiers et d'autres tiers. p>
Il a peut-être eu une dispute l'heure à laquelle il écrivait ceci. De nos jours, les compilateurs décents sont assez intelligents pour gérer ce puits. P>
@R., Hm, référence, non je n'ai pas. Je lis cela qu'il n'y a pas trop longtemps, cependant, cela inclut des gardes de GCC équivalent à #pragma une fois code>. Et BTW l'analyse lexicale de
#Ifdef code> pour voir d'abord dans quoi un fichier doit être analysé pour que réel ne doit pas être trop difficile, non? En tout cas, je n'ai jamais trouvé pour moi que Ceci i> était un goulot d'étranglement lors de la compilation du grand code. Je fais souvent une précompilation avec
-e code> pour voir ce que la phase de prétraitement produit et ce n'est pas mesurable par l'œil humain.
Je suis d'accord avec votre approche - comme d'autres ont commenté, sa maintenance plus claire, auto-documentante et inférieure. P>
Ma théorie sur la raison pour laquelle Rob Pike aurait peut-être suggéré son approche: il parle de c, pas C ++. p>
en C ++, si vous avez beaucoup de cours et que vous déclarez chacun dans son propre fichier d'en-tête, vous aurez alors beaucoup de fichiers d'en-tête. C ne fournit pas vraiment ce genre de structure à grain fin (je ne me souviens pas de voir de nombreux fichiers d'en-tête C mono-STRUCT), et .h / .c file-paires tendez em> pour être plus grand et contenir quelque chose comme un module ou un sous-système. Donc, moins de fichiers d'en-tête. Dans ce scénario, l'approche de Rob Pike pourrait fonctionner. Mais je ne le vois pas aussi approprié pour les programmes non triviaux C ++. P>
Pike a écrit d'autres à ce sujet dans https://talks.golang.org/2012/splash .article : p>
en 1984, une compilation de
PS.c code>, la source de la commande UNIX PS, était observé à
#include
code> 37 fois au moment où tous les Le prétraitement avait été fait. Même si le contenu est jeté 36 fois en le faisant, la plupart des implémentations C ouvriraient le fichier, lire et et analysez-le tous les 37 fois. Sans une grande intelligence, en fait, que le comportement est requis par la sémantique macro-complexe potentiellement complexe de la C préprocesseur. P> blockQuote> compilateurs sont devenus assez intelligents depuis: https://gcc.gnu.org /onlinedocs/cppinternals/guard-macros.html , c'est donc moins un problème maintenant. P>
La construction d'un seul binaire C ++ sur Google peut ouvrir et lire Des centaines d'en-tête individuels dossent des dizaines de milliers de fois. Dans 2007, construisez des ingénieurs sur Google instrumenté la compilation d'un Major Google Binary. Le fichier contenait environ deux mille fichiers qui, Si simplement concaténée ensemble, totalisait 4,2 mégaoctets. Par le temps Le #inclut avait été élargi, plus de 8 gigaoctets étaient livrés à l'entrée du compilateur, une explosion de 2000 octets pour chaque C ++ octet source. p>
comme un autre point de données, le système de construction de Google de 2003 a été déplacé d'un MakeFile unique à une conception par répertoire avec plus de mieux dépendances explicites. Une réduction binaire typique d'environ 40% en taille de fichier, juste d'avoir plus de dépendances précises enregistrées. Même si, le Propriétés de C ++ (ou C pour cette affaire) le rend impraticable de vérifier ces dépendances automatiquement, et aujourd'hui, nous n'avons toujours pas de Compréhension précise des exigences de dépendance du grand Google Binaires C ++. P> blockQuote>
Le point sur les tailles binaires est toujours pertinent. Les compilateurs (lieurs) sont assez conservateurs en ce qui concerne les symboles non utilisés. Comment supprimer des symboles C / C ++ inutilisés avec GCC et LD? P >
Dans le plan 9, les fichiers d'en-tête ont été interdits de contenir plus loin
#include code> clauses; Tout
#includes code> devait être dans le fichier C de niveau supérieur. Cela nécessitait une certaine discipline, bien sûr - le programmeur était nécessaire pour énumérer les dépendances nécessaires exactement une fois, dans le ordre correct-mais la documentation a aidé et en pratique cela fonctionnait très bien. p> blockQuote>
C'est une solution possible. Une autre possibilité est d'avoir un outil qui gère le Comprend pour vous, par exemple Makepss . P>
Il y a aussi des constructions d'unité, parfois appelée unité de compilation unique, une unité de compilation unique. Il existe des outils pour aider à gérer cela, comme https://github.com/sakra/cotire p >
L'utilisation d'un système de construction optimise pour que la vitesse de compilation incrémentielle peut également être avantageuse. Je parle du bazel de Google et de la même chose. Il ne vous protège pas d'une modification d'un fichier d'en-tête inclus dans un grand nombre d'autres fichiers, cependant. P>
Enfin, il existe une proposition de modules C ++ dans les travaux, superbes https://groups.google.com/a/isocpp.org/forum/#!forum/modules . Voir aussi Qu'est-ce que les modules C ++? P>
Comme on le voit dans le premier paragraphe de votre deuxième citation, en 2007, le problème est toujours important. Si seulement des points de repère plus récents sont disponibles.
Selon Web. archive.org/web/20021218032155/http://gcc.gnu.org/... , GCC pourrait faire l'optimisation multiple-include déjà à la fin de 2002. Je me demande quel compilateur a été google utilisant en 2007.