75

PC-Lintは、含まれているが使用されていないヘッダーについて教えてくれることを知っています。できればLinuxでこれを行うことができる他のツールはありますか?

過去 15 年間に多くの機能が移動してきた大規模なコードベースがありますが、機能が 1 つの実装ファイルから別の実装ファイルに移動するときに、残りの #include ディレクティブが削除されることはめったになく、この時点でかなり混乱しています。もちろん、すべての #include ディレクティブを削除して、どのディレクティブを再インクルードするかをコンパイラーに指示させるという骨の折れる作業を行うこともできますが、使用済みのもののリストを再構築するのではなく、逆に問題を解決し、未使用のものを見つけたいと考えています。

4

9 に答える 9

31

免責事項:私の本業は、静的分析ツールを開発する会社で働いています。

ほとんどの (すべてではないにしても) 静的分析ツールに何らかの形式のヘッダー使用状況チェックがなかったら、私は驚かれることでしょう。このウィキペディアのページを使用して、利用可能なツールのリストを取得し、企業にメールして問い合わせることができます。

ツールを評価する際に考慮すべきいくつかのポイント:

関数のオーバーロードの場合、オーバーロードの解決によって選択された関数を含むヘッダーだけでなく、オーバーロードを含むすべてのヘッダーを表示する必要があります。

// f1.h
void foo (char);

// f2.h
void foo (int);


// bar.cc
#include "f1.h"
#include "f2.h"

int main ()
{
  foo (0);  // Calls 'foo(int)' but all functions were in overload set
}

力ずくのアプローチを取る場合は、最初にすべてのヘッダーを削除してから、コンパイルされるまでそれらを再度追加します。「f1.h」が最初に追加された場合、コードはコンパイルされますが、プログラムのセマンティクスは変更されています。

部分的および特殊化がある場合、同様のルールが適用されます。スペシャライゼーションが選択されているかどうかは関係ありません。すべてのスペシャライゼーションが表示されていることを確認する必要があります。

// f1.h
template <typename T>
void foo (T);

// f2.h
template <>
void foo (int);

// bar.cc
#include "f1.h"
#include "f2.h"


int main ()
{
  foo (0);  // Calls specialization 'foo<int>(int)'
}

オーバーロードの例に関して言えば、ブルート フォース アプローチでは、コンパイルはできるが動作が異なるプログラムが生成される可能性があります。

注目できるもう 1 つの関連する分析の種類は、型を前方宣言できるかどうかを確認することです。次の点を考慮してください。

// A.h
class A { };

// foo.h
#include "A.h"
void foo (A const &);

// bar.cc
#include "foo.h"

void bar (A const & a)
{
  foo (a);
}

上記の例では、'A' の定義は必要ないため、ヘッダー ファイル 'foo.h' を変更して、'A' のみの前方宣言を行うことができます。

// foo.h
class A;
void foo (A const &);

この種のチェックは、ヘッダーの依存関係も減らします。

于 2009-08-20T09:23:44.290 に答える
23

これを行うスクリプトは次のとおりです。

#!/bin/bash
# prune include files one at a time, recompile, and put them back if it doesn't compile
# arguments are list of files to check
removeinclude() {
    file=$1
    header=$2
    perl -i -p -e 's+([ \t]*#include[ \t][ \t]*[\"\<]'$2'[\"\>])+//REMOVEINCLUDE $1+' $1
}
replaceinclude() {
   file=$1
   perl -i -p -e 's+//REMOVEINCLUDE ++' $1
}

for file in $*
do
    includes=`grep "^[ \t]*#include" $file | awk '{print $2;}' | sed 's/[\"\<\>]//g'`
    echo $includes
    for i in $includes
    do
        touch $file # just to be sure it recompiles
        removeinclude $file $i
        if make -j10 >/dev/null  2>&1;
        then
            grep -v REMOVEINCLUDE $file > tmp && mv tmp $file
            echo removed $i from $file
        else
            replaceinclude $file
            echo $i was needed in $file
        fi
    done
