在 Python w/o 内存泄漏中公开 STL 结构
Exposing STL structs in Python w/o memory leak
我有一个 std::vector<CameraT>
,由于 Flexo 对 SWIG and C++ memory leak with vector of pointers 的回复,我已经将其绑定到 Python。但是我需要访问 CameraT.t 之类的字段,但找不到正确的方法。
它在第 3 方头文件 (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.
我试图在 %inline 块内外放置 struct CameraT {}
和 #include
语句,但失败了。循环遍历向量元素也会产生内存泄漏警告。不知道还能做什么。
代码在Github。
这里有几个问题。首先,要修复关于内存泄漏的警告,您需要 %include "datainterface.h"
然后使用另一个 %template
指令,用于 CameraT_
模板。 (typedef 不会改变这是必需的事实)。
因此:
%include "datainterface.h"
%template(CameraT) CameraT_<float>;
警告消失,我们可以访问该类型的成员。 (你提到的第一行没有警告是我认为 Python 的引用计数系统的一个怪癖)。
但这还不是全部,我们现在收到关于 t
不可订阅的错误。我们可以通过使用 -debug-tmsearch 作为额外参数调用 swig 来深入了解这一点,它显示了正在选择的类型映射。至关重要的是,我们看到类似的东西:
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 headers 中都找不到合适的类型映射。 (尽管在 arrays_java.i 中,Java 确实存在)。有几种修复方法:
- 使用 carrays.i 并让用户做一些工作。
- 切换到具有现有包装器的容器。 (虽然不是第 3 方代码的选项)
- 自己写一些类型映射。
因为 1 和 2 是微不足道的而且不是很好 Python 我将跳过那些并为我们写一两个类型映射,我的最终 nvm.i 文件最终看起来像:
%module nvm
%{
#include "datainterface.h"
%}
%include "std_vector.i"
%typemap(out) float[ANY] %{
$result = PyTuple_New(_dim0);
for (unsigned i = 0; i < _dim0; ++i)
PyTuple_SetItem($result,i,PyFloat_FromDouble([i]));
%}
%typemap(in) float[ANY] (unsigned i=0, float tmp[_dim0]) %{
= tmp;
PyObject *item, *iterator;
iterator = PyObject_GetIter($input); // spliting this lets a macro work
while (!PyErr_Occurred() && iterator &&
i < _dim0 && (item = PyIter_Next(iterator))) {
[i++] = PyFloat_AsDouble(item);
Py_DECREF(item);
}
Py_DECREF(iterator);
if (i != _dim0) {
PyErr_SetString(PyExc_AttributeError, "Failed to get _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>;
这让我 运行 你的测试文件和我的 addtions 没问题:
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)
此解决方案有一个警告,它可以解决(通过 return 使用代理 object)但不会自动破坏交易:
cams[0].t[0] = 1
print(cams[0].t[0]) # Won't actually have changed
这是因为我们 return 编辑了一个新的 Python 元组,其中包含 t
的副本,所以这里的第二个下标运算符指的是那个而不是原始的 C++数据。如果你也想解决这个问题,输出类型映射必须 return 一个实现 __getitem__
和 __setitem__
的代理,而不仅仅是一个元组。根据您希望如何使用代码,需要进行 speed/complexity 权衡。我有 an example of something similar to this,您需要调整输出类型映射以创建 wrapped_array
并调整 wrapped_array 以保存指向实际数组的指针(以及对 Python 的引用object 所以它不会以错误的顺序被释放。
我有一个 std::vector<CameraT>
,由于 Flexo 对 SWIG and C++ memory leak with vector of pointers 的回复,我已经将其绑定到 Python。但是我需要访问 CameraT.t 之类的字段,但找不到正确的方法。
它在第 3 方头文件 (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.
我试图在 %inline 块内外放置 struct CameraT {}
和 #include
语句,但失败了。循环遍历向量元素也会产生内存泄漏警告。不知道还能做什么。
代码在Github。
这里有几个问题。首先,要修复关于内存泄漏的警告,您需要 %include "datainterface.h"
然后使用另一个 %template
指令,用于 CameraT_
模板。 (typedef 不会改变这是必需的事实)。
因此:
%include "datainterface.h"
%template(CameraT) CameraT_<float>;
警告消失,我们可以访问该类型的成员。 (你提到的第一行没有警告是我认为 Python 的引用计数系统的一个怪癖)。
但这还不是全部,我们现在收到关于 t
不可订阅的错误。我们可以通过使用 -debug-tmsearch 作为额外参数调用 swig 来深入了解这一点,它显示了正在选择的类型映射。至关重要的是,我们看到类似的东西:
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 headers 中都找不到合适的类型映射。 (尽管在 arrays_java.i 中,Java 确实存在)。有几种修复方法:
- 使用 carrays.i 并让用户做一些工作。
- 切换到具有现有包装器的容器。 (虽然不是第 3 方代码的选项)
- 自己写一些类型映射。
因为 1 和 2 是微不足道的而且不是很好 Python 我将跳过那些并为我们写一两个类型映射,我的最终 nvm.i 文件最终看起来像:
%module nvm
%{
#include "datainterface.h"
%}
%include "std_vector.i"
%typemap(out) float[ANY] %{
$result = PyTuple_New(_dim0);
for (unsigned i = 0; i < _dim0; ++i)
PyTuple_SetItem($result,i,PyFloat_FromDouble([i]));
%}
%typemap(in) float[ANY] (unsigned i=0, float tmp[_dim0]) %{
= tmp;
PyObject *item, *iterator;
iterator = PyObject_GetIter($input); // spliting this lets a macro work
while (!PyErr_Occurred() && iterator &&
i < _dim0 && (item = PyIter_Next(iterator))) {
[i++] = PyFloat_AsDouble(item);
Py_DECREF(item);
}
Py_DECREF(iterator);
if (i != _dim0) {
PyErr_SetString(PyExc_AttributeError, "Failed to get _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>;
这让我 运行 你的测试文件和我的 addtions 没问题:
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)
此解决方案有一个警告,它可以解决(通过 return 使用代理 object)但不会自动破坏交易:
cams[0].t[0] = 1
print(cams[0].t[0]) # Won't actually have changed
这是因为我们 return 编辑了一个新的 Python 元组,其中包含 t
的副本,所以这里的第二个下标运算符指的是那个而不是原始的 C++数据。如果你也想解决这个问题,输出类型映射必须 return 一个实现 __getitem__
和 __setitem__
的代理,而不仅仅是一个元组。根据您希望如何使用代码,需要进行 speed/complexity 权衡。我有 an example of something similar to this,您需要调整输出类型映射以创建 wrapped_array
并调整 wrapped_array 以保存指向实际数组的指针(以及对 Python 的引用object 所以它不会以错误的顺序被释放。