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


289

我经常发现文件的标头部分一直都在变大,但从未变小。在源文件的整个生命周期中,类可能已经移动并被重构,并且很可能有很多#includes不需要再存在了。将它们保留在那里只会延长编译时间,并增加不必要的编译依赖项。试图弄清仍然需要哪些可能会很乏味。

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



1
链接的问题似乎只能在Windows上解决该问题,尤其是使用Visual Studio。
D'Nabre 2014年

7
投票重新打开它,因为重复是专门关于使用Visual Studio的。
德鲁·多曼

Answers:


42

它不是自动的,但是doxygen会生成#included文件的依赖关系图。您将不得不在视觉上浏览它们,但是它们对于获取正在使用什么的图片非常有用。


5
这是查看链的好方法。看到A-> B-> C-> D和A-> D立即显示出冗余。
汤姆(Tom)2009年

34
@Tom:这是一个可怕的想法:对于一个它并没有显示是否需要这些包含,其次,包含的列表不应该依赖于将来可能会改变的间接包含(冗余包含通常不是这样的)无论如何,这都是一个大问题,这要归功于包含防护措施和编译器魔术功能),但是文件中实际使用了哪些类/函数(您的编译器不必遍历数千行甚至没有实例化的模板代码)
MikeMB '16

@albert,您可以包括它的屏幕截图,并简要描述在doxygen输出中单击的位置吗?
加布里埃尔·斯台普斯

@GabrielStaples这不是我的答案,所以我不想添加信息。我只更正了链接(作为其所指的托管位置已停止/被占用)。
阿尔伯特

177

Google的cppclean(链接到下载文档)可以找到几类C ++问题,并且现在可以找到多余的#include。

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

当前版本的Eclipse CDT也内置了此功能:在“源”菜单下,单击“组织包含”将按字母顺序排列您的#include,添加Eclipse认为您正在使用的所有标头而不直接包含它们,并注释掉它没有的任何标头认为您不需要。但是,此功能并非100%可靠。


2
现在可以了。我刚刚开始使用它。在这里看到我的笔记。 stackoverflow.com/questions/1301850/...
机会

1
cppclean存储库已关闭,您现在可以在这里找到它:bitbucket.org/robertmassaioli/cppclean(尽管原始站点仍可用于某些示例用法)
Nick

3
我将链接更新为维护的cppclean分支:github.com/myint/cppclean
2014年

1
请注意,cppclean似乎只能在doc的头文件中找到它们,而不能在cpp文件中找到它们:“头文件中不必要的#includes”。
Zitrax

1
@wizurd-我没有跟上Eclipse CDT的最新发展,但我不这么认为。iwyu彻底且相对较慢。Eclipse CDT的分析是快速的(交互式),并且在我对其进行测试时,其准确性较低。
乔什·凯利

65

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


6
恕我直言,这个答案需要更多的支持,因为一旦解决了问题,Google的IWYU工具将是完成此任务的权威工具。
丹·奥尔森,

5
sudo apt-get install iwyu
Andrew Wagner

似乎很棒-有两个证书1)最近更新于2106年2月2)Gogole本身仅将其用于C ++,而不是OP要求的C。
Mawg说恢复Monica

您能否解释一下用户应该如何使用它?自述文件不是很清楚python脚本的输出内容。
国王的小丑2017年

我正在使用它,但并非总是100%正确。也许有70%的时间给出了正确的建议。
InQusitive '18

25

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

Lint更像是一个样式检查器,当然不会具有这种全部功能。

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


8
如果包含文件的布局正确,那么这都不是问题。如果您需要在文件B之前包含文件A,那么您做错了(我在他们做错了的项目中工作)。
David Thornley,2009年

9
@David,是的,但这取决于开发人员在正确执行之前的年限。我可以肯定地说,发生这种情况的几率有利于房屋,而不是您:(
JaredPar 2009年

是的,但是我在修改程序时通常会发现有关信息,突然我遇到了编译错误(如果幸运的话)或晦涩的错误。至少从长远来看,这似乎可以确保#include文件的诚实。
David Thornley,2009年

我会说完全相反。您只需要一个类型依赖检查器即可。相应地安排了包含内容之后,它可能不会编译,但是无论如何,这些都是应该解决的问题。
贝诺瓦

1
@Benoit,那么您将忽略一类可编译但从语义上改变程序含义的问题。考虑一个文件中的#define如何改变另一个文件中的#if分支。删除标头仍然可以
使它

15

我以为PCLint可以做到这一点,但是距离我研究已经过去了几年。您可能会检查出来。

我看了一下这个博客,作者谈到了一些有关配置PCLint来查找未使用的包含的内容。可能值得一看。


好发现!我将不得不使用它。
crashmstr 2009年

4
我经常使用PCLint,它确实告诉我未使用的标头。我小心地注释掉头文件#include并重新编译,以确保头文件确实未被使用...
Harold Bamford

感谢您的确认,Harold。
itsmatt

5
太贵了 不是群众的可行工具。


5

您可以编写一个快速脚本,以擦除单个#include指令,编译项目,然后在未发生编译错误的情况下,将名称记录在#include中以及从中删除的文件中。

让它在夜间运行,第二天,您将可以100%正确地删除包含文件。

有时蛮力就可以了:-)


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

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

