Ho un piccolo progetto che funziona perfettamente con SWIG. In particolare, alcune delle mie funzioni restituiscono std::vector
s, che vengono convertite in tuple in Python. Ora faccio un sacco di numeri, quindi ho appena convertito SWIG in array numpy dopo che sono stati restituiti dal codice C++. Per fare questo, uso qualcosa come il seguente in SWIG.C'è un modo per usare pythonappend con la nuova funzione integrata di SWIG?
%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %}
(In realtà, ci sono diverse funzioni denominate dati, alcuni dei quali ritornano galleggianti, che è il motivo per cui verifico che val
è in realtà una tupla). Questo funziona meravigliosamente.
Ma, mi piacerebbe anche utilizzare il flag -builtin
che è ora disponibile. Le chiamate a queste funzioni dati sono rare e per lo più interattive, quindi la loro lentezza non è un problema, ma ci sono altri cicli lenti che accelerano significativamente con l'opzione integrata.
Il problema è che quando utilizzo questo flag, la funzione pythonappend viene silenziosamente ignorata. Ora, Data restituisce di nuovo una tupla. C'è un modo in cui potrei ancora restituire gli array numpy? Ho provato a usare i typemaps, ma è diventato un gran casino.
Edit:
Borealid ha risposto alla domanda molto bene. Solo per completezza, includo un paio di typemaps correlati ma sottilmente diversi di cui ho bisogno perché ritorno con riferimento const e utilizzo i vettori di vettori (non iniziare!). Sono abbastanza diversi da non desiderare che qualcun altro inciampi cercando di capire le piccole differenze.
%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);
}
Edit 2:
Anche se non è proprio quello che stavo cercando, problemi simili possono essere risolti usando @ approccio del monaco (explained here).
Non penso che si possa fare questo senza scrivere una typemap e farlo sul lato C, proprio perché -builtin rimuove il codice dove normalmente viene piazzato pythonappend. Sei sicuro che -builtin sia molto più veloce (cioè il profiling ti ha portato ad usarlo?) Sarei tentato di usare due moduli, uno con e uno senza -builtin. – Flexo
Sono sorpreso che non ci sia alcun avviso che '-builtin' ignora pythonappend. Non sono all'altezza della sfida del typemapping di 'std :: vector's in array numpy. Ho fatto il profilo e questo ha velocizzato in modo significativo il ciclo più fastidioso dell'interfaccia (non abbastanza lungo per fare una pausa, troppo a lungo per aspettare di frequente). Ma ho anche capito che posso spostare questo ciclo nel mio codice C++, anche se un po 'goffamente. Ecco come andrò. Tuttavia, il tuo suggerimento sui "due moduli" è interessante e potrebbe essere utile in altri casi. – Mike
Hai chiamato SWIG con -Wall? Presumo che avrebbe avvertito in quel caso. – Flexo