Ich habe ein kleines Projekt, das wunderbar mit SWIG zusammenarbeitet. Insbesondere geben einige meiner Funktionen std::vector
s zurück, die in Python in Tupel übersetzt werden. Jetzt mache ich viele Zahlen, also muss SWIG diese nur in Numpy-Arrays konvertieren, nachdem sie vom C ++ - Code zurückgegeben wurden. Dazu verwende ich in SWIG etwa Folgendes.
%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %}
(Tatsächlich gibt es mehrere Funktionen mit dem Namen "Daten", von denen einige " val
float" zurückgeben. Deshalb überprüfe ich, ob es sich tatsächlich um ein Tupel handelt.) Dies funktioniert einfach wunderbar.
Aber ich würde auch gerne das benutzen -builtin
Flagge verwenden, die jetzt verfügbar ist. Aufrufe dieser Datenfunktionen sind selten und meist interaktiv, sodass ihre Langsamkeit kein Problem darstellt. Es gibt jedoch auch andere langsame Schleifen, die mit der integrierten Option erheblich beschleunigt werden.
Das Problem ist, dass wenn ich dieses Flag verwende, die Pythonappend-Funktion stillschweigend ignoriert wird. Jetzt gibt Data nur noch einmal ein Tupel zurück. Gibt es eine Möglichkeit, wie ich noch numpy Arrays zurückgeben kann? Ich habe versucht, Typemaps zu verwenden, aber es wurde zu einem riesigen Durcheinander.
Bearbeiten:
Borealid hat die Frage sehr gut beantwortet. Der Vollständigkeit halber füge ich ein paar verwandte, aber subtil unterschiedliche Typkarten hinzu, die ich benötige, da ich als konstante Referenz zurückkehre und Vektoren von Vektoren verwende (nicht starten!). Diese sind so unterschiedlich, dass ich nicht möchte, dass jemand anders herumstolpert, um die kleinen Unterschiede herauszufinden.
%typemap(out) std::vector<int>& {
npy_intp result_size = $1->size();
npy_intp dims[1] = { result_size };
PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
int* dat = (int*) PyArray_DATA(npy_arr);
for (size_t i = 0; i < result_size; ++i) { dat[i] = (*$1)[i]; }
$result = PyArray_Return(npy_arr);
}
%typemap(out) std::vector<std::vector<int> >& {
npy_intp result_size = $1->size();
npy_intp result_size2 = (result_size>0 ? (*$1)[0].size() : 0);
npy_intp dims[2] = { result_size, result_size2 };
PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_INT);
int* dat = (int*) PyArray_DATA(npy_arr);
for (size_t i = 0; i < result_size; ++i) { for (size_t j = 0; j < result_size2; ++j) { dat[i*result_size2+j] = (*$1)[i][j]; } }
$result = PyArray_Return(npy_arr);
}
Bearbeiten 2:
Obwohl nicht ganz das, wonach ich gesucht habe, können ähnliche Probleme auch mit dem Ansatz von @ MONK ( hier erklärt ) gelöst werden .
-builtin
die Pythonappend ignoriert. Ich bin der Herausforderung nicht gewachsen,std::vector
s in numpy Arrays zu typen. Ich habe ein Profil erstellt und es hat die nervigste Schleife in meiner Benutzeroberfläche erheblich beschleunigt (nicht lang genug, um eine Pause einzulegen; zu lange, um häufig zu warten). Aber mir wurde auch klar, dass ich diese Schleife in meinen C ++ - Code verschieben kann - wenn auch etwas umständlich. So werde ich also gehen. Dennoch ist Ihr Vorschlag für zwei Module interessant und kann in anderen Fällen hilfreich sein.-Wall
(obwohl dies groß genug ist, um zu ignorieren, dass ich nicht denke, dass es das überhaupt erfordern sollte).Data
Methoden mit Cython zu verpacken ?Antworten:
Ich stimme Ihnen zu, dass die Verwendung
typemap
etwas chaotisch wird, aber es ist der richtige Weg, um diese Aufgabe zu erfüllen. Sie haben auch Recht, dass die SWIG-Dokumentation nicht direkt sagt, dass dies nicht%pythonappend
kompatibel ist-builtin
, aber es ist stark impliziert:%pythonappend
fügt der Python-Proxy-Klasse hinzu , und die Python-Proxy-Klasse existiert überhaupt nicht in Verbindung mit der-builtin
Flag überhaupt nicht.Zuvor haben Sie SWIG die C ++ -
std::vector
Objekte in Python-Tupel konvertieren lassen und diese Tupel dann wieder an übergebennumpy
- übergeben, wo sie erneut konvertiert wurden.Was Sie wirklich tun möchten, ist, sie einmal auf der C-Ebene zu konvertieren.
Hier ist ein Code, der alle
std::vector<int>
Objekte in NumPy-Integer-Arrays umwandelt :%{ #include "numpy/arrayobject.h" %} %init %{ import_array(); %} %typemap(out) std::vector<int> { npy_intp result_size = $1.size(); npy_intp dims[1] = { result_size }; PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT); int* dat = (int*) PyArray_DATA(npy_arr); for (size_t i = 0; i < result_size; ++i) { dat[i] = $1[i]; } $result = PyArray_Return(npy_arr); }
Dies verwendet die Numpy-Funktionen der C-Ebene, um ein Array zu erstellen und zurückzugeben. In der Reihenfolge, es:
arrayobject.h
Datei von in der C ++ - Ausgabedatei enthalten istimport_array
genannt zu werden , wenn das Python - Modul geladen wird (andernfalls werden alle NumPy Methoden segfault)std::vector<int>
NumPy-Arrays mit a zutypemap
Dieser Code sollte gesetzt werden , bevor Sie auf
%import
die Überschriften , die die Funktionen der Rückkehr enthaltenstd::vector<int>
. Abgesehen von dieser Einschränkung ist es vollständig in sich geschlossen, sodass es Ihrer Codebasis nicht zu viel subjektives "Durcheinander" hinzufügen sollte.Wenn Sie andere Vektortypen benötigen, können Sie einfach die
NPY_INT
und alleint*
undint
Bits ändern , andernfalls duplizieren Sie die obige Funktion.quelle