GCC的#pragma指令可以在头文件中添加/删除编译器选项。

13

我开发了一个跨平台的库,它在套接字通信中合理使用了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

有没有办法?
4个回答

3

谢谢。虽然有点晚了,但我现在已经开始使用Go了。 :) - John Dibling

0
如果您的库是一个仅包含头文件的库,我认为唯一处理这个问题的方法是修复严格别名违规。如果违规发生在您定义的类型之间,您可以使用通常涉及联合或may_alias类型属性的技巧。如果您的库使用预定义的sockaddr类型,则可能会比较困难。

0

让我们从我认为是虚假前提开始:

错误使用此库可能导致危险的未定义行为。我希望尽我所能确保正确使用此库。

如果你的库以一种方式进行类型转换,使得 -fstrict-aliasing 失效,那么根据 C++ 标准,无论传递了什么编译器标志,都会产生未定义的行为。程序在某些编译器上似乎能够正常工作(尤其是使用 -fno-strict-aliasing),但这并不改变事实。

因此,最好的解决方案是按照 Florian 的建议:修改代码以符合 C++ 语言规范。在此之前,你将一直处于薄冰之上。

"是的,是的",你说道,"但在那之前,我能做些什么来缓解问题呢?"

我建议在库初始化期间使用 运行时检查 来检测是否已以会导致其表现异常的方式进行编译。例如:

// 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。

但我要再次强调:我的代码存在未定义的行为!不能保证它能正常工作。符合标准的 C++ 编译器可能会将其编译成返回0、崩溃或者启动全球核战争的代码!对于您已经在库中其他地方使用的代码而言,如果需要-fno-strict-aliasing参数才能按预期运行的话,同样也适用这一点。


测试代码在严格别名的情况下完全失效:它可能会或可能不会给出预期结果。GCC喜欢对表现UB的代码做奇怪的事情,包括给出矛盾的结果。例如,检查可能会在内联时给出正结果,在未内联时给出负结果(在同一程序中),或者其他任何结果。 - Ruslan
@Ruslan 是的,那就是我在最后一段所说的意思。我进一步澄清了它。 - Scott McPeak
哇,@ScottMcPeak。我希望你六年前就写了这个。 - John Dibling

0

5
这个控制警告,但似乎无法设置“-fno-strict-aliasing”。 - M.M
@M.M 那么没有办法实现这个吗? - Bilow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接