Предоставление структур STL в Python без утечки памяти

у меня есть 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.

2

Решение

Здесь есть пара проблем. Прежде всего, чтобы исправить предупреждение об утечке памяти, вам нужно %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. Используйте carrays.i и заставьте пользователя сделать некоторую работу.
  2. Переключитесь на контейнер с существующими обертками. (Не вариант для стороннего кода, хотя)
  3. Напишите несколько типографских карт сами.

Так как 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 в неправильном порядке).

1

Другие решения


По вопросам рекламы [email protected]