2012-08-17 6 views
7

Szukałem od kilku dni, próbując dowiedzieć się, jak przekształcić tablicę struktur w listę Pythona. Mam funkcję, która zwraca wskaźnik do początku tablicy.Python Structure SWIG

struct foo { 
    int member; 
}; 

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

Po wywołaniu funkcji z Pythona mogę otrzymać jednolitą strukturę, ale próbuje uzyskać dostęp do innych elementów tablicy powoduje błąd:

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

Próbowałem została, używając %array_class ale bezskutecznie. Próbowałem zostały również zdefiniowanie funkcji jako powrót tablicę w pliku interfejsów SWIG:

extern struct foo [ANY] bar(); 

dokumentacja SWIG jest dość dokładny, ale nie wydają się rysunek to.

+1

Czy spojrzałeś tutaj? - http://stackoverflow.com/questions/8114030/swig-python-array-inside-structure – dpandiar

+0

@dpandiar - to całkiem inny przypadek, ponieważ rozmiar jest ustalony i znany, a tablice są członkami i nie zwracają wartości z funkcji – Flexo

Odpowiedz

8

Pomysł, który wypróbowałeś z [ANY], nie zadziała z kilku powodów. Przede wszystkim jednak można używać ANY w mapach typów, aby ta sama mapa działała z różnymi tablicami o stałym rozmiarze, co nie jest tym, co tam masz.

Składnia C nie jest tam również odpowiednia. Nie możesz napisać:

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

czyli

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

W standardowym C najbliżej można dostać się:

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

Ale to naprawdę nie jest łatwiejsze do zawijania.

jednak proste rozwiązanie może być wykonane do pracy z użyciem %array_class, na przykład:

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

pozwala to mi zrobić:

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

Możemy pójść o krok lepsza (ewentualnie) przez wstrzykiwanie kodu w celu automatycznego ukrywania wskaźnika na typie macierzy, dodając następujące wartości przed wyświetleniem bar() przez SWIG:

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

Więc można teraz używać jak:

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 

Trzeba być ostrożnym o własności pamięci. W tych przykładach do tej pory pamięć wycieka. Możesz użyć %newobject, aby poinformować SWIG, że pamięć jest własnością strony Python, ale potem zostanie ona wydana zbyt wcześnie (tak szybko, jak pierwotna wartość zwracana nie będzie już przywoływana), więc musisz zachować oryginalną wartość wokół dłużej. Kompletny przykładem tego, co oszczędza pierwotnego wskaźnika wewnątrz przykład klasy tablicy zachować odniesienie tak długo, jak owijki tablicy sam będzie:

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

Wskazówki jednak, że klasa Tablica ta generuje jest nieograniczona , dokładnie tak, jak w przypadku C struct foo*. Oznacza to, że nie można go iterować w języku Python - rozmiar jest nieznany.Jeśli rozmiar jest rzeczywiście ustalony lub masz sposób poznania rozmiaru, możesz jakoś zawinąć to w znacznie ładniejszy (moim zdaniem) sposób, pisząc mapę typów, która zwraca PyList. Pisanie jest nieco bardziej pracochłonne, ale sprawia, że ​​interfejs wygląda ładniej po stronie Pythona.

+0

Dziękuję bardzo! Właśnie tego szukałem i działałem idealnie. Znam rozmiar tablicy z obliczeń, których nie uwzględniłem, aby uprościć przykład. Spojrzałem na dokumentację na carrays.i wcześniej, ale byłem zdezorientowany przez to: "Podczas korzystania z tego makra, typ jest ograniczony do prostej nazwy typu, takich jak int, float lub Foo." Próbowałem używać typu 'struct foo *', a gdy nie działało, zakładałem, że działa ono tylko dla podstawowych typów c. – user1604630