SWIG封装的C库引发异常的优雅方法

4 投票
2 回答
1254 浏览
提问于 2025-04-16 14:30

我最近把一个库从C++转换成了C,这个库最开始是用C++写的,并且用Boost Python进行了封装。现在我用SWIG进行了C的封装,这样可以支持更多的编程语言。我之所以从C++转到C,是因为这个库其实只是一些函数,而且我希望这个库可以直接用C调用(这样就不需要用C++编译器编译整个程序了)。

不过,有一件事转移过来并不简单,就是有一小部分函数需要能够报告错误。在C++和Boost Python中,这个功能是通过throw和异常处理来优雅实现的。

那么,有什么好的方法可以让这部分函数在C和封装后的语言中都能报告错误呢?

2 个回答

0

看看理查德·R·汉森的《C接口与实现》第四章。

5

这是我处理的方式。%{...%} 这个块的内容会插入到包装文件中。%exception 块是 SWIG 的异常处理部分,它会在每次函数调用后运行,检查是否有错误发生,如果有的话,就会用 PyErr_SetString 抛出一个异常。然后,如果你想在 C 函数中抛出异常,只需简单地调用 "set_err("错误信息");"。

%{
    /* Exception helpers */
    static int swig_c_error_num = 0;
    static char swig_c_err_msg[256];

    const char *err_occurred()
    {
        if (swig_c_error_num) {
            swig_c_error_num = 0;
            return (const char*)swig_c_err_msg;
        }
        return NULL;
    }

    void set_err(const char *msg)
    {
        swig_c_error_num = 1;
        strncpy(swig_c_err_msg, msg, 256);
    }
%}

%exception {
    const char *err;
    $action
    if (err = err_occurred()) {
        PyErr_SetString(PyExc_RuntimeError, err);
        return NULL;
    }
}

另外,如果你的 C 库使用了一套标准的返回代码,你可以在 %exception 块中用检查函数的返回代码来替代这个机制。

%exception {
    int rc;
    rc = $action;

    if (rc == ERR) {
        PyErr_SetString(PyExc_RuntimeError, <err msg>);
        return NULL;
    }
}

撰写回答