通过cython从python访问C ++向量/数组

问题描述

我正在通过cython包装一个c ++程序,并希望在Python中提供该程序的矢量。

我已经为向量编写了一个getter方法,除了返回数据的副本之外,它的工作原理还不错:

# in cython
from libcpp.vector cimport vector
cimport numpy as np

cdef np.ndarray[double] vector_to_double_arr(vector[double] vec):
    cdef double[::1] arr = <double [:vec.size()]>vec.data()
    return np.asarray(arr)

def get_variable()
    return vector_to_double_arr(my_c_vector) # my_c_vector is a global variable defined elsewhere

也就是说,在python中,对返回数组的赋值无效,因为该值仅分配给副本。

# in python

get_variable()[0] = 4 # has no effect

过去,我通过先创建numpy数组并在内部数据上使用c ++进行操作来解决此问题。但是,现在我必须处理一个预先存在的向量。

我已经考虑过直接返回memoryview

cdef np.ndarray[double] vector_to_double_arr(vector[double] vec):
    cdef double[::1] arr = <double [:vec.size()]>vec.data()
    return arr 

但是,我还没有弄清楚如何使memoryview在Python中显示为数组。我需要一些包装此memoryview的对象,并可以适当的方式对其进行写入。我当时在考虑创建一个numpy数组并将其分配给np.ndarray.data属性,但是写入该数组是一种不好的做法,而且行不通。

如何从Python写入c ++向量?作为扩展:如果我不处理向量而是双精度数组,该怎么办?

修改

我现在已经通过复制代码实现了the solution suggested by @ead

from libc.stdlib cimport free
from cpython.object cimport PyObject
from cpython.ref cimport Py_INCREF
np.import_array()


cdef class MemoryNanny:
    cdef void* ptr # set to NULL by "constructor"
    def __dealloc__(self):
        print("freeing ptr=",<unsigned long long>(self.ptr)) #just for debugging
        free(self.ptr)
        
    @staticmethod
    cdef create(void* ptr):
        cdef MemoryNanny result = MemoryNanny()
        result.ptr = ptr
        print("nanny for ptr=",<unsigned long long>(result.ptr)) #just for debugging
        return result
    
cdef extern from "numpy/arrayobject.h":
    # a little bit awkward: the reference to obj will be stolen
    # using PyObject*  to signal that Cython cannot handle it automatically
    int PyArray_SetBaSEObject(np.ndarray arr,PyObject *obj) except -1 # -1 means there was an error
          
cdef array_from_ptr(void * ptr,np.npy_intp N,int np_type):
    cdef np.ndarray arr = np.PyArray_SimpleNewFromData(1,&N,np_type,ptr)
    nanny = MemoryNanny.create(ptr)
    Py_INCREF(nanny) # a reference will get stolen,so prepare nanny
    PyArray_SetBaSEObject(arr,<PyObject*>nanny) 
    return arr

然后按如下方式访问指针:

cdef MyClass()
    ...
    @property
    def myArray(self):
        return array_from_ptr(myArrayPointer,myArrayPointerLength,np.NPY_FLOAT64)

从Python访问该属性效果很好,但是,一旦我更改了数组中的任何值(同样起作用),程序就会暂停。

m = MyClass()
print(m.myArray) # prints what it is supposed to print
m.myArray[0] = 1 # works
print(m.myArray) # prints what is expected,but terminates the program after freeing the freeing the pointer.
...

当我删除上面代码块中的最后一个打印语句时,一切仍然正常。为什么?我很困惑。我该怎么做才能解决问题?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)