у меня есть std::vector<CameraT>
который я уже связал с Python благодаря ответу Flexo для Утечка памяти в SWIG и C ++ с вектором указателей. Но мне нужно получить доступ к таким полям, как CameraT.t, и я не могу найти правильный способ сделать это.
Он определяется в стороннем заголовочном файле (DataInterface.h) как:
template<class FT>
struct CameraT_ {
typedef FT float_t;
float_t t[3];
/* more elaborate c++ stuff */
};
typedef CameraT_<float> CameraT;
Файл интерфейса выглядит примерно так:
%module nvm
%{
#define SWIG_FILE_WITH_INIT
#include "util.h"%}
%include "std_vector.i"
%inline %{
bool CameraDataFromNVM(const char* name, std::vector<CameraT>& camera_data){
/* wrapper code */
}
%}
%template(CamVector) std::vector<CameraT>;
И интеграционный тест:
import nvm
if __name__ == "__main__":
datafile = '../../example-data/vidstills.nvm'
cams = nvm.CamVector()
assert nvm.CameraDataFromNVM(datafile, cams)
print(cams[0]) # this yields no memory warnings
print(cams[0].t[0]) # 'SwigPyObject' object has no attribute 't'
# Yields: swig/python detected a memory leak of type 'CameraT *', no destructor found.
c = list([i for i in cams])
печатает это:
$ python3 test_nvm.py |& head
<Swig Object of type 'CameraT *' at 0x7f638228ee40>
Loading cameras/points: ../../example-data/vidstills.nvm
329 cameras; 8714 3D points; 74316 projections
Traceback (most recent call last):
File "test_nvm.py", line 20, in <module>
print(cams[0].t[0])
AttributeError: 'SwigPyObject' object has no attribute 't'
swig/python detected a memory leak of type 'CameraT *', no destructor found.
Я пытался разместить struct CameraT {}
а также #include
заявления в и за пределами %в соответствии заблокировать и не удалось. Также зацикливание на векторных элементах выдает предупреждение об утечке памяти. Понятия не имею, что еще делать.
Код включен Github.
Здесь есть пара проблем. Прежде всего, чтобы исправить предупреждение об утечке памяти, вам нужно %include "datainterface.h"
а затем использовать другой %template
директива, для CameraT_
шаблон. (Typedef не меняет того факта, что это требуется).
Так что с:
%include "datainterface.h"%template(CameraT) CameraT_<float>;
Предупреждение исчезает, и мы можем получить доступ к членам типа. (Отсутствие предупреждения в первой строке, которую вы упомянули в качестве примера, является странностью системы подсчета ссылок Python, я думаю).
Это еще не все, теперь мы получаем ошибку о t
не быть подписанным. Мы можем получить некоторое представление об этом, вызвав swig с -debug-tmsearch в качестве дополнительного аргумента, который показывает выбранные карты типов. Важно, что мы видим что-то вроде:
datainterface.h:4: Searching for a suitable 'ret' typemap for: CameraT_< float >::float_t CameraT_< float >::t[3]
[snip lots of tries...]
Looking for: SWIGTYPE
None found
Немного удивительно, что я не могу найти подходящих типографских карт для этого ни в одном из заголовков SWIG. (Это, конечно, существует для Java, хотя в arrays_java.i). Есть несколько способов это исправить:
Так как 1 и 2 — тривиальный и не очень хороший Python, я пропущу их и напишу для нас одну или две карты типов, мой окончательный файл nvm.i в итоге выглядел так:
%module nvm
%{
#include "datainterface.h"%}
%include "std_vector.i"
%typemap(out) float[ANY] %{
$result = PyTuple_New($1_dim0);
for (unsigned i = 0; i < $1_dim0; ++i)
PyTuple_SetItem($result,i,PyFloat_FromDouble($1[i]));
%}
%typemap(in) float[ANY] (unsigned i=0, float tmp[$1_dim0]) %{
$1 = tmp;
PyObject *item, *iterator;
iterator = PyObject_GetIter($input); // spliting this lets a macro work
while (!PyErr_Occurred() && iterator &&
i < $1_dim0 && (item = PyIter_Next(iterator))) {
$1[i++] = PyFloat_AsDouble(item);
Py_DECREF(item);
}
Py_DECREF(iterator);
if (i != $1_dim0) {
PyErr_SetString(PyExc_AttributeError, "Failed to get $1_dim0 floats" );
SWIG_fail;
}
%}
%include "datainterface.h"
%inline %{
bool CameraDataFromNVM(const char* name, std::vector<CameraT>& camera_data){
return true;
}
%}
%template(CameraT) CameraT_<float>;
%template(CamVector) std::vector<CameraT>;
Что позволило мне запустить ваш тестовый файл с моими дополнениями:
import nvm
if __name__ == "__main__":
datafile = '../../example-data/vidstills.nvm'
cams = nvm.CamVector(1)
assert nvm.CameraDataFromNVM(datafile, cams)
print(cams[0]) # this yields no memory warnings
cams[0].t = (1,2,3)
print(cams[0].t[0])
#print(cams[0].bar)
# Yields: swig/python detected a memory leak of type 'CameraT *', no destructor found.
c = list([i for i in cams])
print(c)
В этом решении есть предостережение, которое можно решить (путем возврата прокси-объекта), но не автоматически нарушает условия сделки:
cams[0].t[0] = 1
print(cams[0].t[0]) # Won't actually have changed
Это происходит потому, что мы вернули новый кортеж Python с копией t
в этом, так что второй оператор индекса здесь относится к этому, а не к исходным данным C ++. Если вы тоже хотите это исправить, то out typemap должен будет вернуть прокси с реализацией __getitem__
а также __setitem__
вместо просто кортежа. В зависимости от того, как вы ожидаете использовать код, нужно учитывать компромисс между скоростью и сложностью. я имею пример чего-то похожего на это, вам нужно будет отрегулировать Out Typemap для создания wrapped_array
и отрегулируйте wrapped_array, чтобы он содержал указатель на ваш реальный массив (и ссылку на объект Python, чтобы он не получал free’d в неправильном порядке).