2012-08-17 14 views
7

He estado buscando por unos días tratando de descubrir cómo convertir una matriz de estructuras en una lista de Python. Tengo una función que devuelve un puntero al comienzo de la matriz.SWIG Python Structure Array

struct foo { 
    int member; 
}; 

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

Después de llamar a la función de Python consigo una única estructura, sino tratar de acceder a los otros elementos de la matriz provoca un error:

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

He intentado usar %array_class pero fue en vano. También he intentado definir la función que devuelve una matriz en el archivo de interfaz SWIG:

extern struct foo [ANY] bar(); 

documentación El TRAGO es bastante exhaustiva pero parece que no puede resolver esto.

+1

¿Has visto aquí? - http://stackoverflow.com/questions/8114030/swig-python-array-inside-structure – dpandiar

+0

@dpandiar - ese es un caso completamente diferente porque el tamaño es fijo y conocido y las matrices son miembros y no devuelven valores de una función – Flexo

Respuesta

8

La idea que ha intentado con [ANY] no funcionará por varias razones. Sin embargo, principalmente, ANY se puede usar en mapas de tipos para permitir que el mismo mapa de tipos funcione con varias matrices de tamaño fijo, que no es lo que tienes allí.

La sintaxis para C no está allí tampoco. No se puede escribir:

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

O:

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

En estándar de C. Lo más cerca que se puede obtener es:

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

Pero eso no es realmente más fácil para envolver.

Sin embargo, la solución simple puede hacerse funcionar usando %array_class, por ejemplo:

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

Esto me permite hacer:

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

podemos ir un paso más, aunque (posiblemente) por inyectando el código para encubrir el puntero al tipo de matriz automáticamente, agregando lo siguiente antes de bar() es visto por SWIG:

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

Así que ahora puede utilizarlo como:

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 

Es necesario tener cuidado con la propiedad de memoria. En estos ejemplos, hasta ahora se ha filtrado la memoria. Puede usar %newobject para decirle a SWIG que la memoria pertenece al lado de Python, pero luego se liberará demasiado pronto (tan pronto como ya no se haga referencia al valor de retorno original), por lo que tendría que organizarse para mantener el valor original más. Un ejemplo completo de eso, lo que guarda el puntero original, dentro de la instancia de la clase matriz para mantener una referencia en todo el tiempo que la envoltura propia matriz sería:

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

Aviso pesar de que la clase Array esto genera es ilimitada , exactamente como struct foo* en C. Esto significa que no puede iterar sobre él en Python: el tamaño es desconocido.Si el tamaño es realmente fijo, o si tiene alguna forma de conocer el tamaño de alguna manera, puede envolverlo de una forma mucho más agradable (a mi modo de ver) escribiendo un mapa de tipos que devuelva una lista PyList. Es un poco más difícil de escribir, pero hace que la interfaz parezca mejor en el lado de Python.

+0

¡Muchas gracias! Esto es exactamente lo que estaba buscando y funcionó perfectamente. Sé el tamaño de la matriz de un cálculo que no incluí para simplificar el ejemplo. Miré la documentación sobre carrays.i antes, pero me confundí con esto: "Al usar esta macro, el tipo se restringe a un nombre de tipo simple como int, float o Foo". Seguí intentando usar el tipo 'struct foo *' y, cuando no funcionó, asumí que solo funcionaría para los tipos c básicos. – user1604630