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

我经常发现文件的头部分总是变得越来越大,但它从来没有变小过。在源文件的整个生命周期中,类可能已经移动和重构,并且很可能有相当多的#includes不需要存在。保留它们只会延长编译时间,并增加不必要的编译依赖关系。试图找出哪些仍然需要是相当乏味的。

是否有某种工具可以检测多余的#include指令,并建议哪些我可以安全地删除?< br > 棉绒会这样吗?< / p >
127751 次浏览

我认为PCLint会这样做,但它已经有几年了,因为我已经看到它。你可以去看看。

我看了这个博客,作者谈到了一些配置PCLint以查找未使用的包含。也许值得一看。

检测多余include的问题在于,它不能仅仅是一个类型依赖项检查器。一个多余的包含是一个文件,它没有提供任何有价值的编译而且不改变其他文件所依赖的另一项。头文件有很多方法可以改变编译,比如定义一个常量,重新定义和/或删除一个使用的宏,添加一个名称空间来改变名称的查找。为了检测像名称空间这样的项,您需要的不仅仅是一个预处理器,实际上几乎需要一个完整的编译器。

Lint更多的是一个样式检查器,当然不会有这个完整的功能。

我想你会发现检测多余include的唯一方法是删除、编译和运行套件。

您可以编写一个快速脚本,删除单个#include指令,编译项目,并在没有发生编译错误的情况下记录#include中的名称和它被删除的文件。

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

有时候蛮力很管用:-)


编辑:,有时它不:-)。下面是一些来自评论的信息:

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

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

谷歌的cppclean(链接到:下载文档)可以找到几种类型的c++问题,现在它可以找到多余的#includes。

还有一个基于clang的工具include-what-you-use可以做到这一点。include-what-you-use甚至可以建议前向声明(因此您不必#include那么多),并可选地为您清理#includes。

当前版本的Eclipse CDT也内置了这个功能:进入Source菜单并单击Organize Includes将按字母顺序排列#include's,添加Eclipse认为你正在使用的任何标题,但不直接包括它们,并注释掉它认为你不需要的任何标题。然而,这个特性并不是100%可靠。

CScout重构浏览器可以检测C(不幸的是不是c++)代码中多余的include指令。你可以在期刊文章中找到它如何工作的描述。

很抱歉(再次)在这里发帖,人们通常不扩展评论。

检查我对crashmstr的评论,FlexeLint / PC-Lint会为你做这件事。信息信息766号。我的手册(8.0版)的11.8.1节讨论了这个问题。

同时,这一点很重要,不断迭代,直到消息消失。换句话说,在删除了不需要的头文件后,重新运行lint,一旦删除了一些不需要的头文件,更多的头文件可能已经成为“不需要的”。(这听起来可能有点傻,慢慢读吧。解析一下,就明白了。)

我尝试过使用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依赖项搞砸。有更多的病理情况,但基本上你需要自己检查标题,以获得最好的结果。

我强烈推荐这篇关于物理结构和c++的文章来自博客Games。他们推荐了一个全面的方法来清理#include混乱:

的指导方针

以下是从Lakos的书中提炼出来的一组指导原则,可以最大限度地减少文件之间的物理依赖关系。我已经使用它们很多年了,我对效果一直很满意。

  1. 每个cpp文件首先包含它自己的头文件。(剪)
  2. 头文件必须包含解析它所需的所有头文件。(剪)
  3. 头文件应该有最少的头文件来解析它。(剪)

这篇文章解释了通过解析Doxygen来删除#include的技术。这只是一个perl脚本,所以它很容易使用。

我从来没有找到一个成熟的工具来完成你的要求。我用过的最接近的东西是IncludeManager,它绘制了你的头包含树,这样你就可以直观地发现像头只包含在一个文件和圆形头包含这样的东西。

也许有点晚了,但我曾经找到一个WebKit perl脚本,它做的正是你想要的。我相信它需要一些调整(我不太精通perl),但它应该能做到:

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

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

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

  1. 实际不需要的头文件 模块(.c, .cpp)在所有
  2. 模块需要头文件
  3. . .

根据我的经验,有两种方法可以很好地检测到它:

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

    在现实世界, 你可以在make之前导出CFLAGS=-H, 如果所有的Makefile没有被覆盖 CFLAGS选项。或者像我以前说的,你 可以创建一个cc/g++包装器添加-H 的每次调用中强制执行 $(CC)及$(CXX)。然后在 包装器的目录到$PATH 变量,那么你的成就将全部 而是使用你的包装器命令。的 过程中,包装器应该调用 真正的GCC编译器。这个技巧 需要更改,如果您的Makefile使用 直接gcc。而不是$(CC)或 $(CXX)或隐含规则

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

    使清洁

    Make 2>&1 | tee result.txt

  • <李> < p > PC-Lint / FlexeLint(解决问题 1和2)

    确保添加+e766选项,这个警告是关于:

    .使用的头文件

    Pclint /flint -vf…

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

有一个免费的工具包括文件依赖监视器可以集成到visual studio中。它用红色显示多余的#include。

还可以查看include-what-you-use,它解决了类似的问题。

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

结束这个讨论:c++预处理器是图灵完成的。包含是否是多余的,这是一个语义属性。因此,由Rice定理可知,包含是否多余是不可判定的。不可能有一个程序(总是正确地)检测包含是否多余。

CLion,来自JetBrains的C/ c++ IDE,立即检测冗余包含。这些在编辑器中是灰色的,但也有优化当前文件或整个项目中的包含的函数。

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

这是一个简单的强力识别多余头的方法包括。它并不完美,但消除了“明显的”不必要的包含。清除这些代码对清理代码大有帮助。

脚本可以直接在GitHub上访问。

clangd正在为你做这个现在。可能clang-tidy很快也能做到这一点。