c – longjmp和RAII

所以我有一个图书馆(不是我写的),不幸的是使用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相同的堆栈框架的位置,并正常返回,所以我允许代码执行编译器在函数的出口点发出的清理代码.
>它似乎按预期工作.

但我仍然觉得这可能是未定义的行为.你们有什么感想?

解决方法

我假设f在第三方库/应用程序,因为否则你可以修复它,不要中止.鉴于此,RAII可能会或可能无法在所有平台/编译器上产生正确的结果,您可以选择几个.

>创建一个定义中止和LD_PRELOAD的小型共享对象.然后,您可以控制在中止时发生的情况,而不是在信号处理程序中.>在子进程内运行f.然后,您只需检查返回代码,如果失败,再次尝试更新输入.>而不是使用RAII,只需从多个呼叫点调用您的原始功能,并让它明确地手动进行设置/拆卸.在这种情况下,它仍然消除了复制粘贴.

相关文章

对象的传值与返回说起函数,就不免要谈谈函数的参数和返回值...
从实现装饰者模式中思考C++指针和引用的选择最近在看...
关于vtordisp知多少?我相信不少人看到这篇文章,多半是来自...
那些陌生的C++关键字学过程序语言的人相信对关键字并...
命令行下的树形打印最近在处理代码分析问题时,需要将代码的...
虚函数与虚继承寻踪封装、继承、多态是面向对象语言的三大特...