在C / C ++中检测多余的#includes?

时间:2009-03-05 13:43:54

标签: c++ c refactoring include dependencies

我经常发现文件的标题部分一直变得越来越大,但它永远不会变小。在源文件的整个生命周期中,类可能已经移动并被重构,很可能有很多#includes不需要再存在。将它们留在那里只会延长编译时间并增加不必要的编译依赖性。试图找出仍然需要的东西可能会非常繁琐。

是否有某种工具可以检测到多余的#include指令并建议哪些可以安全删除? lint可能这样做吗?

19 个答案:

答案 0 :(得分:157)

Google的cppclean(链接到:downloaddocumentation)可以找到几类C ++问题,现在可以找到多余的#includes。

还有一个基于Clang的工具include-what-you-use可以做到这一点。包括你使用什么甚至可以建议前向声明(所以你不必#include这么多)并可选择为你清理你的#includes。

当前版本的Eclipse CDT也内置了此功能:在Source菜单下,单击Organize Includes将按字母顺序排列#include,添加Eclipse认为您正在使用的任何标头,而不直接包含它们,以及注释出任何你认为不需要的标题。但是,此功能并非100%可靠。

答案 1 :(得分:58)

同时查看include-what-you-use,它解决了类似的问题。

答案 2 :(得分:40)

这不是自动的,但doxygen将为#included文件生成依赖关系图。你必须在视觉上仔细阅读它们,但它们对于了解正在使用什么的图片非常有用。

答案 3 :(得分:24)

检测多余包含的问题是它不能只是一个类型依赖检查器。多余的包含是一个文件,它不会为编译提供任何有价值的文件不会改变其他文件所依赖的其他项目。头文件可以通过多种方式改变编译,例如通过定义常量,重新定义和/或删除已使用的宏,添加一个命名空间,该命名空间可以在某种程度上改变名称的查找。为了检测命名空间之类的项目,您需要的不仅仅是预处理器,实际上您几乎需要一个完整的编译器。

Lint更像是一种风格检查器,当然不具备这种全部功能。

我认为你会发现检测多余包含的唯一方法是删除,编译和运行套件。

答案 4 :(得分:15)

我认为PCLint会做到这一点,但距离我看过它已经有几年了。你可以看一下。

我查看了this blog,作者谈了一些关于配置PCLint以查找未使用的包含的内容。可能值得一看。

答案 5 :(得分:7)

CScout重构浏览器可以在C(不幸的是不是C ++)代码中检测多余的include指令。您可以在this期刊文章中找到有关其工作原理的说明。

答案 6 :(得分:6)

您可以编写一个快速脚本来擦除单个#include指令,编译项目,并在没有编译错误的情况下将名称记录在#include中,并将其从中删除。

让它在夜间运行,第二天你将有一个100%正确的包含文件列表,你可以删除。

有时蛮力只是起作用: - )


编辑,有时不会:-)。以下是评论中的一些信息:

  1. 有时您可以分别删除两个头文件,但不能同时删除两个头文件。解决方案是在运行期间删除头文件,而不是将它们带回来。这将找到一个可以安全删除的文件列表,尽管可能有一个解决方案包含更多要删除的文件,但该算法无法找到。 (这是对要删除的包含文件空间的贪婪搜索。它只能找到本地最大值)
  2. 如果您根据某些#ifdef重新定义了一些宏,则行为可能会有细微的变化。我认为这些是非常罕见的情况,作为构建一部分的单元测试应该能够捕获这些变化。

答案 7 :(得分:5)

抱歉(重新)发布此处,人们通常不会扩展评论。

检查我对crashmstr的评论,FlexeLint / PC-Lint会为你做这个。信息性消息766.我的手册(版本8.0)的第11.8.1节讨论了这一点。

此外,这很重要,请继续迭代,直到消息消失。换句话说,在删除未使用的标头后,重新运行lint,一旦删除一些不需要的标头,更多的头文件可能已经变得“不需要”。 (这可能听起来很愚蠢,慢慢阅读并解析它,这是有道理的。)

答案 8 :(得分:5)

我从来没有找到一个完整的工具来完成你所要求的。我使用的最接近的是IncludeManager,它会对您的标题包含树进行绘制,以便您可以在视觉上发现仅包含在一个文件和圆形标题内容中的标题。

答案 9 :(得分:4)

如果您正在使用Eclipse CDT,您可以尝试免费为Beta测试人员提供http://includator.com(在撰写本文时),并自动删除多余的#includes或添加缺失的#include。对于那些拥有FlexeLint或PC-Lint且正在使用Elicpse CDT的用户,http://linticator.com可能是一个选项(也可以免费进行beta测试)。虽然它使用了Lint的分析,但它提供了快速修复,可以自动删除多余的#include语句。

