我应该如何检测大型C ++项目中不必要的#include文件?
我正在Visual Studio 2008中处理一个大的C ++项目,并且有很多文件包含不必要的#include
指令。 有时#include
只是工件,所有东西都可以正确的编译,而在其他情况下,类可以被声明,#include可以被移动到.cpp
文件中。 有没有什么好的工具来检测这两种情况?
虽然它不会显示不需要的包含文件,Visual Studio有一个设置/showIncludes
(右键单击.cpp
文件, Properties->C/C++->Advanced
),将在编译时输出包含所有文件的树。 这可以帮助识别不需要包含的文件。
你也可以看一下pimpl的习惯用法,让你可以用较less的头文件依赖关系来获取,以便更容易地看到你可以删除的文件。
PC Lint在这方面工作的很好,也为你find了各种其他愚蠢的问题。 它具有可用于在Visual Studio中创build外部工具的命令行选项,但是我发现Visual Lint插件更易于使用。 即使免费版本的视觉林特帮助。 但给PC-Lint一枪。 configuration它,所以它不会给你太多的警告需要一点时间,但你会惊讶于它的结果。
!!免责声明! 我使用商业静态分析工具(而不是PC Lint)。 !!免责声明!
简单的非parsing方法有几个问题:
1)过载集合:
重载的函数有可能声明来自不同的文件。 这可能是删除一个头文件导致select不同的重载,而不是编译错误! 结果将是语义上的一个沉默的变化,事后可能很难find。
2)模板专业化:
与重载示例类似,如果您对模板有部分或显式的特化,则希望在使用模板时全部可见。 主模板的专业化可能在不同的头文件中。 使用特化除去标题不会导致编译错误,但是如果已经select了特化,可能会导致未定义的行为。 (请参阅: C ++函数的模板特化的可见性 )
正如'msalters'指出的那样,对代码进行全面分析也可以分析类的使用情况。 通过检查一个类是如何通过一个特定的文件path来使用的,这个类的定义(以及它的所有依赖关系)可以被完全移除,或者至less被移动到更接近于包含主要源树。
有一个新的基于铿锵的工具, 包括你用什么 ,目的是做到这一点。
我不知道有没有这样的工具,而且我曾经想过写一个,但事实certificate,这是一个难以解决的问题。
说你的源文件包括ah和bh; 啊包含#define USE_FEATURE_X
和bh使用#ifdef USE_FEATURE_X
。 如果#include "ah"
被注释掉了,你的文件仍然可以编译,但是可能不会达到你所期望的。 以编程方式检测这个并不重要。
无论这个工具是否需要知道你的构build环境。 如果啊看起来像:
#if defined( WINNT ) #define USE_FEATURE_X #endif
那么USE_FEATURE_X
只有在WINNT
被定义的情况下才被定义,所以这个工具需要知道编译器本身产生了哪些指令,哪些指令是在编译命令中指定的,而不是在头文件中指定的。
像Timmermans一样,我不熟悉任何工具。 但是我知道编写Perl(或者Python)脚本的程序员尝试注释掉每个包含的行,然后编译每个文件。
现在看来,Eric Raymond 有一个这样的工具 。
谷歌的cpplint.py有一个“包括你使用”的规则(其中包括许多其他),但据我所知,没有“ 只包括你使用的东西”。 即使如此,它可能是有用的。
如果您对这个主题感兴趣,您可能需要查看Lakos的大型C ++软件devise 。 这有点过时了,但进入了很多“物理devise”问题,如find需要包含的标题的绝对最小值。 我还没有真正看到其他地方讨论过这种事情。
如果你的头文件通常以
#ifndef __SOMEHEADER_H__ #define __SOMEHEADER_H__ // header contents #endif
(而不是使用#pragma一次),您可以将其更改为:
#ifndef __SOMEHEADER_H__ #define __SOMEHEADER_H__ // header contents #else #pragma message("Someheader.h superfluously included") #endif
由于编译器输出正在被编译的cpp文件的名字,这会让你至less知道哪个cpp文件导致头被多次带入。
给包括经理一个尝试。 它在Visual Studio中轻松集成,可视化您的包含path,帮助您find不必要的东西。 它在内部使用Graphviz,但还有很多很酷的function。 虽然它是一个商业产品,它的价格非常低。
您可以使用C / C ++包含文件相关性监视器来构build一个包含graphics,并且可以直观地查找不需要的内容。
PC-Lint的确可以做到这一点。 一个简单的方法是将其configuration为只检测未使用的包含文件,并忽略所有其他问题。 这很简单 – 只启用消息766(“Header file not in module”),只需在命令行中包含选项-w0 + e766。
相同的方法也可以用于相关消息,如964(“模块中不直接使用的头文件”)和966(“模块中未使用的间接包含的头文件”)。
FWIW我在上周的博客文章中更详细地写了这篇文章: http: //www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318。
如果您正在寻找删除不必要的#include
文件以减less构build时间,那么花时间和金钱可能会更好地使用cl.exe / MP , make -j , Xoreax IncrediBuild ,distcc / icecream等并行化您的构build过程。
当然,如果你已经有了一个并行的构build过程,并且你还在努力加速它,那么通过一切手段来清理你的#include
指令,并删除那些不必要的依赖关系。
从每个包含文件开始,并确保每个包含文件只包含编译自身所需的内容。 任何包含文件,然后丢失的C + +文件,可以被添加到C + +文件本身。
对于每个包含和源文件,每个注释包含一个文件,并查看它是否编译。
按字母顺序对包含文件进行sorting也是一个好主意,在不可能的地方添加注释。
添加下面的一个或两个#defines将排除经常不必要的头文件,并且可能大大提高编译时间,特别是如果不使用Windows API函数的代码。
#define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN
如果您还没有使用预编译头文件来包含您不会更改的所有内容(平台头文件,外部SDK头文件或静态已完成的项目文件),那么构build时间将会产生巨大差异。
http://msdn.microsoft.com/en-us/library/szfdksca(VS.71).aspx
另外,虽然对于你的项目来说可能为时过晚,但是把你的项目分成几个部分,而不是把所有的本地头部包含到一个大主标题中,这是一个很好的做法,尽pipe这需要一些额外的工作。
如果你使用Eclipse CDT,你可以试试http://includator.com来优化你的包含结构。; 然而,对于VC ++的预定义包含而言,includator可能还不够了解,build立CDT使用VC ++与正确的包含尚未内置到CDT中。
最新的Jetbrains IDE CLion会自动显示(灰色)当前文件中未使用的包含。
还可以从IDE获得所有未使用的包含(以及函数,方法等)的列表。
一些现有的答案表明这很难。 确实如此,因为您需要一个完整的编译器来检测前向声明是合适的情况。 你不能不知道符号是什么意思,就不能parsingC ++。 语法太简单了。 您必须知道某个名称是否指定了一个类(可以是前向声明的)或variables(不能)。 另外,您需要具有名称空间感知function。
也许有点晚,但我曾经find一个WebKit perl脚本,只是做你想要的。 它需要适应我相信(我不熟悉perl),但它应该做的伎俩:
(这是一个旧的分支,因为主干没有该文件了)
如果有一个你认为不再需要的头文件(比如string.h),你可以注释掉include,然后把它放在下面的所有内容中:
#ifdef _STRING_H_ # error string.h is included indirectly #endif
当然你的接口头文件可能会使用一个不同的#define约定logging它们包含在CPP内存中。 还是没有约定,在这种情况下,这种方法将无法正常工作。
然后重build。 有三种可能性:
-
它build立好了。 string.h不是编译关键字,可以删除它的包含。
-
#恐怖旅行。 string.g被间接包含在内你还是不知道是否需要string.h。 如果需要,你应该直接#包括它(见下文)。
-
你得到一些其他的编译错误。 string.h是需要的,并没有被间接包含,所以包含是正确的开始。
注意,当你的.h或者.c直接使用另一个.h或者.c时,根据间接包含,几乎可以肯定是一个错误:只要你使用的其他头文件需要它,你的代码将只需要这个头文件,这可能不是你的意思。
其他答案中提到的关于修改行为的头文件中提到的注意事项,以及声明导致构build失败的事情也适用于此处。