【问题标题】:How do I use SWIG to return specific exceptions based on C function return type?如何使用 SWIG 根据 C 函数返回类型返回特定异常?
【发布时间】:2016-06-29 02:10:51
【问题描述】:

以下是我的 C API 中的函数示例:

int do_something(struct object *with_this)
struct object *get_something(struct object *from_this)

do_something 将返回 0 表示成功或 -1 并设置 errno 表示失败。 get_something 将在成功时返回一个有效指针,或者 NULL 并设置 errno 为失败。

我使用 SWIG 2.0 来生成 python 绑定。对于do_something 绑定,我希望它在失败时返回基于errno 的异常,如果成功则返回Python None 对象。对于get_something 绑定,我希望它在失败时返回基于errno 的异常,如果成功则返回不透明对象。

问题是如何让 SWIG 为我完成所有这些魔法?

目前我正在使用 SWIG 2.0。

我可以使用%exception 并为每个符号定义不同的异常,但我希望它基于返回类型。我会有很多 API 函数,我不想一一列举。所以我可以为每个符号执行此操作:

%exception do_something {
    $action
    if (result < 0) {
        return PyErr_SetFromErrno(PyExc_RuntimeError);
    }
}

%exception get_something {
    $action
    if (result == NULL) {
        return PyErr_SetFromErrno(PyExc_RuntimeError);
    }
}

如果我能做这样的事情会更好(就像你可以用 %typemap 做的那样):

%exception int {
    $action
    if (result < 0) {
        return PyErr_SetFromErrno(PyExc_RuntimeError);
    }
}

%exception struct object * {
    $action
    if (result == NULL) {
        return PyErr_SetFromErrno(PyExc_RuntimeError);
    }
}

我可以这样做吗?如果我有这样的东西,那么就会处理异常。而且我相信我的get_something 绑定会很完美。

如果我使用%typemap,我的do_something 绑定仍然需要这样的东西才能返回Python None 对象:

%typemap(out) int {
    $result = Py_BuildValue("");
}

我正在寻找的魔法是存在的,还是我做错了?有没有更好的方法来做到这一点?

【问题讨论】:

  • 相当肯定%exception 是您的用例的红鲱鱼,您想在%typemap(out) 甚至%pythoncode 内完成所有操作。如果没有人超过我,我会尽量记住为你写一个正确的答案。
  • 感谢柔印。我已经尝试过typemap(out),但问题是我必须重新实现类型处理,而我不想为get_something 的情况这样做。对于get_something 的情况,我只想处理异常但仍按原样处理类型。
  • 我知道你在担心什么,我会考虑一下,然后给出一些解决方案。
  • 随机评论:您是否尝试使用这一系列函数来模拟 OO 行为,并将隐式 this 参数作为所有函数的第一个参数?您可能可以在 Python 界面中做一些更简洁的事情来拯救 Python 开发人员,即使您愿意,C 也不是真正的 OO。 (但这将是另一个问题)
  • @Flexo 不完全是。我在这里的应用程序是我有一个使用下面的对象的 C API。重要的部分是我想以几乎原样使用 Python 的方式展示 C API。 Python 将使用PyCapsules 在 C API 和 Python 层之间来回传递这个不透明的对象。所有函数都将使用mutator/accessor 方法来保持不透明性。所以我希望这个原样能够使用EAFP 原则。

标签: python c swig


【解决方案1】:

为了帮助回答这个问题,我使用了你的函数并创建了以下 test.h 文件,其中包含足够的真实代码来实际说明问题:

struct object { int bar; };

static int do_something(struct object *with_this) { 
  errno = EACCES;
  return -1; 
}

static struct object *get_something(struct object *from_this) {
  errno = EAGAIN;
  return NULL;
}

我很确定%typemap(out) 是编写此代码的地方,而不是%exception,因为您想要的行为取决于返回类型,而不是调用的函数的名称。您还关心回报价值的事实进一步支持了这一点。您可以在一定程度上避免重复使用 $typemap 来引用您的其他类型映射。

所以我写了一个小小的 SWIG 界面来说明这一点:

%module test

%{
#include <errno.h>
#include "test.h"
%}

%typemap(out) int do_something %{
  if ($1 < 0) {
    PyErr_SetFromErrno(PyExc_RuntimeError);
    SWIG_fail;    
  }
  $result = Py_None; // Return only controls exception
  Py_INCREF($result);
%}

%typemap(out) struct object *get_something %{
  if (!$1) {
    PyErr_SetFromErrno(PyExc_RuntimeError);
    SWIG_fail; 
  }

  // Return passed through unless NULL
  $typemap(out,$1_ltype);
%}

%include "test.h"

测试时有效:

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.do_something(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: (13, 'Permission denied')
>>> test.get_something(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: (11, 'Resource temporarily unavailable')
>>> 

这只是因为我在匹配类型映射时明确命名do_something,所以没有命名函数的回退是好的。如果您只是尝试删除该限制,您将收到有关递归类型映射的错误。您可以使用%apply 解决此问题,例如

%typemap(out) struct object * MY_NULL_OR_ERRNO %{
  if (!$1) {
    PyErr_SetFromErrno(PyExc_RuntimeError);
    SWIG_fail;
  }

  $typemap(out,$1_ltype);
%}

%apply struct object * MY_NULL_OR_ERRNO { struct object *get_something, struct object *some_other_function };

如果这太痛苦了,你当然可以手动编写 typemap:

%typemap(out) struct object * %{
  if (!$1) {
    PyErr_SetFromErrno(PyExc_RuntimeError);
    SWIG_fail;
  }

  $result = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, $owner);
%}

如果您真的希望无处不在返回struct object*,这似乎是最简单的选择。

还有其他可能的解决方案使用%pythoncode%pythonappend,但在我看来,它们都不是上述的真正改进。

【讨论】:

  • 谢谢!这正是我所需要的。你最后的“手工打字”是我最喜欢的。我很高兴有一种方法可以通过自动变量获取所有这些信息。感谢您提供替代方案;他们肯定会派上用场。惊人的答案!
猜你喜欢
  • 1970-01-01
  • 2019-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-07
  • 2011-01-15
  • 1970-01-01
  • 2017-03-16
相关资源
最近更新 更多