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_Object
PyBuffer_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).