用于查找未使用的包含标头的工具?

时间:2009-08-19 18:38:50

标签: c++ coding-style header-files

我知道PC-Lint可以告诉您包含但未使用的标头。有没有其他工具可以做到这一点,最好是在linux上?

我们拥有一个庞大的代码库,在过去的15年中已经看到了大量的功能,但是当功能从一个实现文件移动到另一个实现文件时,很少会删除剩余的#include指令,这使我们陷入了相当糟糕的状态。这点。显然,我可以去除所有#include指令,并让编译器告诉我哪些要重新包含,但是我宁愿反向解决问题 - 找到未使用的指令 - 而不是重建已使用的指令列表。

9 个答案:

答案 0 :(得分:30)

免责声明:我的日常工作是为一家开发静态分析工具的公司工作。

如果大多数(如果不是全部)静态分析工具没有某种形式的标头使用检查,我会感到惊讶。您可以使用this维基百科页面获取可用工具列表,然后通过电子邮件向公司询问。

在评估工具时可能会考虑以下几点:

对于函数重载,您希望所有包含重载的标题都可见,而不仅仅是包含重载决策选择的函数的标题:

// 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',那么代码将编译,但程序的语义已经改变。< / p>

当您具有部分和专业化时,类似的规则也适用。无论是否选择专业化都无关紧要,您需要确保所有专业化都可见:

// 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)'
}

对于过载示例,强力方法可能会导致程序仍然编译但行为不同。

您可以注意的另一种相关类型的分析是检查类型是否可以向前声明。请考虑以下事项:

// 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 &);

这种检查还会减少标头依赖性。

答案 1 :(得分:22)

这是一个执行此操作的脚本:

#!/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

答案 2 :(得分:5)

看看Dehydra

来自网站:

  

Dehydra是一款轻量级,可编写脚本的通用静态分析工具,能够对C ++代码进行特定于应用程序的分析。从最简单的意义上讲,Dehydra可以被认为是一种语义grep工具。

应该可以提出一个检查未使用的#include文件的脚本。

答案 3 :(得分:4)

谷歌的cppclean似乎在寻找未使用的头文件方面做得不错。我刚开始使用它。它会产生一些误报。它通常会在头文件中找到不必要的包含,但它不会告诉您的是您需要关联类的前向声明,并且需要将include移动到关联的源文件中。

答案 4 :(得分:3)

如果您正在使用Eclipse CDT,您可以尝试免费为Beta测试人员提供Includator(在撰写本文时),并自动删除多余的#includes或添加缺失的#include。

免责声明:我为开发Includator的公司工作,过去几个月一直在使用它。它对我来说效果很好,所以试一试: - )

答案 5 :(得分:1)

据我所知,没有一个(不是PC-Lint),这是一种耻辱,而且令人惊讶。我已经看到了做这个伪代码的建议(这基本上是自动化你的“艰苦过程”:

  每个cpp文件的


      每个标题包括
          注释掉包含           编译cpp文件
          if(compile_errors)
              取消注释标题
          否则
              从cpp删除标题包含

把它放在一个夜间的cron中,它应该完成这项工作,保持有问题的项目没有未使用的标题(你总是可以手动运行它,显然,但它需要很长时间才能执行)。唯一的问题是,当不包括标题不会产生错误,但仍会产生代码。

答案 6 :(得分:1)

我已经手动完成了这项工作,并且由于编译时间缩短,它的价值很短(哦,这是长期的吗? - 这需要很长时间):

  1. 为每个cpp文件解析较少的标头。
  2. 减少依赖性 - 整个世界不需要在重新编译之后 改为一个标题。
  3. 它也是一个递归过程 - 每个头文件都需要检查以查看是否有任何头文件包含在内。另外,有时您可以替换标题包含的前向声明。

    然后整个过程需要每隔几个月/每年重复一次,以便保留在剩余的标题之上。

    实际上,我对C ++编译器有点恼火,他们应该能够告诉你什么是不需要的 - 微软编译器可以告诉你在编译期间可以安全地忽略对头文件的更改。

答案 7 :(得分:0)

如果有人感兴趣,我只是在sourceforge上推出了一个小型的Java命令行工具。 因为它是用Java编写的,所以它显然可以在linux上运行。

项目的链接是https://sourceforge.net/projects/chksem/files/chksem-1.0/

答案 8 :(得分:-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在。目录编译所有cpp文件并包含-I.,然后重新编译将测试每个包含文件是否可以自己编译。使用goto-error在您喜欢的IDE中编译,并修复错误。

完成后,find . -name '*.h.cpp' -exec rm {} \;