2012-08-17 16 views
7

Ho cercato per alcuni giorni di cercare di capire come trasformare un array di strutture in una lista Python. Ho una funzione che restituisce un puntatore all'inizio della matrice.Array di strutture Python SWIG

struct foo { 
    int member; 
}; 

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

Dopo il richiamo della funzione da Python ricevo una singola struttura ma cercando di accedere agli altri elementi della matrice causa un errore:

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

Ho provato con %array_class ma senza alcun risultato. Ho anche provato che definisce la funzione come restituire un array in file di interfaccia SWIG:

extern struct foo [ANY] bar(); 

documentazione Lo SWIG è abbastanza approfondita, ma io non riesco a capire questo.

+1

Hai guardato qui? - http://stackoverflow.com/questions/8114030/swig-python-array-inside-structure – dpandiar

+0

@dpandiar - questo è un caso piuttosto diverso perché la dimensione è fissa e nota e gli array sono membri e non restituiscono valori da una funzione – Flexo

risposta

8

L'idea che hai provato con [ANY] non funzionerà per diversi motivi. Principalmente sebbene ANY possa essere usato in typemaps per permettere alla stessa typemap di funzionare con array di dimensioni fisse variabili, che non è quello che hai lì.

Anche la sintassi per C non è sufficiente. Non si può scrivere:

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

Oppure:

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

In serie C. Il più vicino si può ottenere è:

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

Ma questo non è davvero più facile per avvolgere.

Tuttavia, la soluzione più semplice può essere fatto funzionare utilizzando %array_class, ad esempio:

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

Questo mi permette di fare:

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

Possiamo andare un passo migliore, anche se (forse) da iniettando il codice per nascondere automaticamente il puntatore al tipo di matrice, aggiungendo quanto segue prima che venga visualizzato bar() da SWIG:

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

Così ora è possibile utilizzarlo come:

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 

È necessario essere attenti a titolarità della memoria. In questi esempi finora la memoria è trapelata. Puoi usare %newobject per dire a SWIG che la memoria è di proprietà di Python, ma poi verrà rilasciata troppo presto (non appena il valore di ritorno originale non sarà più referenziato) quindi dovrai organizzarti per mantenere il valore originale intorno più a lungo. Un esempio completo di questo, che salva il puntatore originale all'interno l'istanza della classe array di mantenere un riferimento intorno finchè l'involucro matrice stessa sarebbe:

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

Avviso però che la classe array questo genera è illimitato , esattamente come un struct foo* in C. Ciò significa che non è possibile eseguire iterazioni su di esso in Python - la dimensione è sconosciuta.Se la dimensione è veramente corretta, o se hai un modo per conoscere la dimensione, in qualche modo puoi avvolgerla in un modo molto più bello (a mio avviso) scrivendo una typemap che restituisce una PyList. È un po 'più di lavoro da scrivere, ma rende l'interfaccia più bella sul lato Python.

+0

Grazie mille! Questo è esattamente quello che stavo cercando e ha funzionato perfettamente. Conosco la dimensione della matrice da un calcolo che non ho incluso per semplificare l'esempio. Ho esaminato la documentazione sui carriys.i prima ma ero confuso da questo: "Quando si utilizza questa macro, il tipo è limitato a un nome di tipo semplice come int, float o Foo." Ho continuato a provare a usare il tipo 'struct foo *' e quando non funzionava supponevo che funzionasse solo con tipi di base c. – user1604630

Problemi correlati