在不带内存的Python中公开STL结构

2024-09-28 23:19:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个std::vector<CameraT>,由于Flexo对SWIG and C++ memory leak with vector of pointers的响应,我已经将其绑定到Python。但我需要访问像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;

接口文件大致如下所示:

^{pr2}$

以及集成测试:

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。在


Tags: ofnotypefloatstructprintvectormemory
1条回答
网友
1楼 · 发布于 2024-09-28 23:19:19

这里有几个问题。首先,要修复有关内存泄漏的警告,您需要%include "datainterface.h",然后对CameraT_模板使用另一条%template指令。(typedef不会改变这是必需的这一事实)。在

因此:

%include "datainterface.h"
%template(CameraT) CameraT_<float>;

警告消失,我们可以访问该类型的成员。(在您提到的示例的第一行中没有警告,这是Python引用计数系统的一个怪癖)。在

不过,还不止这些,我们现在得到一个关于t不可订阅的错误。我们可以通过调用swig和-debug tmsearch作为一个额外的参数来深入了解这一点,它显示了正在选择的类型映射。最重要的是,我们看到的是:

^{pr2}$

稍微令人惊讶的是,我在SWIG头中都找不到合适的类型映射。(它当然存在于Java中,但在arrays\u Java.i中)。有几种方法可以修复它:

  1. 使用carrays。我让用户做一些工作。在
  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元组,其中有一个{{4CD>}的副本,所以这里的第二个下标操作符是指这个,而不是原来的C++数据。{{6}如果你想用一个cdtuple}来修复一个cdtuple}的实现。有一个速度/复杂性的权衡取决于你期望如何使用代码。我有an example of something similar to this,您需要调整out typemap来创建一个wrapped_array,并调整wrapped_数组来保存指向实际数组的指针(以及对Python对象的引用,这样它就不会以错误的顺序释放)。在

相关问题 更多 >