关于异常:可移植地处理C ++中的异常错误

关于异常:可移植地处理C ++中的异常错误

Portably handle exceptional errors in C++

我正在将Visual C ++应用程序移植到GCC(应在MingW和Linux上构建)。

现有代码在某些地方使用__try { ... } __except(1) { ... }块,因此几乎没有什么(可能缺少内存类型错误?)会使程序退出而无需进行最少的日志记录。

使用GCC做类似的事情有哪些选择?

编辑:感谢Visual Studio中指向/ EH选项的指针,我现在需要的是一些有关如何在Linux上处理信号的示例。我发现这是2002年的消息。

除了SIGFPESIGSEVG之外,我还应该注意其他哪些信号? (最关心那些可能因我做错事而引起的问题)

赏金信息:
我希望我的应用程序能够在退出之前自动记录尽可能多的错误情况。

我会得到什么信号,之后通常无法记录错误消息? (内存不足,还有什么?)

我如何以一种可移植的方式处理异常和(最重要的)信号,以确保代码至少在Linux和MingW上可以正常工作。 #ifdef可以。

我不仅拥有记录故障的包装过程的原因是,出于性能方面的考虑,我将一些数据保存到磁盘上直到最后一分钟,因此,如果出现问题,我想尝试所有可能的方法,然后再将数据写出退出。


尝试{xxx} catch(...){xxx}更具可移植性,但可能捕获得很少。这取决于编译器设置和环境。

使用默认的VC ++设置,异步(SEH)错误不会传递到C ++ EH基础结构中。要捕获它们,您需要改用SEH处理程序(__try / __ except)。 VC ++允许您通过C ++错误处理来路由SEH错误,这可以使catch(...)捕获SEH错误。这包括内存错误,例如空指针取消引用。细节。

但是,在Linux上,Windows使用SEH的许多错误是通过信号指示的。这些永远不会被try / catch捕获;要处理它们,您需要一个信号处理程序。


catch(...)捕获C ++异常已经使您处于暮光之城。

尝试捕获未被catch(...)捕获的错误会使您直接陷入未定义的行为中。没有C ++代码可以保证正常工作。您的最小记录代码可能会导致导弹发射。

我的建议是甚至不要尝试catch(...)。仅捕获可以有意义地安全记录的异常,并让操作系统处理其余的异常(如果有)。

如果您根本原因之外有错误处理代码故障,则事后调试会变得很丑陋。


对于可移植性,要尝试的一件事是对大多数常见异常使用try-catch块,然后将终止处理程序(set_terminate_handler)设置为对灾难性退出条件具有最小的钩子。您也可以尝试添加类似atexit或on_exit处理程序的内容。当您输入这些功能时,您的执行环境可能很奇怪或已损坏,因此请注意您假定的环境是否合理。

最后,当使用常规try-catch对时,可以考虑使用函数try块,而不是在函数主体中打开try块:

1
2
3
4
5
int foo(int x) try {
  // body of foo
} catch (...) {
   // be careful what's done here!
}

它们是C ++相对未知的部分,即使在部分(小规模)堆栈损坏的情况下,在某些情况下也可以提供恢复。

最后,是的,您可能需要研究可以自己连续处理的信号或可能中止的信号,并且如果希望使用较少的处理机制,则可以考虑调用new运算符的非抛出版本,并在需要时进行编译以不生成浮点异常(您可以始终在FP结果上检查isnan(。),isfinite(。)来保护自己)。

关于最后一点,请注意:我注意到浮点结果分类函数可以在linux和Windows下的不同标头中...因此您可能必须条件化这些包含。

如果您感到不高兴,请使用setjmp和longjmp编写所有内容(这是个玩笑...)。


为什么不使用C ++标准例外而不是MSFT的专有扩展? C ++具有异常处理概念。

1
2
3
4
5
6
7
8
9
struct my_exception_type : public logic_error {
    my_exception_type(char const* msg) : logic_error(msg) { }
};

try {
    throw my_exception_type("An error occurred");
} catch (my_exception_type& ex) {
    cerr << ex.what << endl;
}

C ++还具有一个" catchall"子句,因此,如果要记录异常,可以使用以下包装器:

1
2
3
4
5
try {
    // …
}
catch (...) {
}

但是,这在C ++中不是很有效,因为创建这样的通用包装器意味着必须由编译器将处理代码插入每个后续堆栈帧中(不同于.NET等受管系统中,只要实际上不会引发任何异常)。


一种易于使用,可移植且几乎不使用任何资源的方法是捕获空类。我知道一开始听起来可能很奇怪,但它可能非常有用。

这是我为另一个问题也适用于您的问题的示例:链接

另外,您可以捕获多个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try
{
   /* code that may throw exceptions */
}
catch (Error1 e1)
{
   /* code if Error1 is thrown */
}
catch (Error2 e2)
{
   /* code if Error2 is thrown */
}
catch (...)
{
   /* any exception that was not expected will be caught here */
}


推荐阅读