我一直在寻找几天试图弄清楚如何将结构数组转换为Python列表。我有一个函数返回指向数组开头的指针。
struct foo {
int member;
};
struct foo *bar() {
struct foo *t = malloc(sizeof(struct foo) * 4);
... do stuff with the structs ...
return t;
}
从Python调用函数后,我得到一个结构但是尝试访问数组的其他元素会导致错误:
foo = bar()
print foo[1].member
TypeError: 'foo' object does not support indexing
我尝试使用%array_class
,但无济于事。我也尝试将函数定义为在SWIG接口文件中返回一个数组:
extern struct foo [ANY] bar();
SWIG文档非常详尽,但我似乎无法弄清楚这一点。
答案 0 :(得分:8)
您尝试使用[ANY]
的想法因多种原因无效。主要是ANY
可以在类型映射中使用,以允许相同的类型映射使用不同的固定大小的数组,这不是你所拥有的。
C的语法也不在那里。你不能写:
int[4] bar() {
static int data[4];
return data;
}
或者:
int bar()[4] {
static int data[4];
return data;
}
在标准C中。最接近的是:
int (*bar())[4] {
static int data[4] = {1,2,3,4};
return &data;
}
但这并不是更容易包装。
但是,可以使用%array_class
使用简单的解决方案,例如:
%module test
%inline %{
struct foo {
int member;
};
struct foo *bar() {
struct foo *arr = malloc(sizeof(struct foo) * 4);
for (int i = 0; i < 4; ++i)
arr[i].member = i;
return arr;
}
%}
%include <carrays.i>
%array_class(struct foo, fooArray);
这让我这样做:
Python 3.2.3 (default, May 3 2012, 15:54:42)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> arr = test.fooArray.frompointer(test.bar())
>>> arr
<test.fooArray; proxy of <Swig Object of type 'fooArray *' at 0xb6f332a8> >
>>> arr[0]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f33038> >
>>> arr[1]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f33380> >
>>> arr[2]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f33398> >
>>> arr[3]
<test.foo; proxy of <Swig Object of type 'struct foo *' at 0xb6f330c8> >
>>>
我们可以通过注入代码来自动转换指向数组类型的指针,通过在SWIG看到bar()
之前添加以下内容,可以更好地向前迈出一步:
%pythonappend bar() %{
# Wrap it automatically
val = fooArray.frompointer(val)
%}
所以你现在可以像:
一样使用它Python 3.2.3 (default, May 3 2012, 15:54:42)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.bar()[1].member
1
>>> arr = test.bar()
>>> arr[3].member
3
您需要注意内存所有权。在这些例子中,到目前为止,存储器已泄露。您可以使用%newobject
告诉SWIG内存由Python端拥有,但是它会过早发布(一旦原始返回值不再被引用),那么您需要安排保持原来的价值更长。一个完整的例子,它将原始指针保存在数组类的实例中,以便只要数组包装器本身就保持引用:
%module test
%pythonappend bar() %{
# Wrap it automatically
newval = fooArray.frompointer(val)
newval.ptr_retain = val
val = newval
%}
%newobject bar();
%inline %{
struct foo {
int member;
};
struct foo *bar() {
struct foo *arr = malloc(sizeof(struct foo) * 4);
for (int i = 0; i < 4; ++i)
arr[i].member = i;
return arr;
}
%}
%include <carrays.i>
%array_class(struct foo, fooArray);
请注意,这个生成的数组类是无界的,就像C中的struct foo*
一样。这意味着你不能在Python中迭代它 - 大小未知。如果大小是真正修复的,或者你有办法知道大小,你可以通过编写一个返回PyList的类型图以更好的方式(在我的视图中)包装它。编写它需要做更多的工作,但是在Python方面使界面看起来更好。