【问题标题】:GCC pragma to add/remove compiler options in a source fileGCC pragma 在源文件中添加/删除编译器选项
【发布时间】:2013-10-26 05:43:15
【问题描述】:

我开发了一个跨平台库,可以在套接字通信中合理使用type-punning。这个库已经在许多项目中使用,其中一些我可能不知道。

错误地使用这个库会导致危险的未定义行为。我想尽我所能确保这个库被正确使用。

当然,除了文档之外,在 G++ 下,我知道的最佳方法是使用 -fstrict_aliasing-Wstrict-aliasing 选项。

在 GCC 下有没有办法在源文件级别应用这些选项?

换句话说,我想写如下内容:

MyFancyLib.h

#ifndef MY_FANCY_LIB_H
#define MY_FANCY_LIB_H

#pragma (something that pushes the current compiler options)
#pragma (something to set -fstrict_aliasing and -Wstrict-aliasing)

// ... my stuff ...

#pragma (something to pop the compiler options)

#endif

有办法吗?

【问题讨论】:

    标签: c++ gcc compiler-options


    【解决方案1】:

    让我们从我认为是错误的前提开始:

    错误地使用这个库会导致危险的未定义行为。我想尽我所能确保这个库被正确使用。

    如果您的库确实以 -fstrict-aliasing 中断的方式进行类型双关语,那么根据 C++ 标准,它具有未定义的行为无论传递了哪些编译器标志。当使用某些标志(特别是-fno-strict-aliasing)编译时,该程序似乎可以在某些编译器上运行这一事实并没有改变这一点。

    因此,最好的解决方案是按照 Florian 所说的去做:更改代码,使其符合 C++ 语言规范。除非你这样做,否则你永远如履薄冰。

    “是的,是的”,你说,“但在那之前,我能做些什么来缓解这个问题?”

    我建议包含一个run-time check,在库初始化期间使用,以检测是否以一种会导致其行为异常的方式编译的情况。例如:

    // Given two pointers to the *same* address, return 1 if the compiler
    // is behaving as if -fstrict-aliasing is specified, and 0 if not.
    //
    // Based on https://blog.regehr.org/archives/959 .
    static int sae_helper(int *h, long *k)
    {
      // Write a 1.
      *h = 1;
    
      // Overwrite it with all zeroes using a pointer with a different type.
      // With naive semantics, '*h' is now 0.  But when -fstrict-aliasing is
      // enabled, the compiler will think 'h' and 'k' point to different
      // memory locations ...
      *k = 0;
    
      // ... and therefore will optimize this read as 1.
      return *h;
    }
    
    int strict_aliasing_enabled()
    {
      long k = 0;
    
      // Undefined behavior!  But we're only doing this because other
      // code in the library also has undefined behavior, and we want
      // to predict how that code will behave.
      return sae_helper((int*)&k, &k);
    }
    

    (以上是 C 而不是 C++,只是为了便于在两种语言中使用。)

    现在在您的初始化例程中,调用strict_aliasing_enabled(),如果它返回 1,则立即退出并显示错误消息,说明库编译不正确。这将有助于保护最终用户免受不当行为的影响,并提醒客户端程序的开发人员他们需要修复其构建。

    我已经用 gcc-5.4.0 和 clang-8.0.1 测试了这段代码。当-O2被传递时,strict_aliasing_enabled()返回1。当-O2 -fno-strict-aliasing被传递时,该函数返回0。

    但让我再次强调一下:我的代码有 undefined 行为! (可以)不能保证它会起作用。符合标准的 C++ 编译器可以将其编译为返回 0、崩溃或启动 Global Thermonuclear War! 的代码!如果您需要 -fno-strict-aliasing 使其按预期运行,那么您可能已经在库中其他地方使用的代码也是如此。

    【讨论】:

    • 测试代码在存在严格别名的情况下完全被破坏:它可能会也可能不会给出预期的结果。 GCC 喜欢用显示 UB 的代码做一些奇怪的事情,包括给出矛盾的结果。例如。该检查在内联时可能会给出肯定结果,在未内联时(在同一程序中)或其他任何情况下可能会给出否定结果。
    • @Ruslan 是的,这就是我最后一段的意思。我已经进一步澄清了。
    • 哇,@ScottMcPeak。我希望你在 6 年前写过这篇文章。
    【解决方案2】:

    如果您的库是仅标头库,我认为解决此问题的唯一方法是修复严格的别名违规。如果在您定义的类型之间发生冲突,您可以使用涉及联合的常用技巧或may_alias 类型属性。如果您的库使用预定义的 sockaddr 类型,这可能会很困难。

    【讨论】:

      【解决方案3】:

      您可以尝试诊断编译指示并更改警告的错误级别。更多细节在这里:

      http://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html

      【讨论】:

      • 这控制了警告但似乎无法设置-fno-strict-aliasing
      • @M.M 那么有没有办法做到这一点?
      猜你喜欢
      • 1970-01-01
      • 2021-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 2022-01-02
      相关资源
      最近更新 更多