web-dev-qa-db-ja.com

大規模なC ++プロジェクトで不要な#includeファイルを検出するにはどうすればよいですか?

私はVisual Studio 2008で大規模なC++プロジェクトに取り組んでいますが、不要な#includeディレクティブを持つファイルがたくさんあります。場合によっては#includesが単なるアーティファクトであり、すべて削除すると正常にコンパイルされます。また、他の場合はクラスを前方宣言し、#includeを.cppファイルに移動できます。これらの両方のケースを検出するための良いツールはありますか?

93
shambolic

不要なインクルードファイルは表示されませんが、Visual Studioには、コンパイル時にすべてのインクルードファイルのツリーを出力する設定/showIncludes.cppファイルを右クリック、Properties->C/C++->Advanced)があります。これは、含める必要のないファイルを識別するのに役立ちます。

また、pimplのイディオムを見て、ヘッダーファイルの依存関係を減らして、削除できるデータを簡単に確認できるようにすることもできます。

47
Eclipse

PC Lint はこれに対して非常にうまく機能し、他のあらゆる種類の間抜けな問題もあなたのために見つけます。 Visual Studioで外部ツールを作成するために使用できるコマンドラインオプションがありますが、 Visual Lint アドインを使用する方が簡単であることがわかりました。 Visual Lintの無料バージョンでも役立ちます。しかし、PC-Lintを試してみてください。あまりにも多くの警告が出ないように設定するには少し時間がかかりますが、それがどうなるか驚くでしょう。

29
Joe

これを行うことを目的とした新しいClangベースのツール include-what-you-use があります。

28
Josh Kelley

!!免責事項!!私は商用の静的解析ツール(PC Lintではありません)に取り組んでいます。 !!免責事項!!

単純な非解析アプローチにはいくつかの問題があります。

1)過負荷セット:

オーバーロードされた関数には、異なるファイルからの宣言が含まれている可能性があります。 1つのヘッダーファイルを削除すると、コンパイルエラーではなく、異なるオーバーロードが選択される可能性があります。その結果、セマンティクスのサイレントな変更が行われ、その後の追跡が非常に困難になる場合があります。

2)テンプレートの専門化:

