在Cython中封装std::数组,并将其暴露给内存视图

Wrapping std::array in Cython and Exposing it to memory views

本文关键字:暴露 视图 内存 Cython 封装 std 数组      更新时间:2023-10-16

目前Cython的repo中似乎有一个pull请求来包装c++std::array,但在此之前,我可能需要一些帮助。我目前正在这样包装std::array

cdef extern from "<array>" namespace "std" nogil:
    cdef cppclass array2 "std::array<double, 2>":
        array2() except+
        double& operator[](size_t)

这是可行的,但我必须在cython内存视图上循环,比如double arr[:],然后逐个复制值。有更简单的方法吗?本质上,我想做以下事情:

cdef double arr[2]
arr[0] = 1.0
arr[1] = 2.0
cdef array2 array2_arr = arr
#and the reverse
cdef array2 reverse
reverse[0] = 1.0
reverse[1] = 2.0 
cdef double reverse_arr[2] = reverse

这完全不合理吗?因此,使用std::array是非常乏味的,因为我需要一个for循环来将值从cython复制到c++,以及从c++复制到cython。此外,由于cython不允许我们使用非类型模板参数,因此我必须为代码中std::array的每个变体定义一个包装器。任何关于如何有效地与std::array合作的建议都将不胜感激。

编辑:

我现在可以使用以下命令从内存视图转到array2类型:

def __cinit__(self, double[:] mem):
  cdef array2 *arr = <array2 *>(&mem[0])

但似乎无论我做什么,我都无法让cython将array2类型转换为内存视图:

cdef array2 arr = self.thisptr.getArray()
# error: '__pyx_t_1' declared as a pointer to a reference of type 'double &'
cdef double[::1] mview = <double[:2]>(&arr[0]) 
#OR
# Stop must be provided to indicate shape
cdef double[::1] mview = <double[::2]>(&arr[0])

请帮我弄清楚如何将C++指针投射到内存视图。迄今为止,我尝试过的每一种组合都会导致某种选角错误。

编辑:我发现我将使用Cython的新版本(我使用的是Cythong 0.22)并升级到0.23.5来执行以下语法,没有任何错误。

cdef double[::1] mview = <double[:4]>(&arr[0])

然而,如果我试图从我正在使用的函数返回mview,我会得到垃圾内存。将memoryview返回到数组的指针会失去作用域,因此会自动销毁数组。一旦我弄清楚如何正确返回数组,我就会尝试更新官方答案。

经过一番努力,我找到了问题的答案。

数组和使用数组的类的定义:

cdef extern from "<array>" namespace "std" nogil:
  cdef cppclass array4 "std::array<int, 4>":
    array4() except+
    int& operator[](size_t)
cdef extern from "Rectangle.h" namespace "shapes":
  cdef cppclass ArrayFun:
    ArrayFun(array4&)
    array4 getArray()

Python实现

cdef class PyArrayFun:
    cdef ArrayFun *thisptr      # hold a C++ instance which we're wrapping
    def __cinit__(self, int[:] mem):
      #
      # Conversion from memoryview to std::array<int,4>
      #
      cdef array4 *arr = <array4 *>(&mem[0])
      self.thisptr = new ArrayFun(arr[0])
    def getArray(self):
      cdef array4 arr = self.thisptr.getArray()
      #
      # Conversion from std::array<int, 4> to memoryview
      #
      cdef int[::1] mview = <int[:4]>(&arr[0])
      cdef int[::1] new_view = mview.copy()
      for i in range(0,4):
        print ("arr is ", arr[i])
        print("new_view is ", new_view[i])
      # A COPY MUST be returned because arr goes out of scope and is
      # default destructed when this function exist. Therefore we have to 
      # copy again. This kinda of sucks because we have to copy the 
      # internal array out from C++, and then we have to copy the array
      # out from Python, therefore 2 copies for one array. 
      return mview.copy()