除了在每个函数原型上添加属性之外,有没有其他方法让gcc知道C函数永远不会传播异常,即所有声明在extern "C"
内的函数都应该是__attribute__((nothrow))
?理想情况下,应该有一种类似于-f
的命令行选项。
您可以始终使用-fno-exceptions
,这将确保C++编译器不生成异常传播代码。
puts()
。即使在-O3下,GCC 4.8也拒绝生成明显的直线代码;它总是会生成一个额外的(死)代码路径,以调用_Unwind_Resume
。想象一下程序中每个作用域的这些额外的死块,您就会开始了解问题的规模……即使在桌面系统上。(可执行文件的文件大小是一个问题。Icache污染是另一个问题。) - Quuxplusone顺便提一下:
你确定告诉编译器“所有这些函数都不会抛出异常”正是你想要的吗?
并不是说extern "C" ...
函数不能传播/触发异常。拿一个例子来说明:
class Foo {
public:
class Away {};
static void throwaway(void) { throw Away(); }
}
extern "C" {
void wrap_a_call(void (*wrapped)(void)) { wrapped(); }
}
int main(int argc, char **argv)
{
wrap_a_call(Foo::throwaway);
return 0;
}
wrap_a_call()
,当像上面那样调用它时,它会愉快地引发异常:$ ./test
terminate called after throwing an instance of 'Foo::Away'
Abort(coredump)
即使在C++中使用/调用extern "C"
函数,也可能会出现"异常泄漏"(通过调用函数指针),并且仅仅因为你在特定的地方使用/调用extern "C"
函数,并不能保证在调用这些函数时不会抛出异常。
longjmp
破坏),否则在C++到C到C++调用/回调边界上传播异常已经很危险了(对于C标准库中的函数,这是未定义行为)。 - R.. GitHub STOP HELPING ICE当异常被抛出时,它会生成中断,这会展开堆栈并覆盖现有的堆栈。它会一直上升到try / except语法所在的点。这意味着如果您不使用异常,则不会有任何开销。唯一的内存/时间开销是在try / catch块和throw()中展开堆栈。
如果您的C函数不会生成异常,则仅在调用C++中的try / catch时存在空间开销,但对于任何数量的异常都是相同的。(以及在初始化此小空间时带有常数的小时间开销)。
- GCC 现在优化异常处理代码。特别是被证明没有任何效果的清理区域将被优化掉。
extern "C"
函数确实不能抛出异常(具有定义行为)吗?如果是这样,您希望 GCC 在没有选项的情况下进行优化。但是,如果该函数被声明为extern "C"
,但(与您的代码不同)在 C++ 中实现,则我认为在实践中它可以抛出异常,只要它实际上没有从 C 调用。我记不清这是否合法。 - Steve Jessopqsort
的情况),也不能期望异常能够跨越外部函数(C)边界传播。然而,gcc开发人员希望将异常作为C语言的一部分(可以使用-fexceptions
作为“GNU C”的一部分),并且希望在回调等情况下支持跨C/C++代码边界的异常处理。因此,我们处于这样的混乱境地。 - R.. GitHub STOP HELPING ICE#ifndef THAT_ATTRIBUTES #ifdef __cplusplus #define THAT_ATTRIBUTES __attribute__((nothrow)) #else #define THAT_ATTRIBUTES
,以避免对common_junk.h
的依赖。我理解你反对垃圾代码的立场,不过,写出合适的C代码并期望实现能够合理处理它是更好的选择。 - Steve Jessop