Erkennen überflüssiger #includes in C / C ++?

289

Ich stelle oft fest, dass der Header-Bereich einer Datei immer größer wird, aber nie kleiner wird. Während des gesamten Lebens einer Quelldatei wurden Klassen möglicherweise verschoben und überarbeitet, und es ist sehr wahrscheinlich, dass es einige gibt #includes, die nicht mehr vorhanden sein müssen. Wenn Sie sie dort belassen, verlängern Sie nur die Kompilierungszeit und fügen unnötige Kompilierungsabhängigkeiten hinzu. Der Versuch herauszufinden, welche noch benötigt werden, kann ziemlich mühsam sein.

Gibt es ein Tool, das überflüssige # include-Anweisungen erkennen und vorschlagen kann, welche ich sicher entfernen kann?
Tut Lint das vielleicht?

shoosh
quelle
1
Die verknüpfte Frage scheint das Problem nur unter Windows zu lösen, insbesondere unter Verwendung von Visual Studio.
D'Nabre
7
Stimmen Sie ab, um dies erneut zu öffnen, da es bei dem Duplikat speziell um die Verwendung von Visual Studio geht.
Drew Dormann

Antworten:

42

Es ist nicht automatisch, aber doxygen erstellt Abhängigkeitsdiagramme für #includedDateien. Sie müssen sie visuell durchgehen, aber sie können sehr nützlich sein, um sich ein Bild davon zu machen, was was verwendet.

albert
quelle
5
Dies ist eine großartige Möglichkeit, Ketten zu sehen. Wenn Sie A -> B -> C -> D und A -> D sehen, wird die Redundanz sofort sichtbar.
Tom
34
@ Tom: Das ist eine schreckliche Idee: Zum einen wird nicht angezeigt, ob diese Includes benötigt werden oder nicht, und zum anderen sollte die Liste der Includes nicht von indirekten Includes abhängen, die sich in Zukunft ändern können (redundante Includes sind normalerweise nicht so großes Problem trotzdem, dank Guards und Compiler-Magie), aber welche Klassen / Funktionen tatsächlich in der Datei verwendet werden (Ihr Compiler sollte nicht Tausende von Zeilen Vorlagencode durchlaufen müssen, die nicht einmal instanziiert werden)
MikeMB
@albert, können Sie Screenshots davon einfügen und kurz beschreiben, wo Sie in die Sauerstoffausgabe klicken müssen?
Gabriel Staples
@GabrielStaples Es ist nicht meine Antwort, daher möchte ich keine Informationen hinzufügen. Ich habe nur den Link korrigiert (da der Hosting-Ort, auf den er sich bezieht, gestoppt / beschlagnahmt wurde, um verwendet zu werden).
Albert
177

Googles cppclean (Links zu: Download , Dokumentation ) kann verschiedene Kategorien von C ++ - Problemen finden und es kann jetzt überflüssige #includes finden.

Es gibt auch ein Clang-basiertes Tool, einschließlich-was-Sie-verwenden , das dies tun kann. include-what-you-use kann sogar Vorwärtsdeklarationen vorschlagen (damit Sie nicht so viel # einschließen müssen) und optional Ihre # Einschlüsse für Sie bereinigen.

In aktuellen Versionen von Eclipse CDT ist diese Funktion ebenfalls integriert: Wenn Sie im Menü "Quelle" auf "Includes organisieren" klicken, werden Ihre # Includes alphabetisch sortiert, alle von Eclipse verwendeten Header hinzugefügt, ohne sie direkt einzuschließen, und alle nicht verwendeten Header auskommentiert Ich glaube nicht, dass du brauchst. Diese Funktion ist jedoch nicht 100% zuverlässig.

Josh Kelley
quelle
2
Das tut es jetzt. Ich fange gerade an, es zu benutzen. Siehe meine Notiz hier. stackoverflow.com/questions/1301850/…
Chance
1
Das cppclean-Repository ist nicht verfügbar. Sie können es jetzt hier herunterladen : bitbucket.org/robertmassaioli/cppclean (die ursprüngliche Site ist jedoch für einige Beispielanwendungen immer noch nützlich)
Nick
3
Ich habe den Link zu einer gepflegten cppclean-Gabel aktualisiert: github.com/myint/cppclean
BenC
1
Beachten Sie, dass cppclean sie anscheinend nur in Header-Dateien findet, nicht in cpp-Dateien aus dem Dokument: "Unnötige #inschlüsse in Header-Dateien".
Zitrax
1
@wizurd - Ich habe mit den jüngsten Entwicklungen in Eclipse CDT nicht Schritt gehalten, aber ich denke nicht. iwyu ist gründlich und relativ langsam. Die Analyse von Eclipse CDT ist schnell (interaktiv) und beim Testen weniger genau.
Josh Kelley
65

Schauen Sie sich auch include-what-you-use an , um ein ähnliches Problem zu lösen.

Tzafrir
quelle
6
IMHO benötigt diese Antwort viel mehr Upvotes, da das IWYU-Tool von Google das endgültige Tool für diese Aufgabe sein wird, sobald die Knicke geklärt sind.
Dan Olson
5
sudo apt-get install iwyu
Andrew Wagner
Scheint großartig - mit zwei Cavaets 1) letztes Update Februar 2106 2) Gogole selbst verwendet es nur für C ++, nicht für C, was das OP angefordert hat.
Mawg sagt, Monica
Können Sie ein wenig erklären, wie ein Benutzer es verwenden sollte? Die README ist nicht sehr klar darüber, was die Ausgabe des Python-Skripts enthält.
King's Jester
Ich benutze dies, aber es ist nicht immer 100% korrekt. Vielleicht 70% mal gibt es die richtigen Vorschläge.
InQusitive
25

