我应该如何在大型C ++项目中检测到不必要的#include文件?

时间:2020-03-05 18:56:29  来源:igfitidea点击:

我正在Visual Studio 2008中从事大型C ++项目,并且有很多文件带有不必要的#include指令。有时,#include只是工件,将它们删除后,一切都会很好地编译,在其他情况下,类可能会被预先声明,并且#include可以移动到.cpp文件中。是否有检测这些情况的良好工具?

解决方案

回答

虽然它不会显示不需要的包含文件,但Visual Studio的设置为/ showIncludes(右键单击.cpp文件,"属性"-> C / C ++->"高级"),它将输出所有文件在编译时。这可以帮助识别不需要包括的文件。

我们还可以看一下pimpl习惯用法,以减少对头文件的依赖性,从而更轻松地查看可以删除的内容。

回答

像Timmermans一样,我对任何工具都不熟悉。但是我知道有一些程序员编写了Perl(或者Python)脚本,试图一次注释掉每个包含行,然后编译每个文件。

现在看来,埃里克·雷蒙德(Eric Raymond)拥有解决此问题的工具。

Google的cpplint.py有一个"包含我们使用的内容"规则(还有许多其他规则),但是据我所知,没有"仅包含我们使用的内容"规则。即使这样,它还是有用的。

回答

我不知道有任何这样的工具,并且我过去曾考虑过编写一个工具,但事实证明,这是一个很难解决的问题。

假设源文件包含a.h和b.h; a.h包含" #define USE_FEATURE_X",b.h使用" #ifdef USE_FEATURE_X"。如果将#include" a.h"注释掉,则文件可能仍会编译,但可能无法满足期望。以编程方式检测到这一点并非易事。

无论使用哪种工具,都需要了解构建环境。如果a.h看起来像:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

然后,仅在定义了" WINNT"的情况下才定义" USE_FEATURE_X",因此该工具将需要知道编译器本身生成了哪些指令,以及在编译命令中而不是在头文件中指定了哪些指令。

回答

PC Lint对此非常有效,它也为我们找到了其他各种愚蠢的问题。它具有可用于在Visual Studio中创建外部工具的命令行选项,但我发现Visual Lint加载项更易于使用。甚至Visual Lint的免费版本也有帮助。但是,试试PC-Lint。配置它使其不会给我们太多警告会花费一些时间,但是我们会惊讶于它的出现。

回答

添加以下#define中的一个或者两个
将经常排除不必要的头文件,并且

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

可能会大大改善
尤其是在代码未使用Windows API函数的情况下的编译时间。

回答

请参阅http://support.microsoft.com/kb/166474

如果我们要删除不必要的#include文件以减少构建时间,则最好使用cl.exe / MP,make -j,Xoreax IncrediBuild,distcc / icecream等来并行化构建过程,以节省时间和金钱。 。

回答

当然,如果我们已经有一个并行的构建过程,并且仍在尝试加快它的速度,那么请务必清除#include指令并删除那些不必要的依赖项。

回答

现有的一些答案表明这很困难。的确如此,因为我们需要一个完整的编译器来检测前向声明合适的情况。我们不能在不了解符号含义的情况下解析C ++。语法实在是太模棱两可了。我们必须知道某个名称是命名类(可以向前声明)还是变量(不能)。另外,我们需要了解名称空间。

!!免责声明!!我使用的是商业静态分析工具(而不是PC Lint)。 !!免责声明!!

简单的非解析方法存在多个问题:

1)过载设置:

重载函数可能具有来自不同文件的声明。删除一个头文件可能导致选择了不同的重载,而不是编译错误!结果将是语义上的无声更改,此后可能很难跟踪。

2)模板专长:

与重载示例类似,如果我们对模板具有部分或者明确的专长,则希望它们在使用模板时都可见。主模板的专门化可能在不同的头文件中。删除带有专门化的标头不会导致编译错误,但是如果选择了该专门化,则可能导致未定义的行为。 (请参阅:C ++函数模板专业化的可见性)

回答

正如" msalters"所指出的那样,对代码执行完整的分析还可以分析类的用法。通过检查如何通过特定的文件路径使用类,可以完全删除该类的定义(以及所有其相关性),或者至少将其移到更接近include中主要源的级别树。

如果尚未使用,则使用预编译的头文件包含所有我们不会更改的内容(平台头文件,外部SDK头文件或者项目的静态已完成部分)将极大地缩短构建时间。

http://msdn.microsoft.com/zh-CN/library/szfdksca(VS.71).aspx

回答

另外,尽管对项目来说可能为时已晚,但将项目组织成多个部分,并且不将所有本地标头集中到一个大的主标头中,是一个好习惯,尽管这需要一些额外的工作。

从每个包含文件开始,并确保每个包含文件仅包含编译自身所需的内容。然后,C ++文件缺少的所有包含文件都可以添加到C ++文件本身。

对于每个包含文件和源文件,一次注释掉每个包含文件,然后查看其是否可以编译。

回答

将包含文件按字母顺序排序也是一个好主意,如果不可能,请添加注释。

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif

如果头文件通常以

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else 
#pragma message("Someheader.h superfluously included")
#endif

(而不是一次使用#pragma),我们可以将其更改为:

回答

并且由于编译器输出正在编译的cpp文件的名称,因此至少可以让我们知道哪个cpp文件导致多次引入标头。

PC-Lint确实可以做到这一点。一种简单的方法是将其配置为仅检测未使用的包含文件并忽略所有其他问题。这非常简单,仅启用消息766("模块中未使用头文件"),而仅在命令行中包含选项-w0 + e766.

相同的方法也可以用于相关消息,例如964("模块中不直接使用头文件")和966("模块中不直接使用间接包含的头文件")。

回答

FWIW上周在博客文章http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318中更详细地介绍了这一点。

回答

一般而言,如果我们对该主题感兴趣,则可能需要查看Lakos的大规模C ++软件设计。这有些陈旧,但是会遇到很多"物理设计"问题,例如找到需要包含的标头的绝对最小值。我还没有真正在其他地方看到过这种讨论。

回答

尝试包括管理器。它可以轻松地集成到Visual Studio中,并可视化包含路径,这有助于我们查找不必要的内容。
在内部,它使用Graphviz,但还有许多更酷的功能。而且,尽管它是一种商业产品,但价格却非常低廉。

回答

我们可以使用C / C ++ Include File Dependencies Watcher构建包含图,并在视觉上找到不需要的包含。

也许有点晚了,但是我曾经发现一个WebKit perl脚本可以完成我们想要的操作。我相信它需要一些调整(我不太了解perl),但是应该可以解决问题:

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

段落数量不匹配