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


96

Visual Studio 2008で大規模なC ++プロジェクトに取り組んでおり、不要な#includeディレクティブを含むファイルがたくさんあります。時々、#includesは単なるアーティファクトであり、すべてが削除されても問題なくコンパイルされ.cppます。他の場合では、クラスが前方宣言され、#includeがファイルに移動されます。これらの両方のケースを検出するための優れたツールはありますか?

回答:


50

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

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


1
/ showincludesは素晴らしいです。これを手動で行うことは、それなしでは困難でした。
シャンボリック2008

30

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


3
pc-lintでこれを行う方法についてのいくつかの説明は、riverblade.co.uk /…にあります
David Sykes


26

!!免責事項!! 商用の静的分析ツール(PC Lintではない)を使用しています。!!免責事項!!

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

1)オーバーロードセット:

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

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

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

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


@RichardCorden:あなたのソフトウェア(QA C ++)は高すぎる。
Xander Tulip

13
@XanderTulip:売り込みにならなければこれに対応するのは難しいので、事前に謝罪します。私見、あなたが考慮しなければならないのは、妥当なサイズのプロジェクトでこのようなもの(および他の多くの言語/制御フローのバグ)を見つけるのに優れたエンジニアがどれだけかかるかです。ソフトウェアが変更されると、同じタスクを何度も繰り返す必要があります。したがって、節約された時間を計算する場合、ツールのコストはおそらくそれほど大きくありません。
Richard Corden、2012年

10

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

ソースファイルにahとbhが含まれているとします。ahはを含み#define USE_FEATURE_X、bhはを使用し#ifdef USE_FEATURE_Xます。場合は#include "a.h"コメントアウトされ、あなたのファイルがまだコンパイルかもしれないが、何を期待しないことがあります。これをプログラムで検出することは簡単ではありません。

どんなツールでもこれはあなたのビルド環境を知る必要があります。次のような場合:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

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


9

Timmermansのように、私はこのためのツールに精通していません。しかし、Perl(またはPython)スクリプトを作成して、各インクルード行を1つずつコメント化してから、各ファイルをコンパイルするプログラマーを知っています。


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

Googleのcpplint.pyには、「使用したものを含める」というルールがありますが、私が知る限り、「使用したものだけを含める」というルールはありません。そうであっても、それは便利です。


これを読んだときは笑わなければならなかった。先月、私の上司は私たちのプロジェクトの1つでまさにこれを行いました。ヘッダーの削減には、いくつかの要因が含まれます。
Don Wakefield、

2
Macのcodewarriorは、これを行うためにスクリプトを組み込み、コメントアウト、コンパイル、エラーのコメント解除時に#includesの最後まで続行していました。これはファイルの先頭にある#includeでのみ機能しましたが、通常はそこにあります。それは完璧ではありませんが、物事を合理的に健全に保ちます。
slycrel 2010年

5

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


4

付けマネージャに含める試みを。Visual Studioに簡単に統合し、インクルードパスを視覚化して、不要なものを見つけるのに役立ちます。内部的にはGraphvizを使用していますが、さらに多くの優れた機能があります。しかも市販品ですが、価格がとても安いです。



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ファイルがヘッダーを何度も呼び出しているかを知ることができます。


12
ヘッダーを複数回含めても問題ないと思います。使用するものをインクルードすることは良いことであり、インクルードファイルに依存しないでください。OPが望んでいるのは、実際には使用されていない#includeを見つけることだと思います。
Ryan Ginstrom 2010

12
IMOが積極的に間違ったことを行う。ヘッダーは、他のヘッダーがないと機能しない場合に含める必要があります。そして、あなたが持っている場合A.hB.hの両方がに依存していることC.hと、あなたは、A.hB.hあなたは両方を必要とするので、あなたは、なりますが含まC.h回が、コンパイラはそれを二時間をスキップしますので、それは、罰金だとあなたはしなかった場合、あなたは覚えておく必要があるだろう常にC.h前に含めるA.hB.h、はるかに役に立たない包含に終わります。
Jan Hudec、2011

5
コンテンツは正確です。これは、複数回含まれるヘッダーを見つけるための優れたソリューションです。しかし、元の質問はこれで答えられておらず、いつこれが良いアイデアになるかは想像できません。Cppファイルには、ヘッダーがどこかに先に含まれていても、依存するすべてのヘッダーを含める必要があります。プロジェクトをコンパイル順序固有にしたくない、または別のヘッダーに必要なヘッダーが含まれると想定したくない。
jaypb 2013年

3

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

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

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


2

#includeビルド時間を短縮するために不要なファイルを削除する場合は、cl.exe / MPmake -jXoreax IncrediBuild、distcc / icecreamなどを使用して、ビルドプロセスの並列化に時間とお金をかける方がよいでしょう。

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


2

各インクルードファイルから始めて、各インクルードファイルに、それ自体のコンパイルに必要なものだけが含まれていることを確認します。C ++ファイルで欠落しているインクルードファイルは、C ++ファイル自体に追加できます。

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

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


2
非常に多数の実装ファイルが含まれている場合、このコメントがどれほど実用的かはわかりません。
ソニー

1

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

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

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


1
両方の必要はありません-VC_EXTRALEANはWIN32_LEAN_AND_MEANを定義します
Aidan Ryan

1

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

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

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


プリコンパイル済みヘッダーの優れた説明:cygnus-software.com/papers/precompiledheaders.html自動生成されたプリコンパイル済みヘッダーがVisualStudioの最新バージョンで壊れているかどうかはわかりませんが、チェックする価値があります。)
idbrii

1

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


1

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

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


0

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


「必要な#includeを決定することは、停止の問題を解決することと同じです。頑張ってください:)」もちろん、ヒューリスティックを使用できますが、これを行うフリーソフトウェアについては知りません。
ポルジュ2008

0

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

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

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


0

もう必要ないと思われる特定のヘッダー(string.hなど)がある場合は、そのインクルードをコメント化して、すべてのインクルードの下に配置できます。

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

もちろん、インターフェイスヘッダーが別の#define規則を使用して、CPPメモリへの組み込みを記録する場合があります。または、規約がない場合、このアプローチは機能しません。

その後、再構築します。3つの可能性があります。

  • それは大丈夫です。string.hはコンパイルに不可欠ではなく、そのインクルードは削除できます。

  • #エラーがトリップします。string.gは間接的に何らかの方法で含まれています。string.hが必要かどうかはまだわかりません。必要な場合は、直接#includeする必要があります(以下を参照)。

  • その他のコンパイルエラーが発生します。string.hが必要であり、間接的にインクルードされていないため、インクルードは最初は正しいものでした。

.hまたは.cが別の.hを直接使用する場合の間接的なインクルードによっては、ほぼ間違いなくバグであることに注意してください。使用している他のヘッダーが必要とする限り、コードはそのヘッダーのみを必要とすることを実際に約束しています。これはおそらくあなたが意図したものではありません。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.