SWIG类型映射:Python输入和输出数组
我有一个C语言写的函数,想在Python中使用:
extern int convertAtoB( stateStruct *myStruct,
const double PointA[3],
double PointB[3]);
我想用SWIG这个工具,感觉需要定义一个类型映射(typemap),来把两个点(输入的PointA和输出的PointB)转换成Python能用的格式。不过在typemaps.i文件里似乎没有适合这个的类型映射,所以我得自己定义一个。我在SWIG的文档里找不到关于数组的例子。
我想像这样使用这个库:
s = externalStruct()
point_a = [1, 2, 3]
result, point_b = convertAtoB(s, point_a)
print point_b
"expect [4, 5, 6]"
我该怎么做呢?谢谢!
3 个回答
1
这是一个老话题,但我来回答一下,因为关于SWIG的帖子并不多。
为了特别针对上面的情况
%typemap(in, numinputs=0) double PointB[3] {
double tmp[3];
$1 = tmp;
}
%typemap(argout) double PointB[3] {
PyObject *o = PyList_New(3);
int i;
for(i=0; i<3; i++)
{
PyList_SetItem(o, i, PyFloat_FromDouble($1[i]));
}
$result = o;
}
3
你快到了。为了去掉Python函数签名中的多余参数,你需要把 %typemap(in)
对 PointB[3]
的设置改成 %typemap(in,numinputs=0)
,这样可以告诉SWIG忽略这个输入值(反正你已经在复制它了)。这样就能把多余的参数从Python的方法签名中去掉。
不过我不太确定你是否需要为这个特殊情况复制整个 %typemap(in)
。可能有办法重用现有的typemap,但我不知道怎么做。否则你可能会得到一个额外的
%typemap(in,numinputs=0) double PointB[3] (double temp[$1_dim0]) {
int i;
if (!PySequence_Check($input)) {
PyErr_SetString(PyExc_ValueError,"Expected a sequence");
return NULL;
}
if (PySequence_Length($input) != $1_dim0) {
PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected $1_dim0 elements");
return NULL;
}
for (i = 0; i < $1_dim0; i++) {
PyObject *o = PySequence_GetItem($input,i);
if (PyNumber_Check(o)) {
temp[i] = (double) PyFloat_AsDouble(o);
} else {
PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers");
return NULL;
}
}
$1 = temp;
}
2
这是我找到的一个解决方案,但可能不是最好的:
%typemap(in) double[ANY] (double temp[$1_dim0]) {
int i;
if (!PySequence_Check($input)) {
PyErr_SetString(PyExc_ValueError,"Expected a sequence");
return NULL;
}
if (PySequence_Length($input) != $1_dim0) {
PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected $1_dim0 elements");
return NULL;
}
for (i = 0; i < $1_dim0; i++) {
PyObject *o = PySequence_GetItem($input,i);
if (PyNumber_Check(o)) {
temp[i] = (double) PyFloat_AsDouble(o);
} else {
PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers");
return NULL;
}
}
$1 = temp;
}
这是我最终找到的文档中的一个例子,它展示了如何把Python的列表转换成数组。接下来的部分比较难,我需要把几个例子拼凑在一起,才能把返回的数组转换成Python的列表:
%typemap(argout) double PointB[3]{
PyObject *o = PyList_New(3);
int i;
for(i=0; i<3; i++)
{
PyList_SetItem(o, i, PyFloat_FromDouble($1[i]));
}
$result = o;
}
不过,我必须为API中的每个返回值都创建一个这样的东西。而且我还得用一个虚拟值作为参数来调用它:
point_b = convertAtoB(s, point_a, dummy)
有没有更好的方法呢?