问题是,我有一个C ++向量,需要将其转换为numpy数组。载体来自外部库。我可以做的就是在源中添加一个额外的函数,然后再将它重新编译为原始的DLL。我的想法是,我用ctypes导入函数,然后用void指针指针调用它,让C ++函数插入向量指针。 这里开始了第一个问题。我只能通过显式分配内存将指针发送回Python。我不仅需要使用
myVec = new vector<T>
即使是我需要使用
的int指针myIntP = new int
简单地做
x = 10
int* myIntP = &x
*pythonIntPP = myIntP
不起作用。回到Python,我仍然会有原始指针。
**pythonIntPP = x
不过,会奏效。
为什么我必须为指针分配内存?
无论如何,让我们找到我发现的解决方案: 首先,你将在一个接受void **的DLL中拥有一个C ++函数。 在Python中做
from ctypes import *
lib = cdll.LoadLibrary(r"C:\path\to.dll")
gvp = lib.GetVectorPointer
gvp.restype = c_int
gvp.argtypes = [POINTER(c_void_p), POINTER(c_int)] # we need the vector pointer and size
voidPP = pointer(c_void_p())
sizeP = pointer(c_int())
gvp(voidPP, sizeP)
现在我们在Python对象中有向量指针和大小,但我们无法在Cython中解包指针。因此,我们需要创建一个cdef函数,将其导出到DLL(pyd文件)中,然后再在Python中导入它。
在Cython中我们做
import numpy as np
cimport numpy as np
from libcpp.vector cimport vector
ctypedef vector[T] Vec
ctypedef myVec* VecP
ctypedef VecP* VecPP
arr = np.empty(1, dtype=np.uint8)
def allocate_arr(size):
arr.resize(size, refcheck=False)
cdef public int vec_to_numpy(void** voidPP):
cdef VecP vecp= <VecP>ppVoid[0]
cdef int size = vecp[0].size()
cdef np.ndarray[T, ndim=1] data = arr # arr needs to be allocated
cdef int i
for i in range(size):
data[i] = vecp[0][i]
return 0
在Python中,我们现在可以调用此函数并获取数组
lib = windll.LoadLibrary(r"C:\path\to\module.pyd")
v2n = lib.vec_to_numpy
v2n.restype = c_int
v2n.argtypes = [POINTER(c_void_p)]
import module
module.allocate_arr(sizeP) # allocate first
v2n(voidPP) # send the vector pointer to the Cython function
module.arr # this is the numpy array with the vector data
提示: 您可以告诉链接器使用两个hack之一导出您的Cython函数。 在代码的开头为每个要导出的函数写下这一行(相应地更改名称)。
cdef void emit_pragma 'foo ();\n#pragma comment(linker, "/export:vec_to_numpy=vec_to_numpy")\nstatic void unused' ():pass
或在函数定义中使用此hack(使用__stdcall以避免名称损坏)。
cdef int vec_to_numpy 'unused;\nextern "C" __declspec(dllexport) public int __stdcall vec_to_numpy' (void** voidPP):
您是否了解在这三种语言之间获得充分灵活性的更好方法?
是否有更简单的方法来导出符号,以便可以将它们导入到不涉及黑客攻击的Python中?能够直接在Python中使用cdef函数对我来说非常方便。