Das Problem beim Erkennen überflüssiger Includes besteht darin, dass es sich nicht nur um eine Typabhängigkeitsprüfung handeln kann. Ein überflüssiges Include ist eine Datei, die für die Kompilierung keinen Wert liefert und kein anderes Element ändert, von dem andere Dateien abhängen. Es gibt viele Möglichkeiten, wie eine Header-Datei eine Kompilierung ändern kann, indem sie beispielsweise eine Konstante definiert, ein verwendetes Makro neu definiert und / oder löscht und einen Namespace hinzufügt, der die Suche nach einem Namen später ändert. Um Elemente wie den Namespace zu erkennen, benötigen Sie viel mehr als einen Präprozessor. Tatsächlich benötigen Sie fast einen vollständigen Compiler.

Lint ist eher ein Style Checker und wird sicherlich nicht diese volle Fähigkeit haben.

Ich denke, Sie werden die einzige Möglichkeit finden, ein überflüssiges Include zu erkennen, indem Sie Suiten entfernen, kompilieren und ausführen.

JaredPar
quelle
8
Nichts davon ist ein Problem, wenn die Include-Dateien gut angelegt sind. Wenn Sie jemals Datei A vor Datei B einfügen müssen, machen Sie es falsch (und ich habe an Projekten gearbeitet, bei denen sie es falsch gemacht haben).
David Thornley
9
@ David, ja, aber das hängt von den Jahren der Entwickler ab, bevor Sie es richtig machen. Ich kann mit großer Sicherheit sagen, dass die Chancen dafür das Haus begünstigen, nicht Sie :(
JaredPar
Ja, aber das erfahre ich im Allgemeinen, wenn ich ein Programm ändere, und plötzlich habe ich einen Kompilierungsfehler (wenn ich Glück habe) oder einen obskuren Fehler. Das scheint die # include-Dateien zumindest auf lange Sicht ehrlich zu halten.
David Thornley
Ich würde genau das Gegenteil sagen. Sie benötigen lediglich einen Typabhängigkeitsprüfer. Es wird möglicherweise nicht kompiliert, nachdem Sie die Includes entsprechend angeordnet haben, aber dies sind Probleme, die trotzdem behoben werden sollten.
Benoît
1
@Benoit, dann würden Sie eine Klasse von Problemen ignorieren, die kompilieren, aber die Bedeutung Ihres Programms semantisch ändern. Überlegen Sie, wie ein #define in einer Datei einen # if-Zweig in einer anderen ändern kann. Durch das Entfernen eines Headers kann dies weiterhin zu unterschiedlichen Ergebnissen führen
JaredPar
15

Ich dachte, dass PCLint dies tun würde, aber es ist ein paar Jahre her, seit ich es mir angesehen habe. Sie könnten es überprüfen.

Ich habe mir diesen Blog angesehen und der Autor hat ein wenig über die Konfiguration von PCLint gesprochen, um nicht verwendete Includes zu finden. Könnte einen Blick wert sein.

itsmatt
quelle
Guter Fund! Ich muss das benutzen.
Crashmstr
4
Ich benutze PCLint regelmäßig und es sagt mir von nicht verwendeten Headern. Ich bin vorsichtig, den Header #include zu kommentieren und neu zu kompilieren, um sicherzugehen, dass der Header wirklich nicht verwendet wird ...
Harold Bamford
Danke für die Bestätigung, Harold.
itsmatt
5
zu teuer. kein tragfähiges Werkzeug für die Massen.
7

Der CScout- Refactoring-Browser kann überflüssige Include-Anweisungen in C-Code (leider nicht C ++) erkennen. Eine Beschreibung der Funktionsweise finden Sie in diesem Zeitschriftenartikel.

Diomidis Spinellis
quelle
5

Sie können ein schnelles Skript schreiben, das eine einzelne # include-Direktive löscht, die Projekte kompiliert und den Namen in #include und der Datei protokolliert, aus der es entfernt wurde, falls keine Kompilierungsfehler aufgetreten sind.

Lassen Sie es während der Nacht laufen, und am nächsten Tag erhalten Sie eine 100% korrekte Liste der Include-Dateien, die Sie entfernen können.

Manchmal funktioniert Brute-Force einfach :-)


