Comment détecter les fichiers inutiles #include dans un grand projet C++?
je travaille sur un grand projet C++ dans Visual Studio 2008, et il y a beaucoup de fichiers avec des directives #include
inutiles. Parfois, les #include
s ne sont que des artefacts et tout se compilera très bien avec eux supprimés, et dans d'autres cas, les classes pourraient être avancées et le #include pourrait être déplacé vers le fichier .cpp
. Existe-t-il de bons outils pour détecter ces deux cas?
20 réponses
bien qu'il ne révèlera pas les fichiers include non nécessaires, Visual studio a un paramètre /showIncludes
(clic droit sur un fichier .cpp
, Properties->C/C++->Advanced
) qui affichera un arbre de tous les fichiers inclus au moment de la compilation. Cela peut aider à identifier les fichiers qui ne devraient pas être inclus.
vous pouvez également jeter un coup d'oeil à l'idiome pimpl pour vous en tirer avec moins de dépendances de fichiers d'en-tête pour rendre plus facile de voir la fissure que vous pouvez enlever.
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 peuvent être utilisées pour créer des outils externes dans Visual Studio, mais j'ai trouvé que le Lint visuel addin est plus facile à travailler avec. Même la version gratuite de Visual Lint aide. Mais donnez une chance à PC-Lint. Le configurer pour qu'il ne vous donne pas trop d'Avertissements prend un peu de temps, mais vous serez étonné de ce qu'il tourne jusqu'.
Il y a un Bruit outil comprennent ce que vous utilisez , qui vise à faire cela.
!!DISCLAIMER!! Je travaille sur un outil d'analyse statique Commerciale (Pas PC Lint). !!DISCLAIMER!!
il y a plusieurs problèmes avec une approche simple de non parsing:
1) Jeux De Surcharge:
il est possible qu'une fonction surchargée ait des déclarations provenant de fichiers différents. Il se peut que la suppression d'un fichier d'en-tête entraîne une surcharge différente plutôt qu'une erreur de compilation! Le résultat sera un silencieux changement sémantique qui peuvent être très difficiles à repérer par la suite.
2) modèles de spécialisation:
semblable à l'exemple de surcharge, si vous avez des spécialisations partielles ou explicites pour un modèle, vous voulez qu'elles soient toutes visibles lorsque le modèle est utilisé. Il se peut que les spécialisations pour le modèle principal se trouvent dans des fichiers d'en-tête différents. Supprimer l'en-tête avec la spécialisation ne causera pas d'erreur de compilation, mais peut il en résulte un comportement non défini si cette spécialisation avait été choisie. (Voir: visibilité de la spécialisation de la fonction c++ )
comme le souligne "msalters", la réalisation d'une analyse complète du code permet également une analyse de l'utilisation de la classe. En vérifiant comment une classe est utilisée à travers un chemin spécifique de fichiers, il est possible que la définition de la classe (et donc toutes ses dépendances) puisse être supprimée complètement ou au moins déplacé à un niveau plus proche de la source principale dans l'arbre d'inclusion.
Je ne connais aucun de ces outils, et j'ai pensé en écrire un dans le passé, mais il s'avère que c'est un problème difficile à résoudre.
dites que votre fichier source inclut un.h et B. h; a. h contient #define USE_FEATURE_X
et B. h utilise #ifdef USE_FEATURE_X
. Si #include "a.h"
est commenté, votre fichier peut encore être compilé, mais ne pas faire ce que vous attendez. La détection de cette programmatically est non-triviale.
quel que soit l'outil utilisé ce besoin de connaître votre environnement de construction. Si a. h ressemble à:
#if defined( WINNT )
#define USE_FEATURE_X
#endif
puis USE_FEATURE_X
n'est défini que si WINNT
est défini, de sorte que l'outil devrait savoir quelles directives sont générées par le compilateur lui-même ainsi que celles qui sont spécifiées dans la commande de compilation plutôt que dans un fichier d'en-tête.
comme Timmermans, Je ne connais aucun outil pour ça. Mais j'ai connu des programmeurs qui ont écrit un script Perl (ou Python) pour essayer de commenter chaque ligne d'include une à une puis compiler chaque fichier.
il semble que maintenant Eric Raymond ait un outil pour ce .
Google cpplint.py a une règle" include what you use " (among many others), mais pour autant que je peut dire, pas de "comprendre seulement ce que vous utilisez."De la même façon, il peut être utile.
si vous êtes intéressé par ce sujet en général, vous pouvez jeter un oeil à Lakos' Large Scale C++ Software Design . C'est un peu désuet, mais il y a beaucoup de problèmes de "conception physique" comme trouver le minimum absolu de en-têtes qui doivent être inclus. Je n'ai jamais vraiment vu ce genre de chose discuter ailleurs.
si vos fichiers d'en-tête commencent généralement par
#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif
(au lieu d'utiliser #pragma une fois) vous pouvez changer cela en:
#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else
#pragma message("Someheader.h superfluously included")
#endif
et puisque le compilateur affiche le nom du fichier cpp compilé, cela vous permettrait de savoir au moins quel fichier cpp fait que l'en-tête est introduit plusieurs fois.
vous pouvez construire un graphique include en utilisant C/C++ Include File Dependencies Watcher , et trouver visuellement des includes non nécessaires.
PC-Lint peut effectivement le faire. Une façon facile de le faire est de le configurer pour détecter juste les fichiers include inutilisés et ignorer tous les autres problèmes. C'est assez simple - pour activer juste le message 766 ("fichier D'en-tête 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 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 cherchez à supprimer les fichiers inutiles #include
afin de réduire les temps de construction, votre temps et votre argent pourraient être mieux dépensés parallèlement à votre processus de construction en utilisant cl.exe / MP , make-j , Xoreax IncrediBuild , distcc/ icecream , etc.
bien sûr, si vous avez déjà un processus de construction parallèle et que vous essayez toujours de l'accélérer, alors par tous signifie nettoyer vos directives #include
et supprimer ces dépendances inutiles.
commence par chaque fichier include, et assure-toi que chaque fichier include n'inclut que ce qui est nécessaire pour se compiler. Tous les fichiers include qui manquent alors pour les fichiers c++, peuvent être ajoutés aux fichiers C++ eux-mêmes.
pour chaque fichier include et source, commentez chaque fichier include un à la fois et voyez s'il compile.
c'est également une bonne idée de trier les fichiers include par ordre alphabétique, et si cela n'est pas possible, Ajouter un commentaire.
ajout d'un ou des deux numéros suivants définit excluent souvent inutiles fichiers d'en-tête et peut s'améliorer considérablement compilez les temps en particulier si le code n'utilise pas les fonctions de L'API Windows.
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
si vous ne l'êtes pas déjà, l'utilisation d'un en-tête précompilé pour inclure tout ce que vous n'allez pas changer (des en-têtes de plate-forme, des en-têtes SDK externes, ou des pièces statiques déjà terminées de votre projet) fera une énorme différence dans les temps de construction.
http://msdn.microsoft.com/en-us/library/szfdksca (VS.71).aspx
aussi, bien qu'il soit peut-être trop tard pour votre projet, l'organisation de votre projet en sections et non de 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 voulez travailler avec Eclipse CDT vous pouvez essayer http://includator.com pour optimiser votre structure d'inclusion. Cependant, Includator pourrait ne pas en savoir assez sur les inclusions prédéfinies de VC++et la configuration de CDT pour utiliser VC++ avec des inclusions correctes n'est pas encore intégrée dans CDT.
la dernière version de JetBrains IDE, CLion, affiche automatiquement (en gris) les inclusions qui ne sont pas utilisées dans le fichier courant.
il est également possible d'avoir la liste de tous les non utilisés comprend (et aussi les fonctions, les méthodes, etc...) à partir de l'IDE.
certaines des réponses existantes indiquent que c'est difficile. 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 signifient; la grammaire est tout simplement trop ambiguë pour cela. Vous devez savoir si un certain nom nomme une classe (pourrait être forward-declared) ou une variable (ne peut pas). Vous devez également être l'espace de noms courant.
peut-être un peu tard, mais j'ai trouvé un script WebKit perl qui faisait exactement ce que vous vouliez. Il faudra s'adapter, je crois (Je ne connais pas bien perl), mais ça devrait faire l'affaire:
(c'est une vieille branche parce que le tronc n'a plus le fichier)
S'il y a un en-tête particulier que vous pensez n'est plus nécessaire (dire chaîne.h), Vous pouvez commenter qui comprennent puis mettre ce sous tous les comprend:
#ifdef _STRING_H_
# error string.h is included indirectly
#endif
bien sûr, vos en-têtes d'interface peuvent utiliser une convention #define différente pour enregistrer leur inclusion dans la mémoire du CPP. 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.h n'a pas été compilé-critique, et l'inclure pour lui peut être enlevé.
-
#erreur de voyages. chaîne.g est inclus indirectement en quelque sorte Tu ne sais toujours pas si string.h est nécessaire. Si cela est nécessaire, vous directement #inclure (voir ci-dessous).
-
vous obtenez une autre erreur de compilation. chaîne.h était nécessaire et n'est pas inclus indirectement, donc l'inclusion était correcte pour commencer avec.
notez que selon l'inclusion indirecte lorsque votre .h ou .C Utilisations directes l'autre .h est presque certainement un bug: vous êtes en effet promettant que votre le code ne requerra cet en-tête que si un autre 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 au sujet des en-têtes qui modifient le comportement plutôt que de déclarer des choses qui causent construire les échecs s'appliquent également ici.