done
于 2011-08-21T00:13:51.673 に答える
5

Googleのcppcleanは、未使用のヘッダーファイルを見つけるのに適切な仕事をしているようです。使い始めたばかりです。いくつかの誤検知が発生します。多くの場合、ヘッダーファイルに不要なインクルードが含まれていますが、関連付けられたクラスの前方宣言が必要であり、インクルードを関連付けられたソースファイルに移動する必要があることはわかりません。

于 2011-08-18T17:38:35.077 に答える
5

デヒドラを見てください。

ウェブサイトから:

Dehydra は、C++ コードのアプリケーション固有の分析が可能な、軽量でスクリプト可能な汎用静的分析ツールです。最も単純な意味で、Dehydra はセマンティック grep ツールと考えることができます。

未使用の #include ファイルをチェックするスクリプトを作成できるはずです。

于 2010-02-23T20:52:53.720 に答える
3

Eclipse CDT を使用している場合は、(この記事の執筆時点で) ベータ テスターに​​無料で提供されているIncludatorを試すことができ、余分な #includes を自動的に削除したり、不足しているものを追加したりできます。

免責事項: 私は Includator を開発している会社で働いており、過去数か月間使用しています。それは私にとって非常にうまくいくので、試してみてください:-)

于 2011-06-01T13:36:00.007 に答える
1

私はこれを手動で行いましたが、コンパイル時間が短縮されたため、短期的には価値があります(ああ、長期ですか?-長い時間がかかります):

  1. 各 cpp ファイルで解析するヘッダーが少なくなります。
  2. 依存関係が少ない - 1 つのヘッダーを変更した後、全世界を再コンパイルする必要はありません。

これも再帰的なプロセスです。残っている各ヘッダー ファイルを調べて、含まれているヘッダー ファイルを削除できるかどうかを確認する必要があります。さらに、ヘッダー インクルードを前方宣言に置き換えることができる場合もあります。

その後、残りのヘッダーを管理するために、プロセス全体を数か月/年ごとに繰り返す必要があります。

実際、私は C++ コンパイラに少し悩まされています。必要でないものを教えてくれるはずです。Microsoft コンパイラは、コンパイル中にヘッダー ファイルへの変更を安全に無視できるタイミングを教えてくれます。

于 2009-08-19T19:43:29.790 に答える
1

私の知る限り、(PC-Lint 以外の) ものはありません。これは残念なことであり、驚くべきことです。この少しの疑似コードを実行するという提案を見てきました(これは基本的に「骨の折れるプロセス」を自動化しています:

すべての cpp ファイルごとに すべて の
ヘッダーにインクルード インクルード を
コメントアウトして cpp
ファイルをコンパイルする



それを夜間のcronに入れると、問題のプロジェクトに未使用のヘッダーがないように保たれます(もちろん、いつでも手動で実行できますが、実行には時間がかかります)。唯一の問題は、ヘッダーを含めなくてもエラーは発生しないが、コードが生成される場合です。

于 2009-08-19T18:56:42.177 に答える
-1

使用されていないインクルードを削除するためのほとんどのアプローチは、最初に各ヘッダー ファイルが独自にコンパイルされることを確認するとうまく機能します。私はこれを次のように比較的迅速に行いました(タイプミスをお詫びします-これは自宅で入力しています:

find . -name '*.h' -exec makeIncluder.sh {} \;

含まれる場所makeIncluder.sh:

#!/bin/sh
echo "#include \"$1\"" > $1.cpp

各ファイル./subdir/classname.hに対して、このアプローチは./subdir/classname.h.cpp、行を含むという名前のファイルを作成します

#include "./subdir/classname.h"

あなたmakefileの場合。directory はすべての cpp ファイルと contains-I.をコンパイルし、再コンパイルするだけで、すべてのインクルード ファイルが単独でコンパイルできることをテストします。goto-error を使用してお気に入りの IDE でコンパイルし、エラーを修正します。

終わったら、find . -name '*.h.cpp' -exec rm {} \;

于 2013-09-19T02:17:25.683 に答える