如何在SWIG包装库中传播C ++异常到Python? [英] How do I propagate C++ exceptions to Python in a SWIG wrapper library?

查看:218
本文介绍了如何在SWIG包装库中传播C ++异常到Python?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing a SWIG wrapper around a custom C++ library which defines its own C++ exception types. The library's exception types are richer and more specific than standard exceptions. (For example, one class represents parse errors and has a collection of line numbers.) How do I propagate those exceptions back to Python while preserving the type of the exception?

推荐答案

我知道这个问题是几个星期的时间,但是我正在为自己研究一个解决方案。所以我会采取一个刺痛的答案,但我会提前警告,它可能不是一个有吸引力的解决方案,因为swig接口文件可能比手写编码更复杂的包装。此外,据我所知,swig文档从不直接处理用户定义的异常。

I know this question is a few weeks old but I just found it as I was researching a solution for myself. So I'll take a stab at an answer, but I'll warn in advance it may not be an attractive solution since swig interface files can be more complicated than hand coding the wrapper. Also, as far as I can tell, the swig documentation never deals directly with user defined exceptions.

假设您要从C ++代码模块中抛出以下异常, mylibrary.cpp,以及一些错误信息被捕获到您的python代码中:

Let's say you want to throw the following exception from your c++ code module, mylibrary.cpp, along with a little error message to be caught in your python code:

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

MyException是其他地方定义的用户异常。

MyException is a user exception defined elsewhere.

在开始之前,请记下这个异常应该如何在你的python中被捕获。为了我们的目的,我们假设你有python代码如下:

Before you begin, make a note of how this exception should be caught in your python. For our purposes here, let's say you have python code such as the following:

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

我认为这描述了你的问题。

I think this describes your problem.

我的解决方案涉及到你的swig界面文件(mylibrary.i)添加四个如下:

My solution involves making four additions to your swig interface file (mylibrary.i) as follows:

步骤1:
在标头指令(通常未命名的%{...%}块)中添加指向python感知异常的指针的声明,我们称之为pMyException。下面的步骤2将定义:

Step 1: In the header directive (the usually unnamed %{...%} block) add a declaration for the pointer to the python aware exception, which we will call pMyException. Step 2 below will define this:

%{
#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当前在其构造的包装文件中需要的那一点) - pMyException在上面的步骤1中被声明:

Step 2: Add an initialization directive (the "m" is particularly egregious, but that's what swig v1.3.40 currently needs at that point in its constructed wrapper file) - pMyException was declared in step 1 above:

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

步骤3:
如之前的一篇文章所述,需要一个例外指令 - 注意%except(python)已被弃用。这将将c +++函数do_something包含在try-except块中,该块捕获c ++异常并将其转换为上述步骤2中定义的python异常:

Step 3: As mentioned in an earlier post, we need an exception directive - note "%except(python)" is deprecated. This will wrap the c+++ function "do_something" in a try-except block which catches the c++ exception and converts it to the python exception defined in step 2 above:

%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设置了一个python wrap(一个shadow模块)围绕.pyd dll,我们还需要确保我们的python代码可以透视到.pyd文件。以下为我工作,最好直接编辑swig py包装代码:

Step 4: Because swig sets up a python wrapping (a 'shadow' module) around the .pyd dll, we also need to make sure our python code can 'see through' to the .pyd file. The following worked for me and it's preferable to editing the swig py wrapper code directly:

%pythoncode %{
    MyException = _mylibrary.MyException
%}

这可能是太晚了,原来的海报,但也许其他人会发现上面的建议有些使用。对于小工作,您可能希望使用手写编码的c ++扩展包装器来清除swig界面文件的混淆。

It's probably too late to be of much use to the original poster, but perhaps somebody else will find the suggestions above of some use. For small jobs you may prefer the cleanness of a hand-coded c++ extension wrapper to the confusion of a swig interface file.

添加:

在我上面的界面文件列表中,我省略了标准模块指令,因为我只想描述使异常工作所需的增加。但您的模块行应该如下所示:

In my interface file listing above, I omitted the standard module directive because I only wanted to describe the additions necessary to make exceptions work. But your module line should look like:

%module mylibrary

另外,你的setup.py(如果你正在使用distutils,我建议至少开始使用)应该有类似于以下代码,否则第4步将失败_mylibrary无法识别:

Also, your setup.py (if you are using distutils, which I recommend at least to get started) should have code similar to the following, otherwise step 4 will fail when _mylibrary is not recognized:

/* 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"],)

注意我需要的编译标志/ EHsc Windows编译异常。您的平台可能不需要该标志;谷歌有细节。

Note the compile flag /EHsc, which I needed on Windows to compile exceptions. Your platform may not require that flag; google has the details.

我已经使用我自己的名称测试了代码,我已经转换为mylibrary和MyException来帮助推广解决方案。希望转录错误很少或没有。要点是%init和%pythoncode指令以及

I have tested the code using my own names which I have converted here to "mylibrary" and "MyException" to help in generalizing the solution. Hopefully the transcription errors are few or none. The main points are the %init and %pythoncode directives along with

static PyObject* pMyException;

在标题指令中。

希望澄清了解决方案。

这篇关于如何在SWIG包装库中传播C ++异常到Python?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