オーバーロードの例と同様に、テンプレートの部分的または明示的な特殊化がある場合は、テンプレートが使用されるときにそれらがすべて表示されるようにします。プライマリテンプレートの特殊化が異なるヘッダーファイルにある可能性があります。特殊化されたヘッダーを削除してもコンパイルエラーは発生しませんが、その特殊化が選択された場合、未定義の動作が発生する可能性があります。 (参照: C++関数のテンプレート特化の可視性

「msalters」で指摘されているように、コードの完全な分析を実行すると、クラスの使用状況の分析も可能になります。ファイルの特定のパスを介してクラスがどのように使用されるかを確認することにより、クラスの定義(したがって、そのすべての依存関係)を完全に削除するか、少なくともincludeのメインソースにより近いレベルに移動することができます木。

26
Richard Corden

私はそのようなツールを知りませんし、過去にそれを書くことを考えましたが、これは解決が難しい問題であることがわかりました。

ソースファイルにa.hとb.hが含まれているとします。 a.hには#define USE_FEATURE_Xが含まれ、b.hには#ifdef USE_FEATURE_Xが使用されます。 #include "a.h"がコメント化されている場合、ファイルはコンパイルできますが、期待どおりに動作しない場合があります。これを検出するプログラムでは簡単ではありません。

どのツールでも、ビルド環境を知る必要があります。 a.hが次のように見える場合:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

USE_FEATURE_XWINNTが定義されている場合にのみ定義されるため、ツールはコンパイラーによって生成されるディレクティブと、ヘッダーではなくコンパイルコマンドで指定されるディレクティブを知る必要があります。ファイル。

10
Graeme Perrow

Timmermansのように、私はこのためのツールに慣れていません。ただし、Perl(またはPython)スクリプトを記述して、各インクルード行を一度に1行ずつコメントアウトしてから、各ファイルをコンパイルするプログラマーを知っています。


エリック・レイモンド このためのツールを持っている のようです。

Googleの cpplint.py には(使用するものを含める)ルールがありますが、私が知る限り、「includeonly使用するもの。」そうであっても、それは便利です。

9
Max Lybbert

このトピック全般に興味がある場合は、Lakosの Large Scale C++ Software Design をご覧ください。少し古くなっていますが、含める必要のあるヘッダーの絶対最小値を見つけるなど、多くの「物理設計」の問題が発生します。私は、この種のことを他のどこでも議論したのを見たことはありません。

5
Adrian

Include Manager を試してください。 Visual Studioに簡単に統合し、インクルードパスを視覚化して、不要なものを見つけるのに役立ちます。内部的にはGraphvizを使用していますが、さらに多くのクールな機能があります。そして、それは商用製品ですが、非常に低価格です。

4
Alex

C/C++ Include File Dependencies Watcher を使用してインクルードグラフを作成し、視覚的に不要なインクルードを見つけることができます。

4
Vladimir

PC-Lintは確かにこれを行うことができます。これを行う簡単な方法の1つは、未使用のインクルードファイルのみを検出し、他のすべての問題を無視するように構成することです。これは非常に簡単です-メッセージ766(「モジュールで使用されていないヘッダーファイル」)のみを有効にするには、コマンドラインにオプション-w0 + e766を含めるだけです。

同じアプローチは、964(「モジュールで直接使用されていないヘッダーファイル」)や966(「モジュールで使用されていない間接的に含まれたヘッダーファイル」)などの関連メッセージでも使用できます。

FWIW http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318 のブログ投稿でこれについて詳しく書いた。

3

通常、ヘッダーファイルが

#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ファイルがヘッダーを複数回持ち込んでいるかを知ることができます。

3
Sam

ビルド時間を短縮するために不要な#includeファイルを削除する場合、 cl.exe/MPmake -jXoreax IncrediBuild 、 distcc / icecream など.

もちろん、すでに並列ビルドプロセスがあり、それをさらに高速化しようとしている場合は、必ず#includeディレクティブをクリーンアップし、それらの不要な依存関係を削除してください。

2
bk1e

各インクルードファイルから開始し、各インクルードファイルにコンパイルに必要なもののみが含まれるようにします。 C++ファイルに不足しているインクルードファイルは、C++ファイル自体に追加できます。

各インクルードファイルとソースファイルについて、各インクルードファイルを一度に1つずつコメント化し、コンパイルされるかどうかを確認します。

また、インクルードファイルをアルファベット順に並べ替えることをお勧めします。これが不可能な場合は、コメントを追加します。

2
selwyn

次の#definesの一方または両方を追加すると、多くの場合、不要なヘッダーファイルが除外され、特にWindows API関数を使用していないコードの場合、コンパイル時間が大幅に改善される場合があります。

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

http://support.Microsoft.com/kb/166474 を参照してください

1
Roger Nelson

まだコンパイルしていない場合は、プリコンパイル済みヘッダーを使用して、変更しないもの(プラットフォームヘッダー、外部SDKヘッダー、またはプロジェクトの静的に完了した部分)をすべて含めると、ビルド時間が大幅に異なります。

http://msdn.Microsoft.com/en-us/library/szfdksca(VS.71).aspx

また、プロジェクトには手遅れかもしれませんが、プロジェクトをセクションに整理し、すべてのローカルヘッダーを1つの大きなメインヘッダーにまとめないことは良い習慣ですが、少し余分な作業が必要です。

1
anon6439

最新のJetbrains IDEのCLionは、現在のファイルで使用されていないインクルードを自動的に(グレーで)表示します。

IDEから未使用のすべてのインクルード(および関数、メソッドなど)のリストを取得することもできます。

Eclipse CDTを使用する場合は、 http://includator.com を試して、インクルード構造を最適化できます。ただし、IncludatorはVC++の事前定義されたインクルードについて十分に理解していない可能性があり、正しいインクルードでVC++を使用するためのCDTのセットアップはまだCDTに組み込まれていません。

1
PeterSom

少し遅れるかもしれませんが、私はかつてあなたが望んでいたことをしたWebKit Perlスクリプトを見つけました。私は信じるいくつかの適応を必要とします(私はPerlに精通していません)が、トリックを行う必要があります:

http://trac.webkit.org/browser/branches/old/safari-3-2-branch/WebKitTools/Scripts/find-extra-includes

(これはトランクにファイルがないため古いブランチです)

0
rubenvb

不要になったと思う特定のヘッダー(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を直接使用する場合の間接的なインクルードに依存することはほぼ間違いなくバグであることに注意してください。それはおそらくあなたが意図したものではありません。

ビルドの失敗の原因となるものを宣言するのではなく、動作を変更するヘッダーに関する他の回答で言及されている警告もここに適用されます。

0
Britton Kerin

既存の回答のいくつかは、難しいと述べています。フォワード宣言が適切である場合を検出するために完全なコンパイラが必要なので、それは実際に真実です。シンボルの意味を知らずにC++を解析することはできません。文法はそのために曖昧すぎます。特定の名前がクラスに名前を付けるか(前方宣言できる)、変数に名前を付けるか(できない)を知る必要があります。また、名前空間を認識する必要があります。

0
MSalters