SWIG Python Structure Array

ε祈祈猫儿з 提交于 2019-12-10 03:50:56

问题


I've been searching for a few 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 the 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 a single structure but trying to access the other elements of the array causes an error:

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

I've tried using %array_class but to no avail. I've also tried defining 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 seem to figure this out.


回答1:


The idea you tried with [ANY] won't work for several reasons. Primarily though ANY can be used in typemaps to allow the same typemap to work with varying fixed size arrays, which isn't what you've got there.

The syntax for C isn't quire right there either. You can't write:

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

Or:

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

In standard C. The closest you can get is:

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

But that's not really any easier to wrap.

However, the simple solution can be made to work using %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 lets me do:

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 though (possibly) by injecting the code to to covert the pointer to the array type automatically, by adding the following before bar() is seen by SWIG:

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

So you can now 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 memory ownership. In these examples so far the memory is leaked. You can use %newobject to tell SWIG that the memory is owned by the Python side, but then it will get released too early (as soon as the original return value is no longer referenced) so you would need to arrange to keep the original value around longer. A complete example of that, which saves the original pointer inside the instance of the array class to keep a reference around as long as the array wrapper itself would be:

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

Notice though that the array class this generates is unbounded, exactly like a struct foo* in C. This means you can't iterate over it in Python - the size is unknown. If the size is genuinely fixed, or you have a way of knowing the size somehow you can wrap this in a much nicer (in my view) way by writing a typemap that returns a PyList. It's a bit more work to write, but makes the interface seem nicer on the Python side.



来源:https://stackoverflow.com/questions/11998369/swig-python-structure-array

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!