使用SWIG将矢量公开为存储器视图

时间:2013-06-08 10:41:30

标签: c++ python swig

我有一个头文件,如:

#include <vector>

inline std::vector<uint8_t>& vec() {
  static std::vector<uint8_t> v { 'a', 'b', 'c', 'd' };
  return v;
}

inline const std::vector<uint8_t>& cvec() {
  return vec();
}

我可以wrap it in SWIG using std_vector.i and pyabc.i但效率很低(每次访问都会在C ++和Python代码之间跳转)并且假设这些字面上只是一堆字节,我应该能够用{{{ 3}}

如何将我的std::vector<uint8_t>公开为Python memoryview

1 个答案:

答案 0 :(得分:9)

将其公开为memoryview需要先创建Py_buffer。在Python 3.3+中有一个方便的辅助函数PyMemoryView_FromMemory,它为我们做了很多工作。在早期版本中,虽然我们需要采取一些额外的步骤,因此我们的基本输出类型图如下所示:

%typemap(out) std::vector<uint8_t>&, const std::vector<uint8_t>& {
  Py_buffer *buf=(Py_buffer*)malloc(sizeof *buf);
  const bool ro = info<$1_type>::is_readonly();
  if (PyBuffer_FillInfo(buf, NULL,  &((*$1)[0]), (*$1).size(), ro, PyBUF_ND)) {
    // error, handle
  }
  $result = PyMemoryView_FromBuffer(buf);
}

这里我们基本上为Py_buffer分配了一些内存。这只包含Python内部缓冲区的详细信息。我们分配的内存将在memoryview对象创建后归其所有。不幸的是,因为它将通过调用free()发布,我们需要使用malloc()进行分配,即使它是C ++代码。

除了Py_buffer和一个可选的Py_Object PyBuffer_FillInfo需要一个void*(缓冲区本身),缓冲区的大小,一个表示它是否可写的布尔值和一个旗。在这种情况下,我们的标志只是表明我们为缓冲区提供了C风格的连续内存。

为了决定它是否只读,我们使用了SWIG内置的$n_type变量和一个帮助器(如果需要,可以是C ++ 11类型的特性)。

要完成我们的SWIG接口,我们需要提供该帮助程序并包含头文件,因此整个过程变为:

%module test

%{
#include "test.hh" 

namespace {
  template <typename T>
  struct info {
    static bool is_readonly() {
      return false;
    }
  };

  template <typename T>
  struct info<const T&> {
    static bool is_readonly() {
      return true;
    }
  };
}
%}

%typemap(out) std::vector<uint8_t>&, const std::vector<uint8_t>& {
  Py_buffer *buf=(Py_buffer*)malloc(sizeof *buf);
  const bool ro = info<$1_type>::is_readonly();
  if (PyBuffer_FillInfo(buf, NULL,  &((*$1)[0]), (*$1).size(), ro, PyBUF_ND)) {
    // error, handle
  }
  $result = PyMemoryView_FromBuffer(buf);
}

%include "test.hh"

我们可以用以下方法测试它:

import test

print test.vec()
print len(test.vec())
print test.vec()[0]
print test.vec().readonly
test.vec()[0]='z'
print test.vec()[0]

print "This should fail:"
test.cvec()[0] = 0

按预期工作,使用Python 2.7进行测试。

与仅使用std_vector.i包装它相比,这种方法确实有一些缺点。最大的问题是我们无法调整矢量大小,或者稍后将其转换回矢量。我们可以解决这个问题,至少部分是通过为法向量创建SWIG代理并使用PyBuffer_FillInfo的第二个参数在内部存储它。 (例如,如果我们必须管理向量的所有权,那么也需要这样做。)