Visual Studio 2008 で大規模な C++ プロジェクトに取り組んでいますが、不要な#include
ディレクティブを含むファイルがたくさんあります。s が単なる成果物である場合#include
があり、それらを削除してすべてが正常にコンパイルされる場合もあります。また、クラスを前方宣言して #include を.cpp
ファイルに移動する場合もあります。これらのケースの両方を検出するための優れたツールはありますか?
20 に答える
不要なインクルード ファイルは表示されませんが、Visual Studio には、コンパイル時にすべてのインクルード ファイルのツリーを出力する設定 (ファイルを/showIncludes
右クリック) があります。これは、含める必要のないファイルを識別するのに役立ちます。.cpp
Properties->C/C++->Advanced
また、pimpl イディオムを参照して、ヘッダー ファイルの依存関係を減らし、削除できる不要なものを簡単に確認できるようにすることもできます。
PC Lintはこれに非常にうまく機能し、他のあらゆる種類の間抜けな問題も検出します。Visual Studioで外部ツールを作成するために使用できるコマンドラインオプションがありますが、VisualLintアドインの方が操作しやすいことがわかりました。VisualLintの無料バージョンでも役立ちます。しかし、PC-Lintを試してみてください。警告が多すぎないように設定するには少し時間がかかりますが、表示される内容に驚かれることでしょう。
これを目的とした新しい Clang ベースのツールinclude-what-you-useがあります。
!!免責事項!! 私は商用の静的分析ツール(PC Lintではない)に取り組んでいます。!!免責事項!!
単純な非解析アプローチにはいくつかの問題があります。
1)過負荷セット:
オーバーロードされた関数に、異なるファイルからの宣言が含まれている可能性があります。1つのヘッダーファイルを削除すると、コンパイルエラーではなく、別のオーバーロードが選択される可能性があります。その結果、セマンティクスがサイレントに変更され、後で追跡するのが非常に困難になる可能性があります。
2)テンプレートの専門分野:
オーバーロードの例と同様に、テンプレートに部分的または明示的な特殊化がある場合は、テンプレートを使用するときにそれらすべてを表示する必要があります。プライマリテンプレートの特殊化が異なるヘッダーファイルにある可能性があります。特殊化を使用してヘッダーを削除してもコンパイルエラーは発生しませんが、その特殊化が選択されている場合は、未定義の動作が発生する可能性があります。(参照:C ++関数のテンプレート特殊化の可視性)
'msalters'で指摘されているように、コードの完全な分析を実行すると、クラスの使用状況の分析も可能になります。ファイルの特定のパスを介してクラスがどのように使用されるかを確認することにより、クラスの定義(したがってそのすべての依存関係)を完全に削除するか、少なくともインクルードのメインソースに近いレベルに移動することができます。木。
私はそのようなツールを知りませんし、過去に書いてみようと思ったこともありましたが、これは解決が難しい問題であることがわかりました。
ソース ファイルに ah と bh が含まれているとします。ah は を含み#define USE_FEATURE_X
、bh は を使用し#ifdef USE_FEATURE_X
ます。がコメント アウトされている場合#include "a.h"
でも、ファイルはコンパイルされる可能性がありますが、期待どおりに動作しない可能性があります。これをプログラムで検出することは簡単ではありません。
これを行うツールが何であれ、ビルド環境も知る必要があります。ああのように見える場合:
#if defined( WINNT )
#define USE_FEATURE_X
#endif
ThenUSE_FEATURE_X
は が定義されている場合にのみ定義されるWINNT
ため、ツールはコンパイラ自体によって生成されたディレクティブと、ヘッダー ファイルではなくコンパイル コマンドで指定されたディレクティブを認識する必要があります。
Timmermans のように、私はこのためのツールに精通していません。しかし、Perl (または Python) スクリプトを作成して、各インクルード行を一度に 1 行ずつコメントアウトしてから、各ファイルをコンパイルしようとするプログラマーを知っています。
現在、Eric Raymondはこのためのツールを持っているようです。
Google のcpplint.pyには「使用するものを含める」ルールがありますが (他の多くのルールの中でも)、私が知る限り、「使用するものだけを含める」という規則はありません。それでも、それは有用です。
このトピック全般に興味がある場合は、Lakosの大規模C++ソフトウェア設計を確認することをお勧めします。少し時代遅れですが、含める必要のあるヘッダーの絶対最小値を見つけるなど、多くの「物理設計」の問題が発生します。私は他のどこかでこの種のことが議論されているのを見たことがありません。
C/C++ Include File Dependencies Watcherを使用してインクルード グラフを作成し、不要なインクルードを視覚的に見つけることができます。
インクルード マネージャーを試してみてください。Visual Studio に簡単に統合でき、不要なものを見つけるのに役立つインクルード パスを視覚化できます。内部的には Graphviz を使用していますが、他にも多くの優れた機能があります。また、業務用商品でありながら、非常に低価格です。
PC-Lintは確かにこれを行うことができます。これを行う簡単な方法の1つは、未使用のインクルードファイルだけを検出し、他のすべての問題を無視するように構成することです。これは非常に簡単です。メッセージ766(「モジュールでヘッダーファイルが使用されていません」)のみを有効にするには、コマンドラインにオプション-w0+e766を含めるだけです。
同じアプローチは、964(「モジュールで直接使用されていないヘッダーファイル」)や966(「モジュールで使用されていない間接インクルードヘッダーファイル」)などの関連メッセージでも使用できます。
FWIWこれについては、先週のブログ投稿http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318で詳しく説明しました。
ヘッダーファイルが一般的にで始まる場合
#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif
(#pragmaを1回使用するのとは対照的に)これを次のように変更できます。
#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else
#pragma message("Someheader.h superfluously included")
#endif
また、コンパイラはコンパイル中のcppファイルの名前を出力するため、少なくともどのcppファイルがヘッダーを複数回取り込む原因になっているのかがわかります。
次の#definesの一方または両方を追加すると、多くの場合不要なヘッダーファイルが除外され、特にWindows API関数を使用していないコードの場合、コンパイル時間が大幅に改善される可能性があります。
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
ビルド時間を短縮するために不要なファイルを削除する場合は、 cl.exe / MP、make -j、Xoreax IncrediBuild、distcc/ icecreamなど#include
を使用してビルドプロセスを並列化するために時間とお金を費やしたほうがよい場合があります。
もちろん、すでに並列ビルドプロセスがあり、それを高速化しようとしている場合は、必ず#include
ディレクティブをクリーンアップして、不要な依存関係を削除してください。
各インクルード ファイルから開始し、各インクルード ファイルには、それ自体をコンパイルするために必要なものだけが含まれていることを確認します。C++ ファイルに欠落しているインクルード ファイルは、C++ ファイル自体に追加できます。
インクルード ファイルとソース ファイルごとに、各インクルード ファイルを一度に 1 つずつコメント アウトし、コンパイルされるかどうかを確認します。
インクルード ファイルをアルファベット順に並べ替えることもお勧めします。これが不可能な場合は、コメントを追加してください。
まだ行っていない場合は、プリコンパイル済みヘッダーを使用して、変更しないすべてのもの(プラットフォームヘッダー、外部SDKヘッダー、またはプロジェクトの静的な既に完了した部分)を含めると、ビルド時間に大きな違いが生じます。
http://msdn.microsoft.com/en-us/library/szfdksca(VS.71).aspx
また、プロジェクトには遅すぎるかもしれませんが、プロジェクトをセクションに編成し、すべてのローカルヘッダーを1つの大きなメインヘッダーにまとめないことは、少し余分な作業が必要ですが、良い習慣です。
Eclipse CDT を使用する場合は、http://includator.comを試してインクルード構造を最適化できます。ただし、Includator は VC++ の定義済みインクルードについて十分に認識していない可能性があり、正しいインクルードで VC++ を使用するように CDT を設定することはまだ CDT に組み込まれていません。
既存の回答のいくつかは、難しいと述べています。前方宣言が適切なケースを検出するには完全なコンパイラが必要なので、これは確かに真実です。シンボルの意味を知らずに C++ を解析することはできません。そのためには、文法があいまいすぎます。特定の名前がクラス (前方宣言可能) または変数 (不可能) を指定するかどうかを知る必要があります。また、名前空間を認識する必要があります。
もう必要ないと思われる特定のヘッダー (string.h など) がある場合は、そのインクルードをコメントアウトして、すべてのインクルードの下に配置できます。
#ifdef _STRING_H_
# error string.h is included indirectly
#endif
もちろん、インターフェイス ヘッダーは別の #define 規則を使用して、CPP メモリに含まれていることを記録する場合があります。または、規則がない場合、このアプローチは機能しません。
その後、再構築します。次の 3 つの可能性があります。
それは大丈夫です。string.h はコンパイル クリティカルではなく、そのインクルードは削除できます。
#error トリップ。string.g が何らかの形で間接的に含まれていた string.h が必要かどうかはまだわかりません。必要な場合は、直接 #include する必要があります (以下を参照)。
他のコンパイル エラーが発生します。string.h が必要であり、間接的にインクルードされていないため、インクルードはそもそも正しいものでした。
.h または .c が別の .h を直接使用する場合の間接的なインクルードに依存することは、ほぼ確実にバグであることに注意してください。実際には、使用している他のヘッダーが必要とする限り、コードがそのヘッダーのみを必要とすることを約束しています。これはおそらくあなたが意図したものではありません。
ビルドの失敗の原因となるものを宣言するのではなく、動作を変更するヘッダーに関する他の回答で言及されている警告は、ここにも適用されます。
少し遅いかもしれませんが、あなたが望んでいたことを実行する WebKit perl スクリプトを見つけたことがあります。私が信じているいくつかの適応が必要になるでしょう(私はperlに精通していません)が、それはうまくいくはずです:
(トランクにはもうファイルがないため、これは古いブランチです)