我通过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,但这似乎是最好的方法。
答案 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.cast
或numpy.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