答案 10 :(得分:2)

This article解释了使用Doxygen解析#include删除的技巧。这只是一个perl脚本,所以它很容易使用。

答案 11 :(得分:2)

有一个免费工具Include File Dependencies Watcher可以集成到visual studio中。它以红色显示多余的#includes。

答案 12 :(得分:2)

我尝试过使用Flexelint(PC-Lint的unix版本)并且结果有些混乱。这很可能是因为我正在研究一个非常庞大而棘手的代码库。我建议仔细检查报告为未使用的每个文件。

主要担心是误报。将包含相同标头的多个包含报告为不需要的标头。这很糟糕,因为Flexelint没有告诉您标题包含在哪个行或之前包含的位置。

自动化工具可以解决这个问题的方法之一:

在A.hpp:

class A { 
  // ...
};

在B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

在C.cpp:

#include "C.hpp"  

#include "B.hpp"  // <-- Unneeded, but lint reports it as needed
#include "A.hpp"  // <-- Needed, but lint reports it as unneeded

如果你盲目地关注Flexelint的消息,你就会搞砸你的#include依赖关系。有更多的病理情况,但基本上你需要自己检查标题以获得最佳效果。

我强烈推荐来自内部博客的Physical Structure and C++上的这篇文章。他们建议采用全面的方法清理#include混乱:

  

准则

     

这是Lakos出版的一套精心设计的指南,可以最大限度地减少文件之间的物理依赖关系。我已经使用它们多年了,我一直对结果非常满意。

     
      
  1. 每个cpp文件首先包含自己的头文件。 [剪断]
  2.   
  3. 头文件必须包含解析它所需的所有头文件。 [剪断]
  4.   
  5. 头文件应该具有解析它所需的最少数量的头文件。 [剪断]
  6.   

答案 13 :(得分:1)

有两种类型的多余#include文件:

  1. 实际上不需要的头文件 所有
  2. 模块(.c,.cpp)
  3. 模块需要头文件 但不止一次,直接或间接地被包括在内。
  4. 根据我的经验,有两种方法可以很好地检测它:

    • gcc -H或cl.exe / showincludes(解决问题2)

      在现实世界中, 你可以在make之前导出CFLAGS = -H, 如果所有Makefile都没有覆盖 CFLAGS选项。或者就像我用过的那样,你 可以创建一个cc / g ++包装器来添加-H 强制每个调用的选项 $(CC)和$(CXX)。并且在前面 包装器的目录到$ PATH 变量,然后你的make将全部 使用你的包装命令。的 当然你的包装应该调用 真正的gcc编译器。这招数 如果您的Makefile使用,则需要更改 gcc直接。而不是$(CC)或 $(CXX)或暗示规则。

      您还可以通过使用命令行进行调整来编译单个文件。但是如果你想清理整个项目的标题。您可以通过以下方式捕获所有输出:

      make clean

      make 2&gt;&amp; 1 | tee result.txt

    • PC-Lint / FlexeLint(解决问题 1和2)

      确保添加+ e766选项,此警告是关于: 未使用的头文件。

      pclint / flint -vf ...

      这将导致pclint输出包含头文件,嵌套头文件将适当缩进。

答案 14 :(得分:1)

也许有点晚了,但我曾经发现一个WebKit perl脚本可以完成您想要的任务。它需要一些适应我相信(我不是很精通perl),但它应该做的伎俩:

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

(这是一个旧分支,因为trunk不再有文件)

答案 15 :(得分:1)

结束讨论:c ++预处理器完成了turing。这是一个语义属性,包含是否是多余的。因此,从莱斯定理得出,包含是否是多余的是不可判定的。 CAN&#39;是一个程序,(总是正确地)检测包含是否是多余的。

答案 16 :(得分:1)

这是一个简单的brute force way of identifying superfluous header includes。它并不完美,但消除了“明显的”不必要的包含。摆脱这些在清理代码方面有很长的路要走。

可以直接在GitHub上访问scripts

答案 17 :(得分:0)

Gimpel Software的PC Lint可以报告在编译单元中多次包含包含文件的时间,但是找不到您要查找的方式不需要的包含文件

编辑:可以。见itsmatt's answer

答案 18 :(得分:0)

来自JetBrains的C / C ++ IDE

CLion检测到冗余包含开箱即用的功能。这些在编辑器中显示为灰色,但也有optimise includes in the current file or whole project的功能。

我发现你付出了这个功能;首次加载时,CLion需要一段时间来扫描和分析您的项目。