Python SWIG Structure Structure

I searched for several days trying to figure out how to turn an array of structures into a Python list. I have a function that returns a pointer to the beginning of an array.

struct foo { int member; }; struct foo *bar() { struct foo *t = malloc(sizeof(struct foo) * 4); ... do stuff with the structs ... return t; } 

After calling the function from Python, I get one structure, but trying to access other elements of the array causes an error:

 foo = bar() print foo[1].member TypeError: 'foo' object does not support indexing 

I tried using %array_class , but to no avail. I also tried to define the function as returning an array in the SWIG interface file:

 extern struct foo [ANY] bar(); 

The SWIG documentation is pretty thorough, but I can't figure it out.

+7
source share
1 answer

The idea you tried using [ANY] will not work for several reasons. First of all, although ANY can be used in typemaps to allow the same type card to work with different arrays of fixed size, this is not what you have there.

The syntax for C is also not needed. You cannot write:

 int[4] bar() { static int data[4]; return data; } 

Or:

 int bar()[4] { static int data[4]; return data; } 

In the C standard. Closest you can get:

 int (*bar())[4] { static int data[4] = {1,2,3,4}; return &data; } 

But in fact it is not so easy to wrap.

However, a simple solution can be made to work with %array_class , for example:

 %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); 

This allows me to:

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

We can go one step better, although (possibly) by entering the code to automatically hide the pointer to the array type, adding the following before bar() sees SWIG:

 %pythonappend bar() %{ # Wrap it automatically val = fooArray.frompointer(val) %} 

So now you can use it like:

 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 

You need to be careful about owning memory. In these examples, a memory leak still occurs. You can use %newobject to tell SWIG that the memory belongs to the Python side, but then it will be released too soon (as soon as the original return value is no longer referenced), so you will need to arrange to keep the original value around for longer. A complete example of this, which saves the original pointer inside an instance of an array class to save the link until the array shell itself is:

 %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); 

Note that the class of the array that it generates is unlimited, just like a struct foo* in C. This means that you cannot iterate over it in Python - the size is unknown. If the size is really fixed or you have a way to find out the size, somehow you can wrap it much better (in my opinion) by writing a sample map that returns a PyList. This is a bit more work to write, but makes the interface more enjoyable on the Python side.

+8
source

All Articles