Ich arbeite an einem großen C ++ - Projekt in Visual Studio 2008, und es gibt viele Dateien mit unnötigen #include
Anweisungen. Manchmal sind die #include
s nur Artefakte und alles wird gut kompiliert, wenn sie entfernt werden. In anderen Fällen können Klassen vorwärts deklariert und das #include in die .cpp
Datei verschoben werden. Gibt es gute Werkzeuge, um diese beiden Fälle zu erkennen?
quelle
PC Lint funktioniert hierfür recht gut und findet auch für Sie alle möglichen anderen albernen Probleme. Es verfügt über Befehlszeilenoptionen, mit denen externe Tools in Visual Studio erstellt werden können. Ich habe jedoch festgestellt, dass das Visual Lint- Add-In einfacher zu bearbeiten ist. Auch die kostenlose Version von Visual Lint hilft. Aber probieren Sie PC-Lint aus. Die Konfiguration so, dass Sie nicht zu viele Warnungen erhalten, dauert einige Zeit, aber Sie werden erstaunt sein, was dabei herauskommt.
quelle
Es gibt ein neues Clang-basiertes Tool, einschließlich-was-Sie-verwenden , das darauf abzielt.
quelle
!!HAFTUNGSAUSSCHLUSS!! Ich arbeite an einem kommerziellen statischen Analysetool (nicht an PC Lint). !!HAFTUNGSAUSSCHLUSS!!
Bei einem einfachen Nicht-Parsing-Ansatz gibt es mehrere Probleme:
1) Überlastsätze:
Es ist möglich, dass eine überladene Funktion Deklarationen enthält, die aus verschiedenen Dateien stammen. Es kann sein, dass das Entfernen einer Header-Datei dazu führt, dass eine andere Überladung anstelle eines Kompilierungsfehlers ausgewählt wird! Das Ergebnis wird eine stille Änderung der Semantik sein, die danach möglicherweise nur sehr schwer aufzuspüren ist.
2) Vorlagenspezialisierungen:
Ähnlich wie im Überladungsbeispiel möchten Sie, wenn Sie teilweise oder explizite Spezialisierungen für eine Vorlage haben, dass alle sichtbar sind, wenn die Vorlage verwendet wird. Möglicherweise befinden sich Spezialisierungen für die primäre Vorlage in verschiedenen Header-Dateien. Das Entfernen des Headers mit der Spezialisierung führt nicht zu einem Kompilierungsfehler, kann jedoch zu einem undefinierten Verhalten führen, wenn diese Spezialisierung ausgewählt worden wäre. (Siehe: Sichtbarkeit der Vorlagenspezialisierung der C ++ - Funktion )
Wie von 'msalters' hervorgehoben, ermöglicht die Durchführung einer vollständigen Analyse des Codes auch die Analyse der Klassenverwendung. Durch Überprüfen, wie eine Klasse über einen bestimmten Dateipfad verwendet wird, kann die Definition der Klasse (und damit alle ihre Abhängigkeiten) vollständig entfernt oder zumindest auf eine Ebene verschoben werden, die näher an der Hauptquelle im Include liegt Baum.
quelle
Ich kenne keine solchen Tools und habe in der Vergangenheit darüber nachgedacht, eines zu schreiben, aber es stellt sich heraus, dass dies ein schwer zu lösendes Problem ist.
Angenommen, Ihre Quelldatei enthält ah und bh. ah enthält
#define USE_FEATURE_X
und bh verwendet#ifdef USE_FEATURE_X
. Wenn dies#include "a.h"
auskommentiert ist, wird Ihre Datei möglicherweise noch kompiliert, tut jedoch möglicherweise nicht das, was Sie erwarten. Dies programmgesteuert zu erkennen ist nicht trivial.Unabhängig davon, welches Tool dies tut, muss auch Ihre Build-Umgebung bekannt sein. Wenn ah aussieht wie:
Dann
USE_FEATURE_X
wird nur definiert, wennWINNT
definiert ist, sodass das Tool wissen muss, welche Anweisungen vom Compiler selbst generiert werden und welche im Befehl compile und nicht in einer Header-Datei angegeben sind.quelle
Wie Timmermans kenne ich keine Werkzeuge dafür. Aber ich kenne Programmierer, die ein Perl- (oder Python-) Skript geschrieben haben, um zu versuchen, jede Include-Zeile einzeln zu kommentieren und dann jede Datei zu kompilieren.
Es scheint, dass Eric Raymond jetzt ein Werkzeug dafür hat .
In cpplint.py von Google gibt es (unter anderem) die Regel " Einschließen, was Sie verwenden", aber soweit ich das beurteilen kann, nein " Nur einschließen , was Sie verwenden". Trotzdem kann es nützlich sein.
quelle
Wenn Sie sich allgemein für dieses Thema interessieren, sollten Sie sich Lakos ' Large Scale C ++ Software Design ansehen . Es ist etwas veraltet, geht aber auf viele "physische Design" -Probleme ein, wie das Finden des absoluten Minimums an Headern, die eingeschlossen werden müssen. Ich habe so etwas nirgendwo anders gesehen.
quelle
Probieren Sie Include Manager aus . Es lässt sich problemlos in Visual Studio integrieren und visualisiert Ihre Include-Pfade, sodass Sie unnötige Inhalte finden können. Intern wird Graphviz verwendet, aber es gibt noch viele weitere coole Funktionen. Und obwohl es ein kommerzielles Produkt ist, hat es einen sehr niedrigen Preis.
quelle
Sie können ein Include-Diagramm mit C / C ++ Include File Dependencies Watcher erstellen und nicht benötigte Includes visuell suchen.
quelle
Wenn Ihre Header-Dateien im Allgemeinen mit beginnen
(im Gegensatz zur einmaligen Verwendung von #pragma) Sie können dies ändern in:
Und da der Compiler den Namen der zu kompilierenden CPP-Datei ausgibt, würden Sie zumindest wissen, durch welche CPP-Datei der Header mehrmals eingefügt wird.
quelle
A.h
undB.h
dass beide davon abhängenC.h
und Sie einschließen,A.h
undB.h
weil Sie beide benötigen, werden Sie zweimal einschließen , aber das ist in Ordnung, weil der Compiler es das zweite Mal überspringt und wenn Sie es nicht getan haben, müssten Sie sichC.h
erinnern immerC.h
vorA.h
oder amB.h
Ende in viel nutzloseren Einschlüssen einzuschließen.PC-Lint kann dies tatsächlich. Eine einfache Möglichkeit, dies zu tun, besteht darin, es so zu konfigurieren, dass nur nicht verwendete Include-Dateien erkannt und alle anderen Probleme ignoriert werden. Dies ist ziemlich einfach - um nur die Nachricht 766 ("Header-Datei wird im Modul nicht verwendet") zu aktivieren, fügen Sie einfach die Optionen -w0 + e766 in die Befehlszeile ein.
Der gleiche Ansatz kann auch für verwandte Nachrichten wie 964 ("Header-Datei wird nicht direkt im Modul verwendet") und 966 ("Indirekt enthaltene Header-Datei, die nicht im Modul verwendet wird") verwendet werden.
FWIW Ich habe darüber letzte Woche in einem Blog-Beitrag unter http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318 ausführlicher geschrieben .
quelle
Wenn Sie unnötige
#include
Dateien entfernen möchten , um die Erstellungszeiten zu verkürzen , sollten Sie Ihre Zeit und Ihr Geld besser für die Parallelisierung Ihres Erstellungsprozesses mit cl.exe / MP , make -j , Xoreax IncrediBuild , distcc / icecream verwenden usw. verwenden.Wenn Sie bereits einen parallelen Erstellungsprozess haben und dennoch versuchen, ihn zu beschleunigen, bereinigen Sie auf jeden Fall Ihre
#include
Anweisungen und entfernen Sie diese unnötigen Abhängigkeiten.quelle
Beginnen Sie mit jeder Include-Datei und stellen Sie sicher, dass jede Include-Datei nur das enthält, was zum Kompilieren selbst erforderlich ist. Alle Include-Dateien, die dann für die C ++ - Dateien fehlen, können zu den C ++ - Dateien selbst hinzugefügt werden.
Kommentieren Sie für jede Include- und Quelldatei jede Include-Datei einzeln aus und prüfen Sie, ob sie kompiliert wird.
Es ist auch eine gute Idee, die Include-Dateien alphabetisch zu sortieren. Wenn dies nicht möglich ist, fügen Sie einen Kommentar hinzu.
quelle
Das Hinzufügen einer oder beider der folgenden #defines schließt häufig unnötige Header-Dateien aus und kann die Kompilierungszeiten erheblich verbessern, insbesondere wenn der Code keine Windows-API-Funktionen verwendet.
Siehe http://support.microsoft.com/kb/166474
quelle
Wenn Sie dies noch nicht getan haben, wird die Verwendung eines vorkompilierten Headers, der alles enthält, was Sie nicht ändern möchten (Plattform-Header, externe SDK-Header oder statische bereits abgeschlossene Teile Ihres Projekts), einen großen Unterschied in den Erstellungszeiten bewirken.
http://msdn.microsoft.com/en-us/library/szfdksca(VS.71).aspx
Auch wenn es für Ihr Projekt möglicherweise zu spät ist, ist es eine gute Praxis, Ihr Projekt in Abschnitte zu unterteilen und nicht alle lokalen Header in einem großen Hauptheader zusammenzufassen, obwohl dies ein wenig zusätzliche Arbeit erfordert.
quelle
Wenn Sie mit Eclipse CDT arbeiten würden, könnten Sie http://includator.com ausprobieren, um Ihre Include-Struktur zu optimieren. Includator weiß jedoch möglicherweise nicht genug über die vordefinierten Includes von VC ++, und das Einrichten von CDT für die Verwendung von VC ++ mit korrekten Includes ist noch nicht in CDT integriert.
quelle
Die neueste Jetbrains-IDE, CLion, zeigt automatisch (in grau) die Includes an, die in der aktuellen Datei nicht verwendet werden.
Es ist auch möglich, die Liste aller nicht verwendeten Includes (sowie Funktionen, Methoden usw.) aus der IDE abzurufen.
quelle
Einige der vorhandenen Antworten besagen, dass es schwierig ist. Dies ist in der Tat richtig, da Sie einen vollständigen Compiler benötigen, um die Fälle zu ermitteln, in denen eine Vorwärtsdeklaration angemessen wäre. Sie können C ++ nicht analysieren, ohne zu wissen, was die Symbole bedeuten. Die Grammatik ist dafür einfach zu vieldeutig. Sie müssen wissen, ob ein bestimmter Name eine Klasse (könnte vorwärts deklariert werden) oder eine Variable (kann nicht) benennt. Außerdem müssen Sie Namespace-fähig sein.
quelle
Vielleicht etwas spät, aber ich habe einmal ein WebKit-Perl-Skript gefunden, das genau das tat, was Sie wollten. Ich glaube, es muss angepasst werden (ich bin nicht gut mit Perl vertraut), aber es sollte den Trick machen:
http://trac.webkit.org/browser/branches/old/safari-3-2-branch/WebKitTools/Scripts/find-extra-includes
(Dies ist ein alter Zweig, da Trunk die Datei nicht mehr hat.)
quelle
Wenn es einen bestimmten Header gibt, von dem Sie glauben, dass er nicht mehr benötigt wird (z. B. string.h), können Sie das Include auskommentieren und dann unter alle Includes setzen:
Natürlich verwenden Ihre Schnittstellen-Header möglicherweise eine andere # Define-Konvention, um ihre Aufnahme in den CPP-Speicher aufzuzeichnen. Oder keine Konvention. In diesem Fall funktioniert dieser Ansatz nicht.
Dann wieder aufbauen. Es gibt drei Möglichkeiten:
Es baut sich gut auf. string.h war nicht kompilierungskritisch und das Include dafür kann entfernt werden.
Die #Fehlerauslösungen. string.g wurde irgendwie indirekt aufgenommen. Sie wissen immer noch nicht, ob string.h erforderlich ist. Wenn es erforderlich ist, sollten Sie es direkt einschließen (siehe unten).
Sie erhalten einen anderen Kompilierungsfehler. string.h wurde benötigt und wird nicht indirekt eingeschlossen, daher war das Include zunächst korrekt.
Beachten Sie, dass abhängig von der indirekten Einbeziehung, wenn Ihre .h- oder .c-Datei direkt eine andere .h-Datei verwendet, mit ziemlicher Sicherheit ein Fehler vorliegt: Sie versprechen tatsächlich, dass Ihr Code diesen Header nur benötigt, solange ein anderer von Ihnen verwendeter Header dies erfordert. Das ist wahrscheinlich nicht das, was du gemeint hast.
Die in anderen Antworten erwähnten Vorbehalte zu Headern, die das Verhalten ändern, anstatt Dinge zu deklarieren, die Buildfehler verursachen, gelten auch hier.
quelle