【问题标题】:Determining if a source file unportably relies on indirect includes of standard headers确定源文件是否不可移植地依赖于标准头的间接包含
【发布时间】:2017-06-02 19:31:43
【问题描述】:

假设我有一个file.cpp,其中包含几个 C++ 头文件并使用标准库中的各种函数和类。可能是我使用了一些功能,这些功能来自我不直接包含的头文件,但被我包含的一些 other 标准库头文件间接包含。这是一个无意的编程错误,但由于间接包含,一切都将在我的平台上编译并正常工作。

不仅程序格式错误,而且无法保证其他平台也会有相同的间接包含,因此代码可能无法在其他平台上编译,这在实践中很常见。

是否有任何自动方法来检查这种类型的“缺少包含”,因为间接包含,我不能仅仅依靠我的平台上的编译失败?

作为一个具体的例子,在 gcc 的 libc++5 中,<memory> 标头包含一堆其他标准库标头,例如 <functional>,但在其他版本的编译器和其他编译器上并非如此。因此,如果我使用来自 <functional> 的内容,我不会收到编译器错误,但其他用户可能...

【问题讨论】:

  • 更简单的方法是创建仅包含必需声明的虚假系统头文件,然后在工具链中使用它...如果包含错误,您将收到编译错误。最后,一旦所有包含都正确,链接器就会出错。
  • @Jarod42 这听起来很有趣。您能否在答案中详细说明这种技术?
  • “……这在实践中很常见。” - 我希望不是。至少在专业的项目团队中。 (这与“他们的工作有报酬”不同)。
  • 忘记#include 标头 编程错误。静态代码分析器工具可能会有所帮助,但我们不是推荐网站。
  • @Olaf:我认为丢失的正确包含比它应该发生的频率更高,但是在为新目标编译时很容易发现该错误并且很容易修复。在为不同的目标编译时,这种情况发生的频率较低。该功能将是对 include-what-you-use 等工具的良好补充

标签: c++ include header-files


【解决方案1】:

我不知道现有的工具可以做到这一点。

但一种方法是构建(这是一项艰巨的任务)所有 "fake" 标头仅使用标准声明。 (函数定义不应该是必需的,但类定义会是)。

对于某些标题,您可以找到提供Synopsis 的页面,您可以直接使用这些页面,就像cstddef 一样:

namespace std {
    using ptrdiff_t = /*see definition*/ ;
    using size_t = /*see definition*/ ;
    using max_align_t = /*see definition*/ ;
    using nullptr_t = decltype(nullptr);

    enum class byte : unsigned char {} ;
    template <class IntegerType>
    constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept;
    template <class IntegerType>
    constexpr byte operator<<(byte b, IntegerType shift) noexcept;
    template <class IntegerType>
    constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept;
    template <class IntegerType>
    constexpr byte operator>>(byte b, IntegerType shift) noexcept;

    constexpr byte& operator|=(byte& l, byte r) noexcept;
    constexpr byte operator|(byte l, byte r) noexcept;
    constexpr byte& operator&=(byte& l, byte r) noexcept;
    constexpr byte operator&(byte l, byte r) noexcept;
    constexpr byte& operator^=(byte& l, byte r) noexcept;
    constexpr byte operator^(byte l, byte r) noexcept;
    constexpr byte operator~(byte b) noexcept;
    template <class IntegerType>
    constexpr IntegerType to_integer(byte b) noexcept; 
}
#define NULL /*see definition*/
#define offsetof(P, D) /*see definition*/

其他标头伪造起来会更复杂:-/

然后,(最简单的部分)在您的构建链中,您将真正的标准标头替换为您的假标头。比如:

  • g++ -nostdinc -I/path/to/fake/headers
  • 视觉对象的/X(忽略标准包含路径)和/I(附加包含目录)参数。

由于您不提供定义,最终会出现链接器错误。

但是,之前,如果您忘记添加必需的包含,由于缺少声明,您应该会出现编译器错误。

【讨论】:

  • 但是假头文件本质上必须复制标准库的大量功能。当然,您可以将实际实现留空,但标准库的很大一部分是所有模板内容,至少必须充实到足以让所有内容编译。
  • 不幸的是,是的,模板类比简单的函数需要更多的工作。 (并且不提供实现而不是将其留空应该更好)。
  • 所以你建议基本上使用自己的标题?那么实现特定的扩展呢?例如,gcc 提供了具有标准整数类型范围的宏。还是inline 方法/函数?定义为宏等的函数?我可能是错的,但对我来说这看起来是个糟糕的主意。
  • @Jarod42 我们一直在投票??我不明白:-P 似乎周围有很多白痴。
  • @Jarod42 解决如此简单(非)问题需要付出多大的努力。看来这主要是OP的懒惰导致“问题”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
  • 2010-12-16
  • 2015-01-17
  • 2017-05-08
  • 1970-01-01
  • 2011-10-05
相关资源
最近更新 更多