我有一些我希望向Python公开的C代码。它有一个这样的调用约定:
int add(int a, int b, int *err)
其中返回值为(a + b)或其他什么,但如果出现问题,那么我会在*错误中得到错误代码。我想从Python的角度来包装这个函数,使它的行为像这样:
def add(a,b):
if something_bad:
raise RuntimeError("something bad")
return a+b
这应该很简单,对吗?但我没有找到它。
这是我的工作方式,但请注意myerr3
kludge:
%module myswig
%feature("autodoc","1");
%{
int add(int a, int b, int *err){
if(a < 0)*err = 1;
if(b < 0)*err = 2;
return a+b;
}
char *err_string(int err){
switch(err){
case 1:return "first argument was less than 0";
case 2:return "second argument was less than 0";
default:return "unknown error";
}
}
%}
%typemap(in,numinputs=0) int *err (int myerr = 0){
$1 = &myerr;
};
%exception{
$action
if(myerr3 != 0){
PyErr_SetString(PyExc_RuntimeError,err_string(myerr3));
return NULL;
}
};
int add(int a, int b, int *err);
这表现得应该如此,例如
import myswig
print "add(1,1) = "
print myswig.add(1,1)
# prints '2'
print "add(1,-1) = "
print myswig.add(1,-1)
# raises an exception
# we never get here...
print "here we are"
但我无法真正使用此解决方案,因为如果我有其他功能,如
int add(int a, int b, int c, int *err)
然后我的myerr3
kludge会崩溃。
在不更改C代码的调用约定的情况下,解决此问题的更好方法是什么?
答案 0 :(得分:3)
诀窍不是使用%exception
而是定义%typemap(argout)
。也不要直接引用你的临时变量。 %typemap(in)
抑制目标语言中的参数并提供本地临时变量,但您仍应在%typemap(argout)
中引用参数本身。这是原始.i文件的修改版本。我还添加了更多的通用异常抛出,因此它也适用于其他语言:
%module x
%feature("autodoc","1");
// Disable some Windows warnings on the generated code
%begin %{
#pragma warning(disable:4100 4127 4211 4706)
%}
%{
int add(int a, int b, int *err){
if(a < 0)*err = 1;
if(b < 0)*err = 2;
return a+b;
}
char *err_string(int err){
switch(err){
case 1:return "first argument was less than 0";
case 2:return "second argument was less than 0";
default:return "unknown error";
}
}
%}
%include <exception.i>
%typemap(in,numinputs=0) int *err (int myerr = 0) {
$1 = &myerr;
}
%typemap(argout) int* err {
if(*$1 != 0) {
SWIG_exception(SWIG_ValueError,err_string(*$1));
}
}
int add(int a, int b, int *err);
结果如下:
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import x
>>> x.add(1,1)
2
>>> x.add(3,4)
7
>>> x.add(-1,4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "x.py", line 73, in add
return _x.add(*args)
RuntimeError: first argument was less than 0
>>> x.add(3,-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "x.py", line 73, in add
return _x.add(*args)
RuntimeError: second argument was less than 0
答案 1 :(得分:1)
来自Karl Wette,通过swig-user邮件列表:
您可以通过移动声明来修改“in”类型映射 字体图中的“myerr”:
%typemap(in,numinputs=0, noblock=1) int *err { int myerr = 0; $1 = &myerr; };
只要每个函数中只有一个“int * err”参数,这个 应该没事。然后,您可以直接使用“myerr”而无需参数 号。
这似乎是正确的解决方案,不需要kludges。谢谢卡尔!
答案 2 :(得分:0)
如果您愿意接受它不会重入,您可以使用全局而不是myerr3
,例如:
%{
static int myerr = 0;
%}
%typemap(in,numinputs=0) int *err {
$1 = &myerr;
};
%exception{
$action
if(myerr != 0){
PyErr_SetString(PyExc_RuntimeError,err_string(myerr));
return NULL;
}
};
另一种方法是略微滥用freearg
类型地图,而不是%exception
:
// "" makes sure we don't go inside {}, which means using alloca is sane
%typemap(in,numinputs=0) int *err "*($1=alloca(sizeof(int)))=0;"
%typemap(freearg) int *err {
if (*$1 != 0) {
PyErr_SetString(PyExc_RuntimeError,err_string($1));
SWIG_fail;
}
}
或者如果你不能使用alloca
:
%typemap(in,numinputs=0) int *err {
$1=malloc(sizeof(int));
*$1=0;
}
%typemap(freearg) int *err {
if ($1 && *$1 != 0) {
PyErr_SetString(PyExc_RuntimeError,err_string($1));
// Don't leak even if we error
free($1);
$1=NULL; // Slightly ugly - we need to avoid a possible double free
SWIG_fail;
}
free($1);
$1=NULL; // even here another arg may fail
}
您可能会使用第三种可能的方法():
%{
static const int myerr1 = 0;
static const int myerr2 = 0;
static const int myerr3 = 0;
static const int myerr4 = 0;
static const int myerr5 = 0;
//...
%}
%typemap(in,numinputs=0) int *err (int myerr = 0){
$1 = &myerr;
}
%exception{
$action
// Trick: The local myerrN from the typemap "masks" the const global one!
if(myerr1 != 0 || myerr2 != 0 || myerr3 != 0 || myerr4 != 0 || myerr5 != 0) {
PyErr_SetString(PyExc_RuntimeError,err_string(myerr1|myerr2|myerr3|myerr4|myerr5));
return NULL;
}
}
诀窍是来自typemap的特定myerrN
掩盖了static const
全局变量 - if语句总是只引用一个本地常量,它是唯一可以非零的本地常量