Comment dois-je détecter inutiles #inclure des fichiers dans un grand projet C++?
Je suis en train de travailler sur un grand projet C++ dans Visual Studio 2008, et il y a beaucoup de fichiers inutiles #include
directives. Parfois, le #include
s sont juste des artefacts et tout se compile bien avec eux supprimés, et, dans d'autres cas, des classes de l'avant déclarées et le #include pourrait être déplacé à l' .cpp
fichier. Existe-il des bons outils pour la détection de ces deux cas?
Vous devez vous connecter pour publier un commentaire.
Bien qu'il ne révèle pas inutile de l'inclure des fichiers, Visual studio est un paramètre
/showIncludes
(clic droit sur un.cpp
fichier,Properties->C/C++->Advanced
) qui va générer une arborescence de tous les fichiers inclus au moment de la compilation. Cela peut aider à identifier les fichiers qui ne devraient pas être inclus.Vous pouvez aussi jeter un oeil à la pimpl idiome laisser vous en tirer avec le moins d'en-tête de fichier de dépendances pour le rendre plus facile à voir les trucs que vous pouvez supprimer.
PC Peluches fonctionne très bien pour cela, et il trouve toutes sortes d'autres dingo problèmes pour vous aussi. Il a des options de ligne de commande qui peut être utilisé pour créer des Outils Externes dans Visual Studio, mais j'ai trouvé que le Visual Peluches addin est plus facile de travailler avec. Même la version gratuite de Visual Peluches aide. Mais donnez-PC-Lint un coup de feu. La configuration de sorte qu'il ne vous donne pas trop de mises en garde prend un peu de temps, mais vous serez étonné de ce qu'il se transforme.
Il y a une nouvelle Clang outil inclure ce que vous utilisez, qui vise à le faire.
!!AVERTISSEMENT!! Je travaille sur un commercial outil d'analyse statique (pas de PC à Charpie). !!AVERTISSEMENT!!
Il ya plusieurs problèmes avec un simple non l'analyse de l'approche:
1) Surcharge Ensembles:
Il est possible qu'une fonction surchargée a des déclarations qui viennent de différents fichiers. Il pourrait être que la suppression d'un fichier d'en-tête des résultats dans une autre surcharge d'être choisie plutôt qu'une erreur de compilation! Le résultat sera, en silence, un changement sémantique qui peuvent être très difficiles à repérer par la suite.
2) Modèle de spécialisations:
Similaire à la surcharge exemple, si vous avez partielle ou explicite spécialisations pour un modèle que vous désirez qu'elles soient toutes visibles lorsque le modèle est utilisé. Il se pourrait que les spécialisations pour le premier modèle dans les différents fichiers d'en-tête. Suppression de l'en-tête avec la spécialisation ne sera pas provoquer une erreur de compilation, mais peut conduire à un comportement indéterminé si cette spécialisation ont été sélectionnés. (Voir: La visibilité du modèle de la spécialisation de la fonction C++ )
Comme l'a souligné 'msalters', l'exécution d'une analyse complète du code permet également pour l'analyse de la classe d'utilisation. En cochant la façon dont une classe est utilisée si un chemin d'accès spécifique de fichiers, il est possible que la définition de la classe (et donc de l'ensemble de ses dependnecies) peut être enlevé complètement ou au moins est passée à un niveau plus proche de la source principale dans l'include arbre.
Je ne sais pas du tout de tels outils, et j'ai pensé à écrire une dans le passé, mais il s'avère que c'est un problème difficile à résoudre.
Dire que votre fichier source contient une.h et b.h; un.h contient
#define USE_FEATURE_X
et b.h utilise#ifdef USE_FEATURE_X
. Si#include "a.h"
est commenté, votre fichier peut toujours compiler, mais ne peut pas faire ce que vous attendez. La détection de cette par programmation est non-trivial.Quel que soit l'outil n'est ce besoin de connaître votre environnement de construction ainsi. Si un.h ressemble:
Puis
USE_FEATURE_X
n'est définie que siWINNT
est définie, de sorte que l'outil aurait besoin de savoir quelles directives sont générés par le compilateur lui-même ainsi que ceux qui sont spécifiés dans la commande de la compilation plutôt que dans un fichier d'en-tête.Comme Timmermans, je ne suis pas familier avec les outils pour cela. Mais j'ai connu des programmeurs, qui a écrit un Perl (ou Python) script pour essayer de commenter chaque ligne une à une heure et ensuite compiler chaque fichier.
Il semble que maintenant Eric Raymond a un outil pour cela.
Google cpplint.py a un "comprennent ce que vous utilisez" règle (parmi beaucoup d'autres), mais aussi loin que je peux dire, pas de "comprennent seulement ce que vous utilisez." Même ainsi, il peut être utile.
Si vous êtes intéressé par ce sujet en général, vous voudrez peut-être consulter Lakos' À Grande Échelle En C++ De Logiciels De Conception. C'est un peu daté, mais va dans beaucoup de la conception physique" des questions comme trouver le minimum absolu d'en-têtes qui doivent être inclus. Je n'ai pas vraiment vu ce genre de chose discuté nulle part ailleurs.
Donner Inclure Le Gestionnaire De un essai. Il s'intègre facilement dans Visual Studio et visualise vos chemins d'accès qui vous aide à trouver des trucs inutiles.
En interne, il utilise Graphviz, mais il ya beaucoup plus de fonctionnalités intéressantes. Et même si c'est un produit commercial, il a un prix très bas.
Vous pouvez construire un include graphique à l'aide de C/C++ Fichier À Inclure Les Dépendances De L'Observateur, et de trouver inutiles comprend visuellement.
Si vos fichiers d'en-tête commencent généralement avec
(par opposition à l'aide #pragma fois), vous pouvez changer cela pour l':
Et puisque le compilateur sorties le nom du fichier cpp en cours de compilation, qui vous permettrait de savoir au moins qui fichier cpp est à l'origine de l'en-tête pour être portées plusieurs fois.
A.h
etB.h
que les deux dépendent deC.h
et vous inclureA.h
etB.h
, parce que vous avez besoin, vous est inclureC.h
deux fois, mais c'est bien, parce que le compilateur va sauter une seconde fois et si vous n'avez pas, vous devriez vous rappeler de toujours inclureC.h
avantA.h
ouB.h
de se retrouver dans beaucoup plus inutiles les inclusions.PC-Lint peut en effet le faire. Un moyen facile de le faire est de configurer pour qu'il détecte seulement inclure les fichiers inutilisés et d'ignorer toutes les autres questions. C'est assez simple - pour activer le juste message 766 ("en-Tête de fichier non utilisé dans le module"), il suffit d'inclure les options -w0 +e766 sur la ligne de commande.
La même approche peut également être utilisé avec des messages tels que 964 ("fichier d'en-Tête ne sont pas directement utilisés dans le module") et 966 ("Indirectement inclus l'en-tête d'un fichier non utilisé dans le module").
FWIW j'ai écrit à ce sujet plus en détail dans un billet de blog la semaine dernière à http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318.
Si vous êtes à la recherche d'éliminer les
#include
fichiers afin de diminuer les temps de construire, votre temps et votre argent pourrait être mieux dépensé à la parallélisation de votre processus de création à l'aide de cl.exe /MP, make-j, Xoreax IncrediBuild, distcc/icecream, etc.Bien sûr, si vous avez déjà un parallèle processus de construction et vous êtes toujours en train d'accélérer, puis par tous les moyens de nettoyer votre
#include
directives et de supprimer ceux qui sont inutiles dépendances.Commencer avec chaque fichier include, et de veiller à ce que chaque fichier inclus ne comprend que ce qui est nécessaire pour compiler lui-même. Tous les fichiers qui sont ensuite manquant pour les fichiers C++, peuvent être ajoutés à des fichiers C++ eux-mêmes.
Pour chaque comprennent et le fichier source, commentaire chaque fichier include un à un et voir si ça compile.
C'est aussi une bonne idée pour trier les fichiers par ordre alphabétique, et où ce n'est pas possible, ajouter un commentaire.
L'ajout d'un ou deux des éléments suivants #définit
excluent souvent inutiles fichiers d'en-tête et
peuvent sensiblement améliorer la
les temps de compilation en particulier si le code n'est pas à l'aide de fonctions de l'API Windows.
Voir http://support.microsoft.com/kb/166474
Si vous ne l'êtes pas déjà, à l'aide d'un en-tête précompilé pour inclure tout ce que vous n'allez pas changer de plate-forme (en-têtes, externe SDK en-têtes, ou statique déjà terminé pièces de votre projet) fera une énorme différence dans les temps de construire.
http://msdn.microsoft.com/en-us/library/szfdksca(SV.71).aspx
Aussi, bien qu'il ne soit trop tard pour votre projet, l'organisation de votre projet en plusieurs sections et de ne pas regrouper tous les en-têtes d'un grand en-tête principal est une bonne pratique, même si cela prend un peu de travail supplémentaire.
Si vous travaillez avec Eclipse CDT vous pourriez essayer http://includator.com pour optimiser votre structure. Cependant, Includator pourrait ne pas en savoir assez sur VC++prédéfini comprend et mise en place CDT utiliser VC++ avec un bon comprend pas intégrée CDT encore.
La dernière Jetbrains IDE, CLion, montre automatiquement (en gris) les includes qui ne sont pas utilisés dans le fichier courant.
Il est également possible d'avoir la liste de tous les inutilisé comprend (et aussi des fonctions, des méthodes, etc...) à partir de l'IDE.
Certaines des réponses de l'état que c'est dur. C'est vrai, parce que vous avez besoin d'un compilateur complet pour détecter les cas dans lesquels une déclaration anticipée serait approprié. Vous ne pouvez pas analyser C++ sans savoir ce que les symboles; la grammaire est tout simplement trop ambiguë pour que. Vous devez savoir si un nom est le nom d'une classe (peut-être l'avant-déclarée) ou une variable (peut pas). Vous devez également être l'espace de noms courant.
Peut-être un peu tard, mais une fois j'ai trouvé un WebKit script perl qui fait juste ce que tu voulais. C'aurez besoin d'une adaptation je crois (je ne suis pas très versé en perl), mais ça devrait faire l'affaire:
http://trac.webkit.org/browser/branches/old/safari-3-2-branch/WebKitTools/Scripts/find-extra-includes
(c'est une vieille branche, parce que le tronc n'a pas le fichier plus)
Si il y a un en-tête particulier que vous pensez, il n'est plus nécessaire (dire
chaîne de caractères.h), vous pouvez mettre en commentaire que l'inclure, puis mettre ce ci-dessous toutes les
comprend:
De votre interface en-têtes peut utiliser un autre #define convention
pour enregistrer leur inclusion dans les RPC de la mémoire. Ou pas de convention, auquel cas
cette approche ne fonctionnera pas.
Puis reconstruire. Il y a trois possibilités:
Il s'appuie sur ok. chaîne de caractères.h n'était pas de la compilation critique, et de l'inclure pour elle
peut être retiré.
La #erreur de voyages. chaîne de caractères.g est inclus indirectement en quelque sorte
Vous ne savez toujours pas si la chaîne.h est nécessaire. Si cela est nécessaire, vous
directement #inclure (voir ci-dessous).
Vous obtenez quelque autre erreur de compilation. chaîne de caractères.h était nécessaire et n'est pas
inclus indirectement, de sorte que l'inclusion était correct pour commencer.
Note que, selon l'inclusion indirecte lorsque votre .h ou .c utilise directement
l'autre .h s'agit certainement d'un bug: vous êtes en effet prometteur que votre
le code n'aura besoin que d'en-tête, tant que certains autres d'en-tête que vous utilisez
l'exige, ce qui n'est probablement pas ce que vous voulez dire.
Les mises en garde mentionnées dans d'autres réponses sur les en-têtes de modifier le comportement de
plutôt que de déclarer des choses qui provoquent des échecs de compilation s'appliquent également ici.