所以我有一个图书馆(不是我写的),不幸的是使用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,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,&old_sa); } ~signal_guard() { sigaction(N,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); }
当然这个功能的身体要简单得多,但是今天早上我想到了一个想法.这是否保证工作?这是我的想法:
>没有任何变量在调用setjmp / longjmp之间是易变的或变化的.
>我在longjmping到与setjmp相同的堆栈框架的位置,并正常返回,所以我允许代码执行编译器在函数的出口点发出的清理代码.
>它似乎按预期工作.
但我仍然觉得这可能是未定义的行为.你们有什么感想?