edit: und manchmal nicht :-). Hier einige Informationen aus den Kommentaren:

  1. Manchmal können Sie zwei Header-Dateien separat entfernen, aber nicht beide zusammen. Eine Lösung besteht darin, die Header-Dateien während des Laufs zu entfernen und nicht zurückzubringen. Hier finden Sie eine Liste der Dateien, die Sie sicher entfernen können. Möglicherweise gibt es jedoch eine Lösung mit mehr zu entfernenden Dateien, die dieser Algorithmus nicht findet. (Es ist eine gierige Suche über den Bereich der zu entfernenden Include-Dateien. Es wird nur ein lokales Maximum gefunden.)
  2. Es kann zu geringfügigen Verhaltensänderungen kommen, wenn einige Makros je nach #ifdefs unterschiedlich neu definiert wurden. Ich denke, dies sind sehr seltene Fälle, und die Unit-Tests, die Teil des Builds sind, sollten diese Änderungen erfassen.
Gilad Naor
quelle
1
Seien Sie vorsichtig - sagen wir, es gibt zwei Header-Dateien, die beide eine Definition von etwas enthalten. Sie können beide entfernen, aber nicht beide. Sie müssen bei Ihrem Brute-Force-Ansatz etwas gründlicher vorgehen.
Dominic Rodger
Vielleicht haben Sie das so gemeint, aber ein Skript, das ein einzelnes Include entfernt und das zuletzt entfernte Include auslässt, wenn es erfolgreich entfernt wurde, würde den Trick tun.
Dominic Rodger
1
Schlechte Idee. Wenn eine Header-Datei # eine konstante BLAH definiert und eine andere Header-Datei #ifdef BLAH überprüft, wird das Entfernen der ersten Header-Datei möglicherweise noch erfolgreich kompiliert, aber Ihr Verhalten hat sich geändert.
Graeme Perrow
1
Dies kann auch Probleme mit Systemheadern verursachen, da in verschiedenen Implementierungen möglicherweise unterschiedliche Dinge in #include <vector> enthalten sind. Selbst wenn Sie sich an einen Compiler halten, können sich die Header in verschiedenen Versionen ändern.
David Thornley
2
Dies findet keine Fälle, in denen Sie einen Header einfügen, der den Header enthält, den Sie wirklich benötigen.
bk1e
5

Es tut uns leid, hier (erneut) zu posten, die Leute erweitern Kommentare oft nicht.

Überprüfen Sie meinen Kommentar zu crashmstr, FlexeLint / PC-Lint wird dies für Sie tun. Informationsnachricht 766. In Abschnitt 11.8.1 meines Handbuchs (Version 8.0) wird dies erläutert.

Auch und dies ist wichtig, iterieren Sie so lange, bis die Nachricht verschwindet . Mit anderen Worten, nachdem Sie nicht verwendete Header entfernt und Lint erneut ausgeführt haben, werden möglicherweise mehr Header-Dateien "nicht benötigt", sobald Sie einige nicht benötigte Header entfernen. (Das mag albern klingen, langsam lesen und analysieren, es macht Sinn.)

Dan
quelle
Ich weiß genau, was du meinst, und meine Reaktion war "Ewwww". Ich hasse solchen Code.
David Thornley
5

Ich habe noch nie ein vollwertiges Tool gefunden, das Ihre Anforderungen erfüllt. Das nächste, was ich verwendet habe, ist IncludeManager , mit dem Ihr Header-Einschlussbaum grafisch dargestellt wird , sodass Sie beispielsweise in nur einer Datei enthaltene Header und kreisförmige Header-Einschlüsse visuell erkennen können.

