Question

Je travaille sur un projet C ++ volumineux dans Visual Studio 2008 et de nombreux fichiers contiennent des directives #include inutiles. Parfois, les #include ne sont que des artefacts et tout se compilera sans les supprimer. Dans d'autres cas, les classes peuvent être déclarées et le #include peut être déplacé vers .cpp fichier. Existe-t-il de bons outils pour détecter ces deux cas?

Était-ce utile?

La solution

Bien que les fichiers d'inclusion inutiles ne soient pas révélés, Visual studio utilise le paramètre / showIncludes (clic droit sur un fichier .cpp , Propriétés- > C / C ++ - > Advanced ) qui générera une arborescence de tous les fichiers inclus lors de la compilation. Cela peut aider à identifier les fichiers qui n'auraient pas besoin d'être inclus.

Vous pouvez également jeter un coup d'œil à l'idiome pimpl pour vous permettre de vous en tirer avec moins de dépendances dans les fichiers d'en-tête, afin de vous aider à voir plus facilement ce que vous pouvez supprimer.

Autres conseils

Lint PC fonctionne assez bien pour cela, et cela trouve toutes sortes d'autres problèmes loufoques pour vous aussi. Il comporte des options de ligne de commande permettant de créer des outils externes dans Visual Studio, mais j’ai constaté que le Le complément Visual Lint est plus facile à utiliser. Même la version gratuite de Visual Lint aide. Mais donnez un coup de feu à 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 se passe.

Il existe un nouvel outil basé sur Clang, include-what- que vous utilisez et qui a pour but de le faire.

!! AVERTISSEMENT !! Je travaille sur un outil d'analyse statique commercial (pas PC Lint). !! AVERTISSEMENT !!

Une approche simple sans analyse syntaxique pose plusieurs problèmes:

1) Ensembles 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 le choix d'une surcharge différente plutôt que d'une erreur de compilation! Le résultat sera un changement silencieux de la sémantique qu'il sera peut-être très difficile de localiser par la suite.

2) Spécialisations de modèles:

Semblable à l’exemple de surcharge, si vous avez des spécialisations partielles ou explicites pour un modèle, vous souhaitez qu’ils soient tous visibles lorsque le modèle est utilisé. Il se peut que les spécialisations du modèle principal se trouvent dans des fichiers d’en-tête différents. Supprimer l'en-tête avec la spécialisation ne provoquera pas d'erreur de compilation, mais peut entraîner un comportement indéfini si cette spécialisation avait été sélectionnée. (Voir: Visibilité de la spécialisation de modèle de la fonction C ++ )

Comme indiqué par "msalters", l'analyse complète du code permet également d'analyser l'utilisation de la classe. En vérifiant comment une classe est utilisée par un chemin spécifique de fichiers, il est possible que la définition de la classe (et donc de toutes ses dépendances) puisse être supprimée complètement ou au moins déplacée à un niveau plus proche de la source principale dans l'include. arbre.

Je ne connais aucun outil de ce type et j'ai déjà pensé en écrire un, mais il s'avère que c'est un problème difficile à résoudre.

Disons que votre fichier source comprend a.h et b.h; a.h contient #define USE_FEATURE_X et b.h utilise #ifdef USE_FEATURE_X . Si #include "a.h" est mis en commentaire, votre fichier peut toujours être compilé, mais ne fera pas ce que vous attendez. La détection de cette par programmation n’est pas triviale.

Quel que soit l'outil utilisé, vous devez également connaître votre environnement de construction. Si a.h ressemble à:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

Ensuite, USE_FEATURE_X n'est défini que si WINNT est défini. L'outil doit donc savoir quelles directives sont générées par le compilateur lui-même ainsi que celles 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 cela. Mais je connais des programmeurs qui ont écrit un script Perl (ou Python) pour essayer de commenter chaque ligne d’inclusion, puis compiler chaque fichier.

Il semble que Eric Raymond dispose d'un outil pour cela .

La cpplint.py de Google a un " include ce que vous utilisez " règle (parmi beaucoup d’autres), mais pour autant que je sache, il ne faut pas inclure seulement ce que vous utilisez. " Même dans ce cas, cela peut être utile.

Si ce sujet vous intéresse en général, vous pouvez consulter le fichier Conception de logiciels C ++ à grande échelle . C'est un peu démodé, mais ça fait beaucoup de "conception physique" des questions telles que la recherche du minimum absolu d’en-têtes à inclure. Je n'ai pas vraiment vu ce genre de chose discuté ailleurs.

Essayez Include Manager . Il s’intègre facilement dans Visual Studio et permet de visualiser vos chemins d’inclusion, ce qui vous aide à rechercher des éléments inutiles. En interne, il utilise Graphviz, mais il existe de nombreuses autres fonctionnalités intéressantes. Et bien que ce soit un produit commercial, son prix est très bas.

Vous pouvez créer un graphe d'inclusion à l'aide de observateur de dépendances de fichiers à inclure en c / c ++ , et trouver inutile inclut visuellement.

Si vos fichiers d'en-tête commencent généralement par

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif

