生成的包含卫兵:一次替代实用主义

生成的包含卫兵:一次替代实用主义
照片由Toa HeftibaUnsplash拍摄

介绍

在C ++中,没有什么可以阻止程序员多次包含头文件。 这可能会导致定义重复,这是一个错误。 由于很难确保只将头文件包含一次,因此常见的策略是仅对第一个包含计数进行计数。 可以使用“包含保护”来完成,这是一小段预处理器逻辑,如下所示:

生成的包含卫兵:一次替代实用主义

它是如何工作的?

在第一个include上, HEADER_HAS_BEEN_INCLUDED未定义,因此我们定义foo 在随后的包含中,已定义HEADER_HAS_BEEN_INCLUDED ,因此我们仅跳过内容。

例如,如果我们有此C ++文件:

生成的包含卫兵:一次替代实用主义

然后它将扩展为:

生成的包含卫兵:一次替代实用主义

在预处理程序完成后,我们将得到以下结果:

生成的包含卫兵:一次替代实用主义

这是惯用的方法,但是有一些限制:

  1. 需要三行样板代码
  2. 第1行和第2行的变量名称必须完全匹配
  3. 相同的变量名不能在多个文件中使用
  4. 我们必须记住#endif ,它位于文件#ifndef的另一端

那一次实用主义呢?

#pragma once旨在克服这些问题。 它是C ++编译器的非标准功能,但得到了广泛支持。 这个概念很简单:任何包含#pragma once文件实际上只会被包含一次,即使程序员多次包含它。

#pragma once使用#pragma once ,我们的示例变为:

生成的包含卫兵:一次替代实用主义

看起来不错吧? 可悲的是, #pragma once带来了许多问题。

根本原因是#pragma once涉及其中一些代码的生活,而不是它的内容 如果您可以通过多个路径访问同一文件的两个副本,那么它将被包含两次。 而且,如果您有两条路径看起来不同但实际上相同,那么编译器可能不会发现这一点。 最重要的是,它不是标准的,因此编译器实现不必尊重其语义。

可能的解决方法

#pragma once的问题#pragma once源于它在文件的位置而不是其内容上起作用的事实。 如果我们只是使用内容呢? (当然,记录每个标题的所有内容会很慢,但是我们可以通过记录内容的哈希值来进行优化)。

该过程将是:

1.包含头文件时,对其进行哈希处理
2.如果以前已经看到过哈希,则忽略包含
3.否则,正常包含标题

这将是一个可靠的解决方案,因为它根本不关心文件所在的路径,而只关心文件的内容。

实施解决方法

将新命令添加到C ++标准将花费大量时间,但是幸运的是,我们可以使用脚本和预处理器来实现此逻辑。

基本思想是这样的:

生成的包含卫兵:一次替代实用主义

因此,例如此标头:

生成的包含卫兵:一次替代实用主义

SHA-256哈希值为:

生成的包含卫兵:一次替代实用主义

因此,生成的标头可能是:

生成的包含卫兵:一次替代实用主义

尽管单个文件的转换很简单( Python脚本 ),但我们仍然需要管理转换过程。 我们需要确保:

  • 为每个文件运行转换
  • 新文件将自动转换
  • 删除文件的转换会自动删除
  • 仅在文件更改后才重新运行转换
  • 奖励:转换可以安全地放入共享的网络缓存中

使用Buck build ,我们可以轻松地将此逻辑编码到项目的构建脚本中。

让我们从单个文件的构建规则开始,然后进行概括:

生成的包含卫兵:一次替代实用主义

Buck中的genrule很像Make中的目标。 我们定义输入文件,输出文件名和要执行的命令。 该目标使用我们的Python脚本来生成包含保护,并在add.hpp上运行它。 与Make不同,Buck将在输入哈希中隔离并缓存该进程。

现在我们只有一个文件,我们可以将流程推广到n文件。 为此,我们制作了一个Python函数,该genrule为给定的文件创建了一个genrule

生成的包含卫兵:一次替代实用主义

要获取头文件集,我们运行一个glob表达式。 例如:

生成的包含卫兵:一次替代实用主义

并将所有内容整合在一起:

生成的包含卫兵:一次替代实用主义

您可以在GitHub上找到完整的工作示例

现在,我们的头文件可以一次编写而无需包含保护或#pragma once

生成的包含卫兵:一次替代实用主义

Buck中的此设置非常适合与以下对象一起使用:

  • 头文件中的零样板
  • Buck会自动检查新的头文件,因此构建始终是最新的
  • Buck将删除过时的头文件
  • 因为它了解目标图,所以Buck将并行生成标头
  • Buck将缓存生成的头,以便仅在需要时才计算
  • 我们不再依赖人类的准确性(包括防护措施)或非标准功能( #pragma once

既然你在这里...

我们创建了Buckaroo ,以便更轻松地集成C ++库。 如果您想尝试一下,最好的起点是文档 您可以浏览Buckaroo.pm上的现有软件包,或在愿望清单上请求更多。

From: https://hackernoon.com/generated-include-guards-an-alternative-to-pragma-once-31cc3dee6ce