令我惊讶的是,来自 compiler 链接器的警告没有混合-fexceptions和-fno-exceptions,或者专门用于混合带有和不带有展开表的代码。我知道可能有无限的合法性和/或无法控制的方式来结束这种状态,但是我宁愿早点得到std :: terminate(),而不是这样做的(无聊的测试设置):>
runner.cpp
extern void testA();
int main() {
try {
testA();
} catch (...) {}
}
testA.cpp
#include <iostream>
extern void testB();
struct resA {
~resA() { std::cout << "release resA" << std::endl; }
};
void testA() {
resA a;
testB();
}
testB.cpp
#include <iostream>
struct resB {
~resB() { std::cout << "release resB" << std::endl; }
};
void testB() {
resB b;
throw 42;
}
让我们编译a.cpp 无异常,但其余的有异常,也将所有与异常链接在一起:
clang++ -Wall -Wextra -Wpedantic -O3 -std=c++1z a.cpp -fno-exceptions -c
clang++ -Wall -Wextra -Wpedantic -O3 -std=c++1z b.cpp test.cpp -fexceptions -c
clang++ -Wall -Wextra -Wpedantic -O3 -std=c++1z a.o b.o test.o -fexceptions
输出:release resB
我们已经有效地泄漏了资源A,也许是一个巨大的泄漏,没有人注意到,我们可以继续运行
当所有内容都用相同的标志编译时,所有资源都会按预期进行清理,因为所有展开均正确进行了
在这种情况下,即使将“可能抛出”的testB抛出,将testA标记为 noexcept 也不会导致终止
答案 0 :(得分:1)
某些ABI在某些(常见)情况下使展开表成为可选表,例如不更改堆栈指针并且不破坏任何被调用者保存的寄存器的叶函数。因此,在有和没有展开表的情况下链接代码不一定是一个错误。链接编辑器无法轻松确定展开表是因为不需要表而丢失,还是因为二进制文件未正确编译而丢失。
此外,可以在没有异常展开支持的情况下编译代码,但是具有完整的(甚至异步的)展开表。这些表不仅由C ++实现用于堆栈展开,而且还由调试和性能工具使用。无需安装调试信息即可获得准确的堆栈回溯的表提供了可观的价值,而与编程语言或所使用的语言子集无关。这就是为什么某些GNU / Linux发行版以这种方式编译所有二进制文件(包括C程序)的原因,尽管会产生很大的开销。
但是您是对的,更好的工具链诊断值得。 annobin project尝试收集许多检查以检查推荐的构建标志,但目前不涉及展开的信息。它检查-fexceptions
,因为由于GCC PR 61118,即使您曾经使用POSIX线程取消处理程序(没有发生任何实际的取消操作),也几乎需要对C代码进行强化。但是这里肯定有进行更好诊断的空间。 H.J. LU的GNU属性说明也可以解决此问题。