(par opposition à l'utilisation de #pragma une fois), vous pouvez le remplacer par:

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else 
#pragma message("Someheader.h superfluously included")
#endif

Et comme le compilateur affiche le nom du fichier cpp en cours de compilation, vous saurez au moins quel fichier cpp entraîne l’en-tête à être importé plusieurs fois.

PC-Lint peut en effet le faire. Un moyen simple de le faire est de le configurer pour détecter uniquement les fichiers d'inclusion inutilisés et ignorer tous les autres problèmes. C’est assez simple: pour n’activer que le message 766 ("Fichier d’en-tête non utilisé dans le module"), incluez simplement les options -w0 + e766 sur la ligne de commande.

La même approche peut également être utilisée avec des messages associés tels que 964 ("Fichier d'en-tête non utilisé directement dans le module") et 966 ("Fichier d'en-tête inclus indirectement non utilisé dans le module").

FWIW, j’ai écrit à ce sujet plus en détail dans un billet publié la semaine dernière sur http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318 .

Si vous cherchez à supprimer les fichiers #include inutiles afin de réduire les temps de construction, il serait peut-être préférable de passer votre temps et votre argent à paralléliser votre processus de construction à l'aide de cl.exe / MP , make -j , Xoreax IncrediBuild , distcc / glace, , 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 nettoyez absolument vos directives #include et supprimez ces dépendances inutiles.

Commencez avec chaque fichier d'inclusion et assurez-vous que chaque fichier d'inclusion n'inclut que ce qui est nécessaire pour se compiler. Tous les fichiers d'inclusion manquants pour les fichiers C ++ peuvent être ajoutés aux fichiers C ++ eux-mêmes.

Pour chaque fichier d'inclusion et source, commentez chaque fichier d'inclusion un par un et vérifiez s'il est compilé.

C’est également une bonne idée de trier les fichiers d’inclusion par ordre alphabétique. Si cela n’est pas possible, ajoutez un commentaire.

Ajout d’une ou des deux définitions suivantes exclura les fichiers d'en-tête souvent inutiles et peut considérablement améliorer les temps de compilation, en particulier si le code n’utilise pas les fonctions de l’API Windows.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

Voir http://support.microsoft.com/kb/166474

Si ce n'est déjà fait, utiliser un en-tête précompilé pour inclure tout ce que vous n'allez pas modifier (en-têtes de plate-forme, en-têtes SDK externes ou éléments statiques déjà terminés de votre projet) fera une énorme différence en termes de temps de construction. .

http://msdn.microsoft.com/ en-us / library / szfdksca (VS.71) .aspx

De plus, bien qu'il soit peut-être trop tard pour votre projet, organiser votre projet en sections et ne pas regrouper tous les en-têtes locaux dans un seul en-tête principal est une bonne pratique, même si cela nécessite un peu de travail supplémentaire.

Si vous souhaitez utiliser 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 sur la configuration de CDT pour utiliser VC ++ avec les inclusions correctes n'est pas encore intégré à CDT.

Le dernier IDE Jetbrains, CLion, affiche automatiquement (en gris) les inclus qui ne sont pas utilisés dans le fichier actuel.

Il est également possible d'avoir la liste de tous les inclus (ainsi que des fonctions, méthodes, etc ...) non utilisés à partir de l'EDI.

Certaines des réponses existantes indiquent que c'est difficile. C'est en effet vrai, car vous avez besoin d'un compilateur complet pour détecter les cas dans lesquels une déclaration anticipée serait appropriée. Vous ne pouvez pas analyser le C ++ sans savoir ce que signifient les symboles; la grammaire est trop ambiguë pour cela. Vous devez savoir si un certain nom nomme une classe (peut être déclarée en aval) ou une variable (ne peut pas). De plus, vous devez être conscient des espaces de noms.

Peut-être un peu tard, mais j’ai un jour trouvé un script WebKit en perl qui répondait exactement à vos attentes. Je crois qu'il faudra l'adapter (je ne connais pas très bien le langage perl), mais cela devrait suffire:

http : //trac.webkit.org/browser/branches/old/safari-3-2-branch/WebKitTools/Scripts/find-extra-includes

(il s'agit d'une ancienne branche car le coffre ne contient plus le fichier)

Si vous pensez qu'un en-tête particulier n'est plus nécessaire (par exemple, string.h), vous pouvez commenter ce qui inclut, puis le mettre 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 différente #define enregistrer leur inclusion dans la mémoire du RPC. Ou pas de convention, auquel cas cette approche ne fonctionnera pas.

Puis reconstruisez. Il y a trois possibilités:

  • Ça se passe bien. string.h n'était pas critique à la compilation, et l'inclusion correspondante peut être enlevé.

  • Le #error déclenche. string.g a été inclus indirectement d'une manière ou d'une autre Vous ne savez toujours pas si string.h est requis. Si nécessaire, vous devrait directement l'inclure (voir ci-dessous).

  • Vous obtenez une autre erreur de compilation. string.h était nécessaire et n'est pas inclus indirectement, donc l'inclusion était correcte pour commencer.

Notez que selon l’inclusion indirecte lorsque votre .h ou .c utilise directement un autre .h est presque certainement un bug: vous promettez en effet que votre le code ne nécessitera que cet en-tête aussi longtemps que d'autres en-têtes que vous utilisez le requiert, ce qui n’est probablement pas ce que vous vouliez dire.

Les mises en garde mentionnées dans d'autres réponses concernant les en-têtes modifiant le comportement plutôt que de déclarer les choses qui causent des échecs de construction s’appliquent aussi ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top