1
请注意这一点-说有两个头文件都包含某些内容的定义。您可以删除任何一个,但不能全部删除。您需要在蛮力方法上更加彻底。
多米尼克·罗杰

也许这就是您的意思,但是一个脚本删除了一个包含,并且如果成功删除了最后一个包含的内容,则将其删除即可。
多米尼克·罗杰

1
馊主意。如果头文件#定义了常量BLAH,而另一个头文件检查#ifdef BLAH,则删除第一个头文件可能仍然可以成功编译,但是您的行为已更改。
Graeme Perrow 2009年

1
这也可能导致系统标头出现问题,因为不同的实现可能在#include <vector>中包含了不同的内容。即使您坚持使用一个编译器,标头也可能会在不同版本上切换。
David Thornley,2009年

2
找不到包含真正需要的标题的标题的情况。
bk1e

5

很抱歉在这里(重新)发布信息,人们通常不会扩展评论。

检查我对crashmstr的评论,FlexeLint / PC-Lint将为您完成此操作。信息性消息766。我的手册(8.0版)的11.8.1节对此进行了讨论。

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


我完全知道您的意思,而我的回应是“ Ewwww”。我讨厌那样的代码。
David Thornley,2009年

5

我从未找到能够满足您要求的功能完善的工具。我使用过的最接近的是IncludeManager,它绘制了标头包含树的图形,因此您可以直观地发现诸如仅包含在一个文件中的标头和圆形标头包含在内的内容。


4

我尝试使用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 ++的文章。他们建议采用一种综合方法来清理#include混乱:

指导方针

这是Lakos的书中提炼的一套指导,可最大程度地减少文件之间的物理依赖性。我使用它们已有多年了,我一直对结果非常满意。

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

Lakos的书非常适合教育-除了他对编译器技术的过时观察之外。
汤姆(Tom)2009年

4

如果您使用的是Eclipse CDT,则可以尝试http://includator.com,它对于Beta测试人员是免费的(在撰写本文时),并且会自动删除多余的#include或添加缺失的#include。对于拥有FlexeLint或PC-Lint并使用Elicpse CDT的用户,可以选择http://linticator.com(对于Beta测试也是免费的)。当使用Lint的分析时,它提供了快速修复程序,可自动删除多余的#include语句。


这样做的原因是我们的簿记部门无法开具发票。如果计算时间,可以节省时间并不是没有道理的。曾经,我们有能力获得信用卡付款,我们可以大幅降低价格。另一种选择是为我们的开发工作提供赞助。我们的融资模式要求我们获取利润以资助我们的研究工作。我很乐意以更低的价格出售许可证,但是不能。也许我们会为CDT贡献它,而您可以免费获得它,但是我必须以某种方式提供资金。我忘了,您可以免费试用!
PeterSom 2013年

2

本文介绍了一种通过使用Doxygen的解析方法#include删除的技术。那只是一个perl脚本,所以很容易使用。


1
该脚本找到了一些要删除的包含,但是它也提供了许多无法删除的包含。似乎它不支持类枚举,似乎还不适合使用宏,有时还不支持名称空间。
巴蒂斯特·威希特



1

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

  1. 该模块实际上根本不需要的头文件(.c,.cpp)
  2. 头文件是模块所需要的,但不止一次,直接或间接地包含在内。

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

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

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

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

    弄干净

    使2>&1 | 发球result.txt

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

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

    pclint / flint -vf ...

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


1

结束本讨论:c ++预处理器即将完成。无论include是否是多余的,这都是语义属性。因此,根据赖斯定理,不确定包含是否是多余的是不确定的。不可能有一个程序(总是正确地)检测包含是否多余。


5
我是否要求“始终正确”的解决方案?这个答案对于讨论不是很有用。
shoosh

1
嗯,有许多帖子讨论了此类程序必须解决的问题。我的帖子对讨论的那部分给出了结论性的正确答案。而且我不喜欢它,如果程序告诉我,我可以安全地删除#include,然后我的代码不再编译。(或更糟糕的是-仍然可以编译,但执行方式有所不同)。任何此类程序均承担此风险。
Algoman 2014年

4
在所有关于如何困难以及如何解决一个或另一个障碍的说明中,我给了您唯一的100%正确答案。我觉得说这没有什么用是很无礼的……
Algoman 2014年

1
我记得赖斯定理说:“不可能有一个程序可以始终检查给定程序是否解决了这个多余的包含问题”。可能有一些程序可以解决多余的包含问题。
杨哲

1
我个人发现@Algoman的输入非常有帮助。让我意识到这个问题有多难。
bogardon '17


0

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

编辑:可以。查看其matt的答案


确定吗?几年来我没有在C ++代码上使用FlexeLint(与PCL相同),但是即使在最近的C代码上,我也发誓我看到一些关于未使用的头文件的消息(我认为是代码766?)。刚刚检查(v8.0),请参见第11.8.1节。手册。

0

来自JetBrains的C / C ++ IDE CLion检测开箱即用的冗余组件。这些在编辑器中显示为灰色,但是还有一些函数可以优化当前文件或整个项目中的包含

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.