2013-06-08 18 views
5

Mam plik nagłówka jak:Expose wektor jako memoryview użyciem SWIG

#include <vector> 

inline std::vector<uint8_t>& vec() { 
    static std::vector<uint8_t> v { 'a', 'b', 'c', 'd' }; 
    return v; 
} 

inline const std::vector<uint8_t>& cvec() { 
    return vec(); 
} 

mogę wrap it in SWIG using std_vector.i and pyabc.i ale to jest zupełnie nieefektywne (tam skok pomiędzy C++ i Pythonie dla każdego dostępu) i biorąc pod uwagę, że te są dosłownie garstką bajtów, które powinienem był w stanie opakować za pomocą Python's memoryview interface.

Jak mogę odsłonić mój std::vector<uint8_t> jako Python memoryview?

Odpowiedz

7

Pokazywanie go jako memoryview wymaga najpierw utworzenia Py_buffer. W Pythonie 3.3+ dostępna jest wygodna funkcja pomocnicza, PyMemoryView_FromMemory, która wykonuje dla nas wiele pracy. We wcześniejszych wersjach choć musimy wziąć kilka dodatkowych kroków, więc nasz podstawowy z typemap wygląda następująco:

%typemap(out) std::vector<uint8_t>&, const std::vector<uint8_t>& { 
    Py_buffer *buf=(Py_buffer*)malloc(sizeof *buf); 
    const bool ro = info<$1_type>::is_readonly(); 
    if (PyBuffer_FillInfo(buf, NULL, &((*$1)[0]), (*$1).size(), ro, PyBUF_ND)) { 
    // error, handle 
    } 
    $result = PyMemoryView_FromBuffer(buf); 
} 

Tutaj mamy w zasadzie przydzielania trochę pamięci dla Py_buffer. To po prostu zawiera szczegóły wewnętrznego bufora dla Pythona. Przydzielona pamięć będzie własnością obiektu memoryview po utworzeniu. Niestety, ponieważ zostanie wydany pod numerem free(), musimy go przydzielić za pomocą malloc(), mimo że jest to kod C++.

Poza Py_buffer i opcjonalnym Py_ObjectPyBuffer_FillInfo zajmuje void* (sam bufor), wielkość bufora, wartość logiczną wskazującą, czy jest zapisywalny i flagi. W tym przypadku nasza flaga wskazuje po prostu, że udostępniliśmy pamięć w stylu C dla bufora.

Do decydowania, czy jest to plik readonly, czy nie użyliśmy zmiennej SWIG wbudowanej $n_type zmiennej i pomocnika (który mógłby być typem C++ 11, gdybyśmy chcieli).

Aby zakończyć nasz interfejs haust musimy zapewnić, że pomocnika i dołączyć plik nagłówka, więc cała sprawa staje się:

%module test 

%{ 
#include "test.hh" 

namespace { 
    template <typename T> 
    struct info { 
    static bool is_readonly() { 
     return false; 
    } 
    }; 

    template <typename T> 
    struct info<const T&> { 
    static bool is_readonly() { 
     return true; 
    } 
    }; 
} 
%} 

%typemap(out) std::vector<uint8_t>&, const std::vector<uint8_t>& { 
    Py_buffer *buf=(Py_buffer*)malloc(sizeof *buf); 
    const bool ro = info<$1_type>::is_readonly(); 
    if (PyBuffer_FillInfo(buf, NULL, &((*$1)[0]), (*$1).size(), ro, PyBUF_ND)) { 
    // error, handle 
    } 
    $result = PyMemoryView_FromBuffer(buf); 
} 

%include "test.hh" 

Możemy następnie przetestować go z:

import test 

print test.vec() 
print len(test.vec()) 
print test.vec()[0] 
print test.vec().readonly 
test.vec()[0]='z' 
print test.vec()[0] 

print "This should fail:" 
test.cvec()[0] = 0 

Który pracował zgodnie z oczekiwaniami, przetestowano przy użyciu Pythona 2.7.

W porównaniu do samego owijania go za pomocą std_vector.i to podejście ma pewne wady. Największą z nich jest to, że nie możemy zmienić rozmiaru wektora lub przekonwertować go z powrotem na wektor później w sposób trywialny. Moglibyśmy obejść to, przynajmniej częściowo, tworząc proxy SWIG dla wektora jak normalnie i używając drugiego parametru z PyBuffer_FillInfo do przechowywania go wewnętrznie. (Byłoby to również potrzebne, gdybyśmy musieli na przykład zarządzać własnością wektora).

Powiązane problemy