Dan Olson
quelle
4

Ich habe versucht, Flexelint (die Unix-Version von PC-Lint) zu verwenden, und hatte etwas gemischte Ergebnisse. Dies liegt wahrscheinlich daran, dass ich an einer sehr großen und knotigen Codebasis arbeite. Ich empfehle, jede Datei, die als nicht verwendet gemeldet wird, sorgfältig zu prüfen.

Die Hauptsorge sind Fehlalarme. Mehrere Includes desselben Headers werden als nicht benötigter Header gemeldet. Dies ist schlecht, da Flexelint Ihnen nicht sagt, in welcher Zeile der Header enthalten ist oder wo er zuvor enthalten war.

Eine der Möglichkeiten, wie automatisierte Tools dies falsch machen können:

In A.hpp:

class A { 
  // ...
};

In B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

In 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

Wenn Sie den Nachrichten von Flexelint blind folgen, werden Sie Ihre # include-Abhängigkeiten durcheinander bringen. Es gibt mehr pathologische Fälle, aber im Grunde müssen Sie die Header selbst überprüfen, um die besten Ergebnisse zu erzielen.

Ich empfehle diesen Artikel über Physical Structure und C ++ aus dem Blog Games from inside. Sie empfehlen einen umfassenden Ansatz zur Bereinigung des # include-Chaos:

Richtlinien

Hier ist eine Reihe von Richtlinien aus Lakos 'Buch, die die Anzahl der physischen Abhängigkeiten zwischen Dateien minimieren. Ich benutze sie seit Jahren und war immer sehr zufrieden mit den Ergebnissen.

  1. Jede CPP-Datei enthält zuerst eine eigene Header-Datei. [snip]
  2. Eine Header-Datei muss alle Header-Dateien enthalten, die zum Parsen erforderlich sind. [snip]
  3. Eine Header-Datei sollte die Mindestanzahl an Header-Dateien enthalten, die zum Parsen erforderlich sind. [snip]
Ben Martin
quelle
Lakos 'Buch eignet sich hervorragend für die Ausbildung - abgesehen von seinen veralteten Beobachtungen zur Compilertechnologie.
Tom
4

Wenn Sie Eclipse CDT verwenden, können Sie http://includator.com ausprobieren, das für Betatester (zum Zeitpunkt dieses Schreibens) kostenlos ist und überflüssige #includes automatisch entfernt oder fehlende hinzufügt. Für Benutzer mit FlexeLint oder PC-Lint, die Elicpse CDT verwenden, ist http://linticator.com möglicherweise eine Option (auch kostenlos für den Betatest). Während die Analyse von Lint verwendet wird, bietet es Schnellkorrekturen zum automatischen Entfernen der überflüssigen # include-Anweisungen.

PeterSom
quelle
Der Grund dafür ist, dass unsere Buchhaltungsabteilung kleinere Beträge nicht in Rechnung stellen kann. Wenn Sie die Zeit zählen, die Sie möglicherweise sparen, ist dies nicht so unvernünftig. Sobald wir die Möglichkeit haben, Kreditkartenzahlungen zu erhalten, können wir den Preis erheblich senken. Eine weitere Option wäre ein Sponsor für unsere Entwicklungsbemühungen. Unser Finanzierungsmodell erfordert, dass wir Gewinne erzielen, um unsere Forschungsarbeit zu finanzieren. Ich würde gerne Lizenzen viel billiger verkaufen, kann es aber nicht. Vielleicht werden wir es zu CDT beitragen und Sie bekommen es kostenlos, aber das muss ich irgendwie finanzieren. Ich habe vergessen, du kannst es kostenlos versuchen!
PeterSom
2

In diesem Artikel wird eine Technik zum Entfernen von #include mithilfe der Analyse von Doxygen erläutert. Das ist nur ein Perl-Skript, daher ist es recht einfach zu bedienen.

Steve Gury
quelle
1
Das Skript findet einige Includes zum Entfernen, aber es gibt auch viele Includes, die nicht entfernt werden können. Es scheint, dass es keine Klassenaufzählung unterstützt, es scheint auch, dass es eine schlechte Zeit mit Makro und manchmal mit Namespace hat.
Baptiste Wicht
1

Es gibt ein kostenloses Tool Include File Dependencies Watcher, das in das Visual Studio integriert werden kann. Es zeigt überflüssige #includes in rot.

Vladimir
quelle
1

Es gibt zwei Arten überflüssiger # include-Dateien:

  1. Eine Header-Datei, die vom Modul (.c, .cpp) überhaupt nicht benötigt wird
  2. Das Modul benötigt eine Header-Datei, die jedoch direkt oder indirekt mehrmals enthalten ist.

Meiner Erfahrung nach gibt es zwei Möglichkeiten, um es zu erkennen:

  • gcc -H oder cl.exe / showincludes (Problem 2 lösen)

    In der realen Welt können Sie CFLAGS = -H vor make exportieren, wenn nicht alle Makefile-Optionen CFLAGS überschreiben. Oder wie ich es verwendet habe, können Sie einen cc / g ++ - Wrapper erstellen, um jedem Aufruf von $ (CC) und $ (CXX) zwangsweise -H-Optionen hinzuzufügen. Wenn Sie das Verzeichnis des Wrappers der Variablen $ PATH voranstellen, verwendet Ihr make stattdessen den Wrapper-Befehl. Natürlich sollte Ihr Wrapper den echten gcc-Compiler aufrufen. Diese Tricks müssen geändert werden, wenn Ihr Makefile gcc direkt verwendet. anstelle von $ (CC) oder $ (CXX) oder nach impliziten Regeln.

    Sie können auch eine einzelne Datei kompilieren, indem Sie mit der Befehlszeile optimieren. Aber wenn Sie Header für das gesamte Projekt bereinigen möchten. Sie können die gesamte Ausgabe erfassen, indem Sie:

    sauber machen

    mache 2> & 1 | tee result.txt

  • PC-Lint / FlexeLint (Problem 1 und 2 lösen)

    Stellen Sie sicher, dass Sie die Optionen + e766 hinzufügen. Bei dieser Warnung geht es um: nicht verwendete Header-Dateien.

    pclint / flint -vf ...

    Dies führt dazu, dass die pclint-Ausgabe Header-Dateien enthält. Verschachtelte Header-Dateien werden entsprechend eingerückt.

zhaorufei
quelle
1

Um diese Diskussion zu beenden: Der C ++ - Präprozessor ist vollständig. Es ist eine semantische Eigenschaft, ob ein Include überflüssig ist. Aus dem Satz von Rice folgt daher, dass es unentscheidbar ist, ob ein Include überflüssig ist oder nicht. Es kann kein Programm geben, das (immer richtig) erkennt, ob ein Include überflüssig ist.

Algoman
quelle
5
Habe ich nach einer "immer richtigen" Lösung gefragt? Diese Antwort ist für die Diskussion nicht sehr produktiv.
Shoosh
1
Nun, es gab zahlreiche Beiträge, in denen Probleme erörtert wurden, mit denen sich ein solches Programm befassen müsste. Mein Beitrag gibt eine abschließende und korrekte Antwort auf diesen Teil der Diskussion. Und ich jedenfalls würde es nicht mögen, wenn mir ein Programm sagen würde, ich könnte ein #include sicher entfernen und dann wird mein Code nicht mehr kompiliert. (oder schlimmer noch - kompiliert immer noch, macht aber etwas anderes). Jedes solche Programm trägt dieses Risiko.
Algoman
4
Zwischen all den Spekulationen darüber, wie schwer das sein würde und wie Sie das eine oder andere Hindernis lösen könnten, gab ich Ihnen die einzig 100% richtige Antwort. Ich finde es ziemlich frech zu sagen, dass dies nicht produktiv war ...
Algoman
1
Ich erinnerte mich, dass der Satz von Rice besagt: "Es kann kein Programm geben, das immer prüfen kann, ob ein bestimmtes Programm dieses überflüssige Problem löst." Es kann einige Programme geben, die das überflüssige Problem lösen.
Zhe Yang
1
persönlich fand ich @ Algomans Eingabe sehr hilfreich. macht mir klar, wie schwer dieses Problem ist.
Bogardon
0

PC Lint von Gimpel Software kann melden, wenn eine Include-Datei mehr als einmal in einer Kompilierungseinheit enthalten war, kann jedoch keine Include-Dateien finden, die nicht so benötigt werden, wie Sie es suchen.

Bearbeiten: Es kann. Siehe die Antwort von itsmatt

Crashmstr
quelle
Bist du dir da sicher ? Ich habe FlexeLint (wie PCL) seit einigen Jahren nicht mehr für C ++ - Code verwendet, aber selbst kürzlich für C-Code konnte ich schwören, dass ich einige Nachrichten (ich glaube, es ist Code 766?) Über nicht verwendete Header-Dateien gesehen habe. Gerade überprüft (v8.0), siehe Abschnitt 11.8.1. des Handbuchs.
Dan