使用 SWIG 将向量公开为内存视图

Posted

技术标签:

【中文标题】使用 SWIG 将向量公开为内存视图【英文标题】:Expose a vector as a memoryview using SWIG 【发布时间】:2013-06-08 10:41:30 【问题描述】:

我有一个像这样的头文件:

#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 代码之间跳转),并且考虑到这些实际上只是一堆字节,我应该能够用 Python's memoryview interface 包装它们。

如何将我的 std::vector&lt;uint8_t&gt; 公开为 Python memoryview

【问题讨论】:

【参考方案1】:

将其公开为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 的第二个参数在内部存储它。 (例如,如果我们必须管理向量的所有权,这也是需要的)。

【讨论】:

以上是关于使用 SWIG 将向量公开为内存视图的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SWIG 中将向量的锯齿状 C++ 向量转换(类型映射)为 Python

使用 SWIG 为 Python 包装 C++。 “向量”未声明

Node.js-视图引擎-Swig集成express的安装与配置

为 Drupal 视图中的公开项目提供预定义的选项列表?

将 Drupal 视图过滤器公开为开/关复选框以启用/禁用过滤器

将 UIWebView 设为公开