[C++] Wie sollte ich unnötige # Include-Dateien in einem großen C ++ - Projekt erkennen?


Answers

PC Lint funktioniert dafür ganz gut und findet auch für dich viele andere alberne Probleme. Es verfügt über Befehlszeilenoptionen, die verwendet werden können, um externe Tools in Visual Studio zu erstellen, aber ich habe festgestellt, dass das Visual Lint- Add-In einfacher zu verwenden ist. Auch die kostenlose Version von Visual Lint hilft. Aber geben Sie PC-Lint eine Chance. Es zu konfigurieren, so dass es nicht zu viele Warnungen gibt, braucht ein bisschen Zeit, aber Sie werden erstaunt sein, wie es aussieht.

Question

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önnten Klassen nach vorne deklariert werden und #include könnte in die .cpp Datei .cpp werden. Gibt es gute Werkzeuge, um beide Fälle zu erkennen?




Einige der vorhandenen Antworten geben an, dass es schwierig ist. Das ist in der Tat richtig, weil Sie einen vollständigen Compiler benötigen, um die Fälle zu erkennen, in denen eine Vorwärtsdeklaration angebracht wäre. Sie können C ++ nicht analysieren, ohne zu wissen, was die Symbole bedeuten. Die Grammatik ist dafür einfach zu zweideutig. Sie müssen wissen, ob ein bestimmter Name eine Klasse (könnte nach vorne deklariert sein) oder eine Variable (kann nicht). Außerdem müssen Sie namespace-aware sein.




Das Hinzufügen eines oder beider der folgenden #defines wird oft unnötige Header-Dateien ausschließen und kann die Kompilierungszeiten wesentlich verbessern, besonders wenn der Code, der nicht Windows API verwendet, funktioniert.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

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







Wie Timmermans kenne ich keine Werkzeuge dafür. Aber ich habe Programmierer gekannt, 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 .

Googles cpplint.py hat eine "include what you use" -Regel (neben vielen anderen), aber soweit ich das cpplint.py kann, gibt es keine " nur was du verwendest". Trotzdem kann es nützlich sein.




!!HAFTUNGSAUSSCHLUSS!! Ich arbeite an einem kommerziellen statischen Analyse-Tool (nicht PC Lint). !!HAFTUNGSAUSSCHLUSS!!

Es gibt mehrere Probleme mit einem einfachen Nicht-Parsing-Ansatz:

1) Überladungssätze:

Es ist möglich, dass eine überladene Funktion Deklarationen hat, die aus verschiedenen Dateien stammen. Es kann sein, dass das Entfernen einer Header-Datei dazu führt, dass eine andere Überladung gewählt wird als ein Kompilierungsfehler! Das Ergebnis wird eine stille Änderung in der Semantik sein, die danach sehr schwer zu finden ist.

2) Vorlagenspezialisierungen:

Ähnlich wie bei dem Überladungsbeispiel möchten Sie bei einer teilweisen oder expliziten Spezialisierung für eine Vorlage alle anzeigen, dass sie bei der Verwendung der Vorlage sichtbar sind. Es kann sein, dass Spezialisierungen für die primäre Vorlage in verschiedenen Header-Dateien sind. Das Entfernen der Kopfzeile mit der Spezialisierung führt nicht zu einem Kompilierungsfehler, kann jedoch zu einem nicht definierten Verhalten führen, wenn diese Spezialisierung ausgewählt wäre. (Siehe: Sichtbarkeit der Template-Spezialisierung der C ++ - Funktion )

Wie von "msalters" gezeigt, ermöglicht die Durchführung einer vollständigen Analyse des Codes auch eine Analyse der Klassennutzung. Durch die Überprüfung, wie eine Klasse über einen bestimmten Dateipfad verwendet wird, ist es möglich, dass die Definition der Klasse (und damit aller Abhängigkeiten) vollständig entfernt oder zumindest auf eine Ebene verschoben werden kann, die näher an der Hauptquelle im Include liegt Baum.




Wenn Ihre Header-Dateien im Allgemeinen mit beginnen

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

(Im Gegensatz zur einmaligen Verwendung von #pragma) könntest du das ändern zu:

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

Und da der Compiler den Namen der cpp-Datei ausgibt, die kompiliert wird, würden Sie zumindest wissen, welche cpp-Datei den Header mehrmals eingefügt hat.




Wenn Sie unnötige #include Dateien entfernen #include , um die Build-Zeiten zu verringern, könnten Sie Ihre Zeit und Ihr Geld besser dafür verwenden, Ihren Build-Prozess mit cl.exe / MP , make -j , Xoreax IncrediBuild , distcc / icecream usw. zu parallelisieren.

Natürlich, wenn Sie bereits einen parallelen Build-Prozess haben und Sie immer noch versuchen, es zu beschleunigen, dann bereinigen Sie auf alle Fälle Ihre #include Anweisungen und entfernen Sie diese unnötigen Abhängigkeiten.




Wenn Sie mit Eclipse CDT arbeiten würden, könnten Sie http://includator.com ausprobieren, um Ihre Include-Struktur zu optimieren. Es ist jedoch möglich, dass Includator nicht genug über die vordefinierten Includes von VC ++ weiß und dass CDT für die Verwendung von VC ++ mit korrekten Includes eingerichtet wurde, die noch nicht in CDT integriert sind.




Wenn es einen bestimmten Header gibt, von dem Sie denken, dass er nicht mehr benötigt wird (zB string.h), können Sie das Include auskommentieren und dann unter alle Includes setzen:

#ifdef _STRING_H_
#  error string.h is included indirectly
#endif

Natürlich verwenden Ihre Interface-Header möglicherweise eine andere # define-Konvention, um deren Aufnahme in den CPP-Speicher aufzuzeichnen. Oder keine Konvention, in diesem Fall wird dieser Ansatz nicht funktionieren.

Dann neu aufbauen. Es gibt drei Möglichkeiten:

  • Es baut sich gut auf. string.h war nicht kompilierungskritisch und das Include dafür kann entfernt werden.

  • Der # Fehler löst aus. string.g wurde indirekt eingefügt 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 einbezogen, daher war das Include zunächst richtig.

Beachten Sie, dass abhängig von der indirekten Aufnahme, wenn Ihre .h oder .c direkt ein anderes verwendet. H ist mit ziemlicher Sicherheit ein Fehler: Sie versprechen tatsächlich, dass Ihr Code nur diesen Header benötigt, solange ein anderer Header Sie benötigt, was wahrscheinlich nicht das ist, was du meintest.

Die in anderen Antworten erwähnten Vorbehalte bezüglich der Header, die das Verhalten modifizieren, anstatt die Dinge zu deklarieren, die Build-Fehler verursachen, gelten auch hier.




Links