Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

It seems that there is currently a pull request in Cython's repo to wrap c++ std::array but until then, I could use some help. I am currently wrapping the std::array like so:

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

This works, but I have to loop over a cython memory view say, double arr[:], and copy the values one by one. Is there an easier way to do this? Essentially I'd like to do the following:

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

Is this completely unreasonable? As a result, working with std::array is extremely tedious because I have a to have a for-loop to copy values from cython to c++ and from c++ to cython. Furthermore, since cython doesn't give us the ability to have non-type template parameters, I have to define a wrapper for every variation of std::array in my code. Any suggestions on how to efficiently work with std::array would be greatly appreciated.

edit:

I'm now able to go from a memory view to an array2 type using the following:

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

But it seems that no matter what I do, I cannot get cython to convert an array2 type to a memoryview:

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])

Please help me figure out how to cast a C++ pointer to a memory view. Every combination I have tried to date has resulted in some kind of casting error.

EDIT: I found that I am to perform the following syntax with no error using a newer version of Cython (I was using Cythong 0.22) and upgraded to 0.23.5.

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

However, if I attempt to return mview from the function I am using it in, I get garbage memory. Returning the memoryview to the pointer of my array loses scope and therefore destructs my array automatically. As soon as I figure out how to properly return my array, I'll attempt to update the official answer.

like image 890
user985030 Avatar asked Apr 01 '16 12:04

user985030


1 Answers

After much fiddling, I found the answer to my question.

Definition of array and class that uses array:

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 implementation

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()
like image 140
user985030 Avatar answered Oct 30 '22 14:10

user985030