由于Flexo对SWIG and C++ memory leak with vector of pointers的回复,我已经绑定了Python std::vector<CameraT>
。但我需要访问像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
语句放在%inline 块之内和之外并且失败了。循环遍历向量元素也会产生内存泄漏警告。不知道还能做什么。
代码位于Github。
答案 0 :(得分:1)
这里有几个问题。首先,要修复%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标题中都找不到任何合适的类型图。 (它确实存在于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 ++数据而不是原始C ++数据。如果你想解决这个问题,那么out typemap必须返回一个代理,其实现为__getitem__
和__setitem__
,而不仅仅是一个元组。根据您希望如何使用代码,可以实现速度/复杂性的折衷。我有an example of something similar to this,您需要调整out typemap以创建wrapped_array
并调整wrapped_array以保存指向真实数组的指针(以及对Python对象的引用,因此它不会被释放“订单错误。”