如何在SWIG包装库中向Python传播C++异常?

2024-04-26 18:05:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在编写一个自定义的C++库,它定义了自己的C++异常类型。库的异常类型比标准异常更加丰富和具体。(例如,一个类表示解析错误并有一个行号集合。)如何将这些异常传播回Python,同时保留异常的类型?


Tags: 类型标准定义错误行号
3条回答

swig documentation

%except(python) {
try {
$function
}
catch (RangeError) {
    PyErr_SetString(PyExc_IndexError,"index out-of-bounds");
    return NULL;
}
}

我将在这里添加一点,因为这里给出的示例现在说,“%except(python)”是 已弃用。。。

你现在可以(从swig 1.3.40开始,无论如何)做完全通用的,脚本语言独立的翻译。我的例子是:

%exception { 
    try {
        $action
    } catch (myException &e) {
        std::string s("myModule error: "), s2(e.what());
        s = s + s2;
        SWIG_exception(SWIG_RuntimeError, s.c_str());
    } catch (myOtherException &e) {
        std::string s("otherModule error: "), s2(e.what());
        s = s + s2;
        SWIG_exception(SWIG_RuntimeError, s.c_str());
    } catch (...) {
        SWIG_exception(SWIG_RuntimeError, "unknown exception");
    }
}

这将在任何受支持的脚本语言中生成运行时错误异常, 包括Python,而不在其他头文件中获取Python特定的内容。

您需要将此放在需要此异常处理的调用之前。

我知道这个问题有几个星期了,但我只是在自己研究解决方案时发现的。因此,我将尝试一个答案,但我会提前警告,这可能不是一个有吸引力的解决方案,因为swig接口文件可能比手工编写包装器更复杂。而且,据我所知,swig文档从不直接处理用户定义的异常。

假设您希望抛出c++代码模块mylibrary.cpp中的以下异常,以及要在python代码中捕获的一些错误消息:

throw MyException("Highly irregular condition...");  /* C++ code */

MyException是在别处定义的用户异常。

在开始之前,请记下应该如何在python中捕获此异常。在这里,假设您有如下python代码:

import mylibrary
try:
    s = mylibrary.do_something('foobar')
except mylibrary.MyException, e:
    print(e)

我想这说明了你的问题。

我的解决方案包括向swig接口文件(mylibrary.i)添加以下四项:

步骤1: 在header指令(通常是未命名的{…%}块)中,为指向python感知异常的指针添加一个声明,我们将调用pMyException。下面的第2步将对此进行定义:

%{
#define SWIG_FILE_WITH_INIT  /* for eg */
extern char* do_something(char*);   /* or #include "mylibrary.h" etc  */
static PyObject* pMyException;  /* add this! */
%}

步骤2: 添加一个初始化指令(“m”特别离谱,但这正是swig v1.3.40当前在其构造的包装文件中所需要的)-在上面的步骤1中声明了pMyException:

%init %{
    pMyException = PyErr_NewException("_mylibrary.MyException", NULL, NULL);
    Py_INCREF(pMyException);
    PyModule_AddObject(m, "MyException", pMyException);
%}

步骤3: 如前一篇文章中所述,我们需要一个异常指令—注意,%except(python)已被弃用。这将在try-except块中包装c+++函数“do-u something”,该块捕获c++异常并将其转换为在上面的步骤2中定义的python异常:

%exception do_something {
    try {
        $action
    } catch (MyException &e) {
        PyErr_SetString(pMyException, const_cast<char*>(e.what()));
        SWIG_fail;
    }
}

/* The usual functions to be wrapped are listed here: */
extern char* do_something(char*);

步骤4: 由于swig在.pyd dll周围设置了一个python包装(一个“影子”模块),我们还需要确保python代码可以“看穿”到.pyd文件。下面的代码对我有用,最好直接编辑swig py包装器代码:

%pythoncode %{
    MyException = _mylibrary.MyException
%}

对原版海报有多大用处可能为时已晚,但也许其他人会发现上面的建议有些用处。对于小型作业,您可能更喜欢手工编码的c++扩展包装器,而不是swig接口文件的混乱。


添加:

在上面的接口文件清单中,我省略了标准的module指令,因为我只想描述使异常工作所必需的附加内容。但你的模块线应该是:

%module mylibrary

另外,setup.py(如果您使用distutils,我建议您至少开始使用distutils)应该具有类似于以下的代码,否则,当无法识别mylibrary时,步骤4将失败:

/* setup.py: */
from distutils.core import setup, Extension

mylibrary_module = Extension('_mylibrary', extra_compile_args = ['/EHsc'],
                    sources=['mylibrary_wrap.cpp', 'mylibrary.cpp'],)

setup(name="mylibrary",
        version="1.0",
        description='Testing user defined exceptions...',
        ext_modules=[mylibrary_module],
        py_modules = ["mylibrary"],)

注意compile flag/EHsc,这是我在Windows上编译异常所需要的。你的平台可能不需要这个标志;谷歌有详细信息。

我已经用自己的名字测试了代码,我在这里将其转换为“mylibrary”和“MyException”,以帮助概括解决方案。希望转录错误很少或没有。要点是%init和%pythoncode指令以及

static PyObject* pMyException;

在header指令中。

希望这能澄清解决方案。

相关问题 更多 >