我开发了一个跨平台的库,可以在套接字通信中合理使用type-punning。该库已经在许多项目中使用,其中一些我可能不知道。
错误地使用此库可能会导致危险的未定义行为。我想尽我所能确保正确使用这个库。
除了文档当然,在G ++下,我知道这样做的最好方法是使用-fstrict_aliasing
和-Wstrict-aliasing
选项。
GCC下是否有办法在源文件级别应用这些选项?
换句话说,我想写下面的内容:
#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
有办法吗?
答案 0 :(得分:1)
让我们从我认为是错误的前提开始:
错误地使用此库可能导致危险的不确定行为。我想尽我最大的能力确保该库的正确使用。
如果您的库确实以-fstrict-aliasing
中断的方式键入punning,则根据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
使它按预期运行,那么对于您可能已经在库中其他地方使用的代码也是如此。
答案 1 :(得分:0)
您可以尝试使用Diagnostic pragma并更改警告的错误级别。更多细节在这里:
答案 2 :(得分:0)
如果您的库是一个仅限标题的库,我认为处理此问题的唯一方法是修复严格的别名冲突。如果在您定义的类型之间发生违规,则可以使用涉及联合的常用技巧或may_alias
类型属性。如果您的库使用预定义的sockaddr
类型,则可能很难。