urschrei urschrei - 2 months ago 9x
Python Question

How do I cast a 2D list to a void pointer and back

I'm trying to write two Cython functions to wrap external functions. The functions are the inverse of one another; one accepts a string, and returns a struct with two fields: a void pointer to a 2D array (the second dimension is always two elements:

[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], … ]
), and the array's length. The other accepts the same struct and returns a string. So far, I've got the following. It compiles, but the cast to and from the nested list is definitely incorrect.


cdef extern from "header.h":
struct _FFIArray:
void* data
size_t len

cdef _FFIArray decode_polyline_ffi(char* polyline, int precision);
cdef char* encode_coordinates_ffi(_FFIArray, int precision);
cdef void drop_float_array(_FFIArray coords);
cdef void drop_cstring(char* polyline)


import numpy as np
from pypolyline_p cimport (

def encode_coordinates(coords, int precision):
""" coords looks like [[1.0, 2.0], [3.0, 4.0], …] """
cdef double[::1] ncoords = np.array(coords, dtype=np.float64)
cdef _FFIArray coords_ffi
# Wrong
coords_ffi.data = <void*>&ncoords[0]
# Wrong
coords_ffi.len = ncoords.shape[0]
cdef char* result = encode_coordinates_ffi(coords_ffi, precision)
cdef bytes polyline = result
return polyline

def decode_polyline(bytes polyline, int precision):
cdef char* to_send = polyline
cdef _FFIArray result = decode_polyline_ffi(to_send, precision)
# Wrong
cdef double* incoming_ptr = <double*>(result.data)
# Wrong
cdef double[::1] view = <double[:result.len:1]>incoming_ptr
coords = np.copy(view)
return coords


I think the issue is that you're trying to use 2D arrays and 1D memoryviews

In the encoding function

    # the coords are a 2D, C contiguous array
    cdef double[:,::1] ncoords = np.array(coords, dtype=np.float64)
    # ...
    coords_ffi.data = <void*>&ncoords[0,0] # take the 0,0 element
    # the rest stays the same

In the decoding function

   # specify it as a 2D, len by 2, C contiguous array
   cdef double[::1] view = <double[:result.len,:2:1]>incoming_ptr
   # the rest stays the same

(It's possible that your FFI functions expect Fortran contiguous arrays. In which case the ::1 goes on the first dimension of the memoryview, and you also change incoming_ptr)