【发布时间】:2011-07-20 14:44:49
【问题描述】:
所以我有一个库(不是我写的),不幸的是它使用abort() 来处理某些错误。在应用程序级别,这些错误是可以恢复的,所以我想处理它们而不是让用户看到崩溃。所以我最终写了这样的代码:
static jmp_buf abort_buffer;
static void abort_handler(int) {
longjmp(abort_buffer, 1); // perhaps siglongjmp if available..
}
int function(int x, int y) {
struct sigaction new_sa;
struct sigaction old_sa;
sigemptyset(&new_sa.sa_mask);
new_sa.sa_handler = abort_handler;
sigaction(SIGABRT, &new_sa, &old_sa);
if(setjmp(abort_buffer)) {
sigaction(SIGABRT, &old_sa, 0);
return -1
}
// attempt to do some work here
int result = f(x, y); // may call abort!
sigaction(SIGABRT, &old_sa, 0);
return result;
}
不是很优雅的代码。由于这种模式最终不得不在代码的几个地方重复,我想稍微简化一下,并可能将它包装在一个可重用的对象中。我的第一次尝试涉及使用 RAII 来处理信号处理程序的设置/拆卸(需要完成,因为每个函数需要不同的错误处理)。所以我想出了这个:
template <int N>
struct signal_guard {
signal_guard(void (*f)(int)) {
sigemptyset(&new_sa.sa_mask);
new_sa.sa_handler = f;
sigaction(N, &new_sa, &old_sa);
}
~signal_guard() {
sigaction(N, &old_sa, 0);
}
private:
struct sigaction new_sa;
struct sigaction old_sa;
};
static jmp_buf abort_buffer;
static void abort_handler(int) {
longjmp(abort_buffer, 1);
}
int function(int x, int y) {
signal_guard<SIGABRT> sig_guard(abort_handler);
if(setjmp(abort_buffer)) {
return -1;
}
return f(x, y);
}
当然function 的主体更加这样更简单、更清晰,但今天早上我想到了。 这能保证有效吗?这是我的想法:
- 在调用
setjmp/longjmp之间没有变量是易失的或变化的。 - 我
longjmping 到与setjmp和returning 通常位于同一堆栈帧中的位置,因此我允许代码执行编译器在退出点发出的清理代码功能。 - 它似乎按预期工作。
但我仍然觉得这可能是未定义的行为。大家觉得呢?
【问题讨论】:
-
为什么不使用 C++ 异常?它可以轻松解决您的问题。
-
@Hans:c++ 异常不会捕获
abort调用。 -
把它们扔到你的 abort_handler() 中,这没什么不同。
-
@Hans 在信号处理程序中抛出异常并不能保证在所有平台上都能正常工作,因此本质上是不可移植的。
标签: c++ raii signal-handling setjmp