【发布时间】:2008-09-21 07:02:26
【问题描述】:
清理“标题意大利面条”的任何推荐做法,这会导致极度 编译时间慢(Linux/Unix)?
在 GCC 中是否有与“#pragma once”等价的方法?
(发现与此相关的相互矛盾的消息)
谢谢。
【问题讨论】:
标签: c++ legacy-code
清理“标题意大利面条”的任何推荐做法,这会导致极度 编译时间慢(Linux/Unix)?
在 GCC 中是否有与“#pragma once”等价的方法?
(发现与此相关的相互矛盾的消息)
谢谢。
【问题讨论】:
标签: c++ legacy-code
假设您熟悉“包含保护”(#ifdef 在标头开头..),另一种加快构建时间的方法是使用外部包含保护。 它在“Large Scale C++ Software Design”中进行了讨论。这个想法是经典的包含守卫,与#pragma once 不同,不要让您从第二次开始忽略标头所需的预处理器解析(即,它仍然必须解析并查找包含守卫的开始和结束。用外部包含保护您将#ifdef 放在#include 行本身周围。
所以它看起来像这样:
#ifndef MY_HEADER
#include "myheader.h"
#endif
当然在 H 文件中你有经典的包含保护
#ifndef MY_HEADER
#define MY_HEADER
// content of header
#endif
这样,myheader.h 文件甚至不会被预处理器打开/解析,它可以在大型项目中为您节省大量时间,尤其是当头文件位于共享的远程位置时,因为它们有时会这样做。
再一次,一切都在那本书里。 hth
【讨论】:
如果你想做一个完整的清理并有时间去做,那么最好的解决方案是删除所有文件中的所有#includes(除了明显的,例如abc.cpp中的abc.h)然后编译该项目。添加必要的前向声明或标头以修复第一个错误,然后重复直到您干净地完成。
这并不能解决可能导致包含问题的潜在问题,但它确实确保了唯一的包含是必需的。
【讨论】:
我了解到 GCC 认为 #pragma once 已弃用,尽管即使是 #pragma once 也只能做很多事情来加快速度。
要尝试解开#include 意大利面条,您可以查看doxygen。它应该能够生成包含标题的图表,这可能会给您简化事情的优势。我无法立即回忆起详细信息,但图形功能可能需要您安装 GraphViz 并告诉 doxygen 它可以找到 GraphViz 的 dotty.exe 的路径。
如果编译时间是您最关心的问题,您可能会考虑的另一种方法是设置 Precompiled Headers。
【讨论】:
前几天我读到了一个减少标题依赖的巧妙技巧:编写一个脚本
最后,希望您的代码中包含最少的必需包含项。您可以编写一个类似的脚本,重新排列包含以找出它们是否自给自足,或者要求在它们之前包含其他标头(首先包含标头,看看编译是否失败,报告它)。这应该有助于清理您的代码。
还有一些注意事项:
【讨论】:
Richard 有点正确(为什么他的解决方案被记录下来?)。
无论如何,所有 C/C++ 头文件都应该使用内部包含保护。
这表示,要么:
1 - 您的遗留代码不再真正得到维护,您应该使用预编译的标头(这是一种 hack,但是嘿……您需要加快编译速度,而不是重构未维护的代码)
2 - 您的旧代码仍然有效。然后,您可以使用预编译的头文件和/或防护/外部防护作为临时解决方案,但最后,您需要删除所有包含,一次一个 .C 或 .CPP,并编译每个 . C 或 .CPP 文件一次一个,在必要时使用前向声明或包含来更正它们的包含(或者甚至将大型包含分成较小的包含以确保每个 .C 或 .CPP 文件将只获得它需要的标题)。无论如何,测试和删除过时的包含是项目维护的一部分,所以...
我自己对预编译头文件的体验并不完全是好的,因为有一半时间,编译器找不到我定义的符号,所以我尝试了完整的“清理/重建”,以确保它不是已过时的预编译头文件。所以我的猜测是将它用于你甚至不会接触的外部库(比如 STL、C API 头文件、Boost 等等)。不过,我自己的经验是使用 Visual C++ 6,所以我猜(希望?)他们现在做对了。
现在,最后一件事:标题应该始终是自给自足的。这意味着如果标题的包含取决于包含的顺序,那么您就有问题了。例如,如果你可以写:
#include "AAA.hpp"
#include "BBB.hpp"
但不是:
#include "BBB.hpp"
#include "AAA.hpp"
因为 BBB 依赖于 AAA,所以你所拥有的只是一个你从未在代码中承认的依赖关系。不通过定义来确认它只会让你的编译成为一场噩梦。 BBB 也应该包含 AAA(即使它可能会慢一些:最后,前向声明无论如何都会清除无用的包含,因此您应该有一个更快的编译计时器)。
【讨论】:
使用其中一种或多种来加快构建时间
【讨论】:
在标头中:仅当您不能使用前向声明时才包含标头,但始终#include 您需要的任何文件(包含依赖项是邪恶的!)。
【讨论】:
正如另一个答案中提到的,您绝对应该尽可能使用前向声明。据我所知,GCC 没有任何与 #pragma once 等价的东西,这就是为什么我坚持包含守卫的旧时尚风格。
【讨论】:
感谢您的回复,但问题是关于现有代码,其中包括严格的“包含顺序”等。 问题是是否有任何工具/脚本来澄清实际发生的事情。
标头保护不是解决方案,因为它们不会阻止编译器一次又一次地读取整个文件......
【讨论】:
PC-Lint 将大大有助于清理意大利面条标题。它还可以为您解决其他问题,例如未初始化的变量看不见等。
【讨论】:
正如 onebyone.livejournal.com 在回复您的问题时评论的那样,一些编译器支持include guard optimization,我链接的页面定义如下:
包含保护优化是当编译器识别出上述内部包含保护习惯并采取措施避免多次打开文件时。编译器可以查看包含文件,去除 cmets 和空白,并确定整个文件是否在包含保护中。如果是,它将文件名和包含保护条件存储在映射中。下次要求编译器包含文件时,它可以检查包含保护条件并决定是跳过文件还是#include 它而不需要打开文件。
然后,您已经回答了外部包含守卫不是您问题的答案。对于必须以特定顺序包含的头文件,我建议如下:
.c 或.cpp 文件应首先#include 对应的.h 文件,其余#include 指令应按字母顺序排序。当这破坏了头文件之间未声明的依赖关系时,通常会出现构建错误。#define 指令,则每个.h 文件应首先#include 该文件,其余的@ 987654330@ 指令应按字母顺序排序。#include 的形式将显式依赖项从一个头文件添加到另一个头文件。听起来问题的一部分可能是增量构建比应有的慢得多。正如其他人所指出的,这种情况可以通过前向声明或分布式构建系统来改善。
【讨论】: