清理遗留代码“头面条”(Cleaning up Legacy Code “header spaghe

2019-09-18 03:29发布

清理“头面条”的任何建议的做法,这是造成极其缓慢的编译时间(的Linux / Unix)?

是否有任何等同放着清单“的#pragma一次”与GCC?
(关于这一发现相互矛盾的信息)

谢谢。

Answer 1:

假设你熟悉“包括卫士”(在头的开头的#ifdef ..),加快构建时的另一种方式是通过使用外部包括警卫。 这是在“讨论大规模C ++软件设计 ”。 这个想法是典型的包括警卫,不同于曾经的#pragma,不要吝惜你忽略从第二时间(即它仍然必须分析并查找包括后卫的开始和结束的头所需的预处理器解析。随着外部包括守卫你周围放置#include行本身的#ifdef。

所以它看起来像这样:

#ifndef MY_HEADER
#include "myheader.h"
#endif

和H文件中,当然你有经典的包括后卫

#ifndef MY_HEADER
#define MY_HEADER

// content of header

#endif

通过这种方式,myheader.h文件甚至没有打开/预处理器解析,它可以为您节省大量的时间在大项目,尤其是在头文件坐在共享的远程位置,因为他们有时做。

再次,这一切都在那本书。 心连心



Answer 2:

如果你想要做一个完整的清理,并有时间去做那么最好的解决办法是删除所有文件中的所有的#includes(除了那些明显的如abc.h在abc.cpp),然后编译该项目。 添加必要的向前声明或头修复的第一个错误,然后重复,直到你COMPLE干净。

这不解决,可能导致问题包括潜在的问题,但它并确保只包含有需要的人。



Answer 3:

我读过GCC考虑#pragma once过时了,虽然连#pragma once只能做这么多加快速度。

要尽量解开#include意大利面条,你可以看看doxygen的 。 它应该能够生成包括报头,它可以给你的东西简化边缘的曲线图。 我不记得细节副手,而图形功能可能需要安装GraphViz的 ,并告诉doxygen的哪里能找到的GraphViz的dotty.exe的路径。

另一种方法,如果编译的时候是你最关心的是建立可以考虑预编译头 。



Answer 4:

我读了一天大约巧妙的方法,以减少头部的依赖性:写一个脚本,将

  • 找到所有#include语句
  • 去掉一个语句的时间和重新编译
  • 如果编译失败,添加包括语句回

最后,你会希望用最小的在你的代码需要包括结束。 你可以写一个类似的脚本,重新排列包括以找出他们是否是自给自足的,或需要其他头被包含在他们面前(包括头第一,看是否编译失败,举报)。 这应该在一定程度上清理你的代码。

一些注意事项:

  • 现代编译器(其中GCC)承认头部防护装置,并优化以同样的方式为编译一次会,只有打开文件一次。
  • 编译一次,可能是有问题的时候同样的文件中的文件系统有不同的名称(即软链接)

  • GCC支持的#pragma一次,但将其称为“过时”
  • 编译一次,不是所有的编译器的支持,而不是C标准的一部分

  • 不仅编译器可能会产生问题。 像Incredibuild工具也有问题,使用#pragma一次


Answer 5:

理查德有点权(为什么他的解决办法是记录下来?)。

总之,所有的C / C ++头文件应该使用内部包括警卫。

这就是说,无论是:

1 - 您的旧代码是不是真的保持了,而且你应该使用预编译头(这是一个黑客,但嘿...您的需要,是加快您的编辑,不重构代码没有维护)

- 你的遗留代码还活着。 然后,你既可以使用预编译的头和/或警卫/外部警卫的一个临时的解决方案,但最终,你将需要一个时间来删除所有包括一个.C或.CPP,和编译每个。 C或.cpp文件一次一个,纠正他们与前瞻性声明包括或包括在必要时(甚至将大包括成小的,以确保每一个.C或.cpp文件中会得到只需要头)。 总之,检测和删除过时的包括是维护项目的一部分,所以...

我自己的预编译头的经验是不完全是一个很好的,因为一半的时候,编译器找不到我已经定义的符号,所以我尝试了一个完整的“清洁/重建”,以确保它是不是预编译头这是过时的。 所以我的想法是用它来你甚至不会触及(如STL,C API头,升压,等等)外部库。 不过,我自己的经验是用Visual C ++ 6.0,所以我想(希望吗?)他们是对了,现在。

现在,最后一两件事:头应始终是自给自足。 这意味着,如果头列入取决于包容的顺序,那么你有问题。 例如,如果你可以写:

#include "AAA.hpp"
#include "BBB.hpp"

但不是:

#include "BBB.hpp"
#include "AAA.hpp"

因为BBB取决于AAA,那么你已经是你的代码永远不会承认的依赖。 未与定义,只会让你的编译一场噩梦承认它。 BBB应包括AAA,太(即使它可能会略慢一些:到底的,前瞻性的声明无论如何都会清理无用包括,所以你应该有一个更快的编译定时器)。



Answer 6:

使用一个或更多的这些,对于加快构建时间

  1. 使用预编译头
  2. 使用缓存机制(scons的举例)
  3. 使用分布式构建系统(distcc的,Incredibuild($))


Answer 7:

在头:包括头只,如果你不能使用前置声明,但总是#包含你需要的任何文件(包括依赖关系是邪恶的!)。



Answer 8:

正如在其他答复中提到,你一定要尽可能使用前向声明。 据我所知,GCC没有任何关系等同于#PRAGMA一次,这就是为什么我坚持的包括警卫旧的时尚风格。



Answer 9:

感谢您的答复,但问题是关于现有的代码,其中包括严格的“包括订单”等。现在的问题是,是否有任何工具/脚本来阐明什么是真正回事。

头守卫的arent的解决方案,因为他们不阻止编译器一次又一次地读取整个文件,并...



Answer 10:

PC -皮棉将很长的路要走,以清理面条头。 此外,它会为你解决其他问题太像未初始化的变量会看不见,等。



Answer 11:

至于onebyone.livejournal.com到您问题的答复评价说,一些编译器支持包括后卫优化 ,其中的页面我联系了如下定义:

在包括防护件优化是,当一个编译器可识别内部包括上述防护件成语,并采取措施以避免文件打开多次。 编译器可以看一个包含文件,去掉注释和空白,并制定出如果整个文件是包括警卫内。 如果是,它存储的文件名,包括在地图中后卫的条件。 下一次编译器要求包括文件,它可以检查包括警戒条件和做出决定是否要跳过的文件或#包括它,而无需打开该文件。

再说,你已经回答了外部包括警卫不回答你的问题。 为了解开必须包含在一个特定的顺序的头文件,我建议如下:

  • 每个.c.cpp文件应该#include相应.h第一个文件,它的其余部分#include指令应按照字母顺序排序。 你通常会得到编译错误时,这打破头文件之间未声明的相关。
  • 如果你有一个定义为基本类型或全球全球的typedef头文件#define被用于大多数的代码的指令,每一个.h文件应该#include :第一,文件,以及它的其余#include指令应按照字母顺序排序。
  • 当这些变化导致编译错误,你通常必须从一个头文件添加一个显式依赖另一个,在形式#include
  • 当这些变化不会导致编译错误,他们可能会导致行为的变化。 希望你有某种测试套件,你可以用它来验证应用程序的功能。

它还听起来就像是问题的一部分可能是增量构建比他们应该是慢得多。 这种情况可以用前置声明或分布式构建系统加以改进,正如其他人指出。



文章来源: Cleaning up Legacy Code “header spaghetti”