取消引用void指向numpy数组或列表的指针

时间:2016-07-18 15:33:05

标签: python numpy ctypes

我通过ctypes收到了来自外部函数的void指针,其中包含一组c_double数组: [[12.0, 13.0], [14.0, 15.0], …]

我通过restype参数访问它:

from ctypes import Structure, POINTER, c_void_p, c_size_t, c_double, c_uint32, c_char_p, cast, cdll


class _CoordResult(Structure):
    """ Container for returned FFI coordinate data """
    _fields_ = [("coords", _FFIArray)]

class _FFIArray(Structure):
    """
    Convert sequence of float lists to a C-compatible void array
    example: [[1.0, 2.0], [3.0, 4.0]]

    """
    _fields_ = [("data", c_void_p),
                ("len", c_size_t)]

def _void_array_to_nested_list(res, _func, _args):
    """ Dereference the FFI result to a list of coordinates """
    shape = (res.coords.len, 2)
    array_size = np.prod(shape)
    mem_size = 8 * array_size
    array_str = string_at(res.coords.data, mem_size)
    array = [list(pair) for pair in ((POINTER(c_double * 2).from_buffer_copy(res.coords)[:res.coords.len]))]
    drop_array(res.coords)
    return array

decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _CoordResult
decode_polyline.errcheck = _void_array_to_nested_list

然而,这让我回到了无意义的值,因为_void_array_to_nested_list中的指针取消引用是错误的。

解决方案不必使用NumPy,但这似乎是最好的方法。

2 个答案:

答案 0 :(得分:2)

我现在无法对此进行测试,但我会尝试这样做:

import numpy as np

result = ...
shape = (10, 2)
array_size = np.prod(shape)
mem_size = 8 * array_size
array_str = ctypes.string_at(result, mem_size)
array = np.frombuffer(array_str, float, array_size).reshape(shape)

array将是只读的,如果您需要可写数组,请将其复制。

答案 1 :(得分:1)

以下是一种使用ctypes.castnumpy.ctypeslib.as_array的解决方案,而不是ctypes.string_at,以防它是否会制作内存区域的额外副本。

class _FFIArray(Structure):
    _fields_ = [("data", c_void_p), ("len", c_size_t)]

class Coordinate(Structure):
    _fields_ = [("latitude", c_double), ("longitude", c_double)]

class Coordinates(Structure):
    _fields_ = [("data", POINTER(Coordinate)), ("len", c_size_t)]

decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _FFIArray

# assuming that the second argument is the length of polyline, 
# although it should not be needed for `\0` terminated string
res = decode_polyline(polyline, len(polyline))

nres = Coordinates(cast(res.data, POINTER(Coordinate)), res.len)
for i in range(nres.len):
    print(nres.data[i].latitude, nres.data[i].longitude)

# if just a numpy (np) array is needed
xs = np.ctypeslib.as_array((c_double * res.len * 2).from_address(res.data))
# "New view of array with the same data."
xs = xs.view(dtype=[('a', np.float64), ('b', np.float64)], type=np.ndarray)
xs.shape = res.len