2012-08-17 73 views
7

我一直在寻找几天,试图找出如何将结构数组转换为Python列表。我有一个函数返回一个指向数组的开始的指针。SWIG 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尝试,但都无济于事。我也试着定义函数作为痛饮接口文件返回一个数组:

extern struct foo [ANY] bar(); 

的SWIG文档是很透彻,但我似乎无法想出解决办法。

+1

你看过这里吗? - http://stackoverflow.com/questions/8114030/swig-python-array-inside-structure – dpandiar 2012-08-17 02:33:55

+0

@dpandiar - 这是一个非常不同的情况,因为大小是固定的和已知的,并且数组是成员而不是从函数返回值 – Flexo 2012-08-17 10:12:37

回答

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> > 
>>> 

我们可以去一步到位,但(可能)由注入代码以自动将指针转换为阵列类型,在SWIFT之前添加以下内容: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方面看起来更好一些。

+0

非常感谢!这正是我正在寻找和完美工作。我知道我没有包含的计算数组的大小以简化示例。我之前查看过carrays.i的文档,但我对此感到困惑:“使用此宏时,类型限制为简单的类型名称,如int,float或Foo。”我一直试图使用'struct foo *'类型,当它不工作时,假设它只适用于基本的c类型。 – user1604630 2012-08-17 23:08:41