Détecter les #inclusions superflus en C / C ++?

289

Je trouve souvent que la section des en-têtes d'un fichier devient de plus en plus grande tout le temps, mais elle ne diminue jamais. Tout au long de la vie d'un fichier source, les classes peuvent avoir été déplacées et refactorisées et il est très possible qu'il y en ait plusieurs #includesqui n'ont plus besoin d'être là et plus. Les laisser là ne fait que prolonger le temps de compilation et ajoute des dépendances de compilation inutiles. Essayer de comprendre celles qui sont encore nécessaires peut être assez fastidieux.

Existe-t-il une sorte d'outil qui peut détecter les directives #include superflues et suggérer celles que je peux supprimer en toute sécurité?
La charpie fait-elle cela peut-être?

shoosh
la source
1
La question liée ne semble résoudre le problème que sous Windows, en particulier avec Visual Studio.
D'Nabre
7
Voter pour rouvrir cela, car le doublon concerne spécifiquement Visual Studio.
Drew Dormann

Réponses:

42

Ce n'est pas automatique, mais doxygen produira des diagrammes de dépendance pour les #includedfichiers. Vous devrez les parcourir visuellement, mais ils peuvent être très utiles pour obtenir une image de ce qui utilise quoi.

Albert
la source
5
C'est une excellente façon de voir les chaînes. Voir A -> B -> C -> D et A -> D révèle immédiatement la redondance.
Tom
34
@Tom: C'est une idée horrible: D'une part, cela ne montre pas si ces inclusions sont nécessaires ou non et, deuxièmement, la liste des inclusions ne devrait pas dépendre des inclusions indirectes qui pourraient changer à l'avenir (les inclusions redondantes ne sont généralement pas de telles gros problème de toute façon, grâce à l'inclusion de gardes et de la magie du compilateur), mais sur quelles classes / fonctions sont réellement utilisées dans le fichier (votre compilateur ne devrait pas avoir à parcourir des milliers de lignes de code de modèle qui ne sont même pas instanciées)
MikeMB
@albert, pouvez-vous inclure des captures d'écran de cela et décrire brièvement où cliquer dans la sortie doxygen?
Gabriel Staples
@GabrielStaples Ce n'est pas ma réponse, donc je ne veux pas y ajouter d'informations. J'ai seulement corrigé le lien (car le lieu d'hébergement auquel il faisait référence s'est arrêté / saisi pour être utilisé).
albert
177

Le cppclean de Google (liens vers: téléchargement , documentation ) peut trouver plusieurs catégories de problèmes C ++, et il peut désormais trouver des #includes superflus.

Il existe également un outil basé sur Clang, notamment ce que vous utilisez , qui peut le faire. include-what-you-use peut même suggérer des déclarations avancées (afin que vous n'ayez pas à en inclure autant) et éventuellement nettoyer vos #includes pour vous.

Les versions actuelles d' Eclipse CDT intègrent également cette fonctionnalité: passer sous le menu Source et cliquer sur Organiser comprend alphabétisera vos # inclus, ajoutera tous les en-têtes qu'Eclipse pense que vous utilisez sans les inclure directement et commente tous les en-têtes qu'il ne fait pas pensez pas que vous avez besoin. Cependant, cette fonctionnalité n'est pas fiable à 100%.

Josh Kelley
la source
2
C'est le cas maintenant. Je commence juste à l'utiliser. Voir ma note ici. stackoverflow.com/questions/1301850/…
Chance
1
Le référentiel cppclean est en panne, vous pouvez maintenant le récupérer ici: bitbucket.org/robertmassaioli/cppclean (le site d'origine est quand même utile pour un exemple d'utilisation)
Nick
3
J'ai mis à jour le lien vers une fourchette cppclean maintenue: github.com/myint/cppclean
BenC
1
Notez que cppclean semble les trouver uniquement dans les fichiers d'en-tête, pas les fichiers cpp, du document: "Inutile #includes dans les fichiers d'en-tête".
Zitrax
1
@wizurd - Je n'ai pas suivi les récents développements dans Eclipse CDT, mais je ne pense pas. iwyu est minutieux et relativement lent. L'analyse d'Eclipse CDT est rapide (interactive) et, lorsque je l'ai testée, moins précise.
Josh Kelley
65

Consultez également include-what-you-use , qui résout un problème similaire.

Tzafrir
la source
6
À mon humble avis, cette réponse nécessite beaucoup plus de votes positifs, car une fois les problèmes résolus, l'outil IWYU de Google sera l'outil définitif pour cette tâche.
Dan Olson
5
sudo apt-get install iwyu
Andrew Wagner
Cela semble génial - avec deux cavaets 1) dernière mise à jour février 2106 2) Gogole eux-mêmes ne l'utilisent que pour C ++, pas C, ce que l'OP a demandé.
Mawg dit réintégrer Monica le
Pouvez-vous expliquer un peu comment un utilisateur devrait l'utiliser? Le fichier README n'est pas très clair sur ce qui contient la sortie du script python.
Bouffon du roi du
J'utilise ceci, mais ce n'est pas toujours correct à 100%. Peut-être 70% de fois il donne les bonnes suggestions.
InQusitive
25

Le problème avec la détection d'inclusions superflues est qu'il ne peut pas être juste un vérificateur de dépendance de type. Une inclusion superflue est un fichier qui n'apporte rien de valeur à la compilation et ne modifie pas un autre élément dont dépendent les autres fichiers. Il existe de nombreuses façons dont un fichier d'en-tête peut modifier une compilation, par exemple en définissant une constante, en redéfinissant et / ou en supprimant une macro utilisée, en ajoutant un espace de noms qui modifie la recherche d'un nom quelque part sur la ligne. Afin de détecter des éléments comme l'espace de noms dont vous avez besoin de bien plus qu'un préprocesseur, vous avez en fait presque besoin d'un compilateur complet.

Lint est plus un vérificateur de style et n'aura certainement pas cette pleine capacité.

Je pense que vous trouverez le seul moyen de détecter une inclusion superflue: supprimer, compiler et exécuter des suites.

JaredPar
la source
8
Rien de tout cela ne sera un problème si les fichiers include sont bien disposés. Si vous avez besoin d'inclure le fichier A avant le fichier B, vous le faites mal (et j'ai travaillé sur des projets où ils l'ont mal fait).
David Thornley
9
@ David, oui, mais cela dépend des années de développeurs avant de le faire correctement. Je peux dire avec une grande certitude que les chances que cela se produise favorisent la maison, pas vous :(
JaredPar
Oui, mais je le découvre généralement lors de la modification d'un programme, et soudain j'ai une erreur de compilation (si j'ai de la chance) ou un bug obscur. Cela semble garder les fichiers #include honnêtes, au moins à long terme.
David Thornley
Je dirais exactement le contraire. Tout ce dont vous avez besoin est un vérificateur de dépendance de type. Il peut ne pas être compilé après que vous ayez organisé les inclusions en conséquence, mais ce sont des problèmes qui devraient être traités de toute façon.
Benoît
1
@Benoit, vous ignorez alors une classe de problèmes qui compilent mais changent sémantiquement la signification de votre programme. Considérez comment un #define dans un fichier peut altérer une branche #if dans un autre. La suppression d'un en-tête peut toujours permettre la compilation avec des résultats différents
JaredPar
15

Je pensais que PCLint ferait cela, mais cela fait quelques années que je ne l'ai pas examiné. Vous pourriez le vérifier.

J'ai regardé ce blog et l'auteur a parlé un peu de la configuration de PCLint pour trouver les inclusions inutilisées. Ça vaut peut-être le coup d'oeil.

itsmatt
la source
Bonne trouvaille! Je vais devoir l'utiliser.
crashmstr
4
J'utilise PCLint régulièrement et cela me dit des en-têtes inutilisés. Je prends soin de commenter l'en-tête #include et de recompiler pour être sûr que l'en-tête est vraiment inutilisé ...
Harold Bamford
Merci pour la confirmation, Harold.
itsmatt
5
trop cher. pas un outil viable pour les masses.
7

Le navigateur de refactoring CScout peut détecter les directives d'inclusion superflues dans le code C (malheureusement pas C ++). Vous pouvez trouver une description de son fonctionnement dans cet article de journal.

Diomidis Spinellis
la source
5

Vous pouvez écrire un script rapide qui efface une seule directive #include, compile les projets et enregistre le nom dans le #include et le fichier dont il a été supprimé dans le cas où aucune erreur de compilation ne s'est produite.

Laissez-le fonctionner pendant la nuit, et le lendemain, vous aurez une liste 100% correcte des fichiers d'inclusion que vous pouvez supprimer.

Parfois, la force brute fonctionne :-)


edit: et parfois ce n'est pas :-). Voici quelques informations tirées des commentaires:

  1. Parfois, vous pouvez supprimer deux fichiers d'en-tête séparément, mais pas les deux ensemble. Une solution consiste à supprimer les fichiers d'en-tête pendant l'exécution et à ne pas les récupérer. Cela trouvera une liste de fichiers que vous pouvez supprimer en toute sécurité, bien qu'il puisse y avoir une solution avec plus de fichiers à supprimer que cet algorithme ne trouvera pas. (c'est une recherche gourmande sur l'espace des fichiers à supprimer. Il ne trouvera qu'un maximum local)
  2. Il peut y avoir de subtils changements de comportement si certaines macros sont redéfinies différemment selon certains #ifdefs. Je pense que ce sont des cas très rares, et les tests unitaires qui font partie de la construction devraient détecter ces changements.
Gilad Naor
la source
1
Faites attention à cela - disons qu'il y a deux fichiers d'en-tête qui contiennent tous deux une définition de quelque chose. Vous pouvez supprimer l'un ou l'autre, mais pas les deux. Vous devrez être un peu plus approfondi dans votre approche de la force brute.
Dominic Rodger
C'est peut-être ce que vous vouliez dire, mais un script qui supprime une seule inclusion et laisse la dernière inclusion supprimée si elle a été supprimée avec succès ferait l'affaire.
Dominic Rodger
1
Mauvaise idée. Si un fichier d'en-tête # définit un BLAH constant et qu'un autre fichier d'en-tête vérifie #ifdef BLAH, la suppression du premier fichier d'en-tête peut toujours être compilée avec succès mais votre comportement a changé.
Graeme Perrow
1
Cela peut également provoquer des problèmes avec les en-têtes du système, car différentes implémentations peuvent avoir différentes choses incluses dans #include <vector>. Même si vous vous en tenez à un seul compilateur, les en-têtes peuvent changer selon les versions.
David Thornley
2
Cela ne trouvera pas les cas où vous incluez un en-tête qui comprend l'en-tête dont vous avez vraiment besoin.
bk1e
5

Désolé de (re) poster ici, les gens ne développent souvent pas de commentaires.

Vérifiez mon commentaire à crashmstr, FlexeLint / PC-Lint fera cela pour vous. Message d'information 766. La section 11.8.1 de mon manuel (version 8.0) en discute.

De plus, et c'est important, continuez à répéter jusqu'à ce que le message disparaisse . En d'autres termes, après avoir supprimé les en-têtes inutilisés, relancez lint, d'autres fichiers d'en-tête peuvent être devenus "inutiles" une fois que vous avez supprimé certains en-têtes inutiles. (Cela peut sembler idiot, lisez-le lentement et analysez-le, cela a du sens.)

Dan
la source
Je sais exactement ce que tu veux dire, et ma réaction a été "Ewwww". Je déteste le code comme ça.
David Thornley
5

Je n'ai jamais trouvé d'outil à part entière qui accomplisse ce que vous demandez. La chose la plus proche que j'ai utilisée est IncludeManager , qui représente votre arbre d'inclusion d'en-tête afin que vous puissiez repérer visuellement des éléments tels que des en-têtes inclus dans un seul fichier et des inclusions d'en-tête circulaires.

Dan Olson
la source
4

J'ai essayé d'utiliser Flexelint (la version unix de PC-Lint) et j'ai eu des résultats quelque peu mitigés. C'est probablement parce que je travaille sur une base de code très volumineuse et noueuse. Je recommande d'examiner attentivement chaque fichier signalé comme inutilisé.

La principale inquiétude concerne les faux positifs. Plusieurs inclusions du même en-tête sont signalées comme un en-tête inutile. C'est mauvais car Flexelint ne vous dit pas sur quelle ligne l'en-tête est inclus ni où il a été inclus auparavant.

Une des façons dont les outils automatisés peuvent se tromper:

Dans A.hpp:

class A { 
  // ...
};

Dans B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

Dans C.cpp:

#include "C.hpp"  

#include "B.hpp"  // <-- Unneeded, but lint reports it as needed
#include "A.hpp"  // <-- Needed, but lint reports it as unneeded

Si vous suivez aveuglément les messages de Flexelint, vous ferez le ménage dans vos dépendances #include. Il y a plus de cas pathologiques, mais fondamentalement, vous devrez inspecter les en-têtes vous-même pour de meilleurs résultats.

Je recommande fortement cet article sur la structure physique et le C ++ du blog Games from inside. Ils recommandent une approche globale pour nettoyer le gâchis #include:

Des lignes directrices

Voici un ensemble distillé de directives du livre de Lakos qui minimisent le nombre de dépendances physiques entre les fichiers. Je les utilise depuis des années et j'ai toujours été très satisfait des résultats.

  1. Chaque fichier cpp comprend d'abord son propre fichier d'en-tête. [couper]
  2. Un fichier d'en-tête doit inclure tous les fichiers d'en-tête nécessaires à son analyse. [couper]
  3. Un fichier d'en-tête doit avoir le nombre minimal de fichiers d'en-tête nécessaires pour l'analyser. [couper]
Ben Martin
la source
Le livre de Lakos est idéal pour l'éducation - en dehors de ses observations obsolètes sur la technologie des compilateurs.
Tom
4

Si vous utilisez Eclipse CDT, vous pouvez essayer http://includator.com qui est gratuit pour les bêta-testeurs (au moment d'écrire ces lignes) et supprime automatiquement les #includes superflus ou ajoute ceux qui manquent. Pour les utilisateurs qui ont FlexeLint ou PC-Lint et utilisent Elicpse CDT, http://linticator.com peut être une option (également gratuit pour le test bêta). Bien qu'il utilise l'analyse de Lint, il fournit des solutions rapides pour supprimer automatiquement les instructions #include superflues.

PeterSom
la source
La raison en est que notre service de comptabilité n'est pas en mesure de facturer des montants inférieurs. Si vous comptez le temps que vous pourriez économiser, ce n'est pas si déraisonnable. Une fois, nous avons la possibilité d'obtenir des paiements par carte de crédit, nous pouvons réduire considérablement le prix. Une autre option serait de parrainer nos efforts de développement. Notre modèle de financement nous oblige à réaliser des profits pour financer nos travaux de recherche. Je serais heureux de vendre des licences beaucoup moins cher, mais je ne peux pas. Peut-être que nous le contribuerons au CDT et vous l'obtiendrez gratuitement, mais que je dois financer d'une manière ou d'une autre. J'ai oublié, vous pouvez essayer gratuitement!
PeterSom
2

Cet article explique une technique de suppression de #include en utilisant l'analyse de Doxygen. Ce n'est qu'un script perl, il est donc assez facile à utiliser.

Steve Gury
la source
1
Le script trouve des inclusions à supprimer mais il donne également de nombreuses inclusions qui ne peuvent pas être supprimées. Il semble qu'il ne supporte pas l'énumération de classe, semble également avoir un mauvais moment avec la macro et parfois avec l'espace de noms.
Baptiste Wicht
1

Il existe un outil gratuit Include File Dependencies Watcher qui peut être intégré dans le studio visuel. Il montre # superflu comprend en rouge.

Vladimir
la source
1

Il existe deux types de fichiers #include superflus:

  1. Un fichier d'en-tête qui n'est pas du tout nécessaire au module (.c, .cpp)
  2. Un fichier d'en-tête est requis par le module mais il est inclus plusieurs fois, directement ou indirectement.

Il y a 2 façons dans mon expérience qui fonctionnent bien pour le détecter:

  • gcc -H ou cl.exe / showincludes (résoudre le problème 2)

    Dans le monde réel, vous pouvez exporter CFLAGS = -H avant make, si toutes les options CFLAGS du Makefile ne remplacent pas. Ou comme je l'ai utilisé, vous pouvez créer un wrapper cc / g ++ pour ajouter des options -H de force à chaque appel de $ (CC) et $ (CXX). et ajoutez le répertoire du wrapper à la variable $ PATH, alors votre make utilisera à la place votre commande wrapper. Bien sûr, votre wrapper doit invoquer le vrai compilateur gcc. Ces astuces doivent changer si votre Makefile utilise directement gcc. au lieu de $ (CC) ou $ (CXX) ou par des règles implicites.

    Vous pouvez également compiler un seul fichier en peaufinant avec la ligne de commande. Mais si vous voulez nettoyer les en-têtes de l'ensemble du projet. Vous pouvez capturer toutes les sorties en:

    nettoyer

    faire 2> & 1 | tee result.txt

  • PC-Lint / FlexeLint (résoudre les problèmes 1 et 2)

    assurez-vous d'ajouter les options + e766, cet avertissement concerne: les fichiers d'en-tête inutilisés.

    pclint / flint -vf ...

    Cela entraînera la sortie pclint des fichiers d'en-tête inclus, les fichiers d'en-tête imbriqués seront mis en retrait de manière appropriée.

zhaorufei
la source
1

Pour terminer cette discussion: le préprocesseur c ++ est terminé. C'est une propriété sémantique, qu'une inclusion soit superflue. Par conséquent, il résulte du théorème de Rice qu'il est indécidable si une inclusion est superflue ou non. Il NE PEUT PAS y avoir de programme qui (toujours correctement) détecte si une inclusion est superflue.

Algoman
la source
5
Ai-je demandé une solution "toujours correcte"? Cette réponse n'est pas très productive pour la discussion.
shoosh
1
Eh bien, il y a eu de nombreux articles sur les problèmes qu'un tel programme devrait traiter. Mon message donne une réponse concluante et correcte à cette partie de la discussion. Et pour ma part, je ne voudrais pas, si un programme me le disait, je pourrais supprimer en toute sécurité un #include et alors mon code ne compile plus. (ou pire - compile toujours mais fait quelque chose différemment). Tout programme de ce type comporte ce risque.
Algoman
4
Entre toutes les spéculations sur la façon dont cela serait difficile et comment vous pourriez résoudre un obstacle ou un autre, je vous ai donné la seule réponse correcte à 100%. Je trouve assez impudent de dire que ce n'était pas productif ...
Algoman
1
Je me suis souvenu que le théorème de Rice disait "Il ne peut pas y avoir de programme qui puisse toujours vérifier si un programme donné résout ce problème d'inclusions superflues". Il peut y avoir quelques programmes qui résolvent le problème des inclusions superflues.
Zhe Yang
1
personnellement, j'ai trouvé la contribution de @ Algoman très utile. me fait réaliser à quel point ce problème est difficile.
bogardon
0

PC Lint de Gimpel Software peut signaler lorsqu'un fichier inclus a été inclus plus d'une fois dans une unité de compilation , mais il ne peut pas trouver les fichiers inclus qui ne sont pas nécessaires comme vous le recherchez.

Edit: C'est possible. Voir la réponse de itsmatt

crashmstr
la source
En êtes-vous sûr ? Je n'ai pas utilisé FlexeLint (identique à PCL) depuis quelques années sur le code C ++, mais même récemment sur le code C, je pourrais jurer avoir vu quelques messages (je pense que c'est le code 766?) Sur les fichiers d'en-tête inutilisés. Vient de cocher (v8.0), voir la section 11.8.1. du manuel.
Dan