I would like to wrap a C/C++ function with the following signature using Cython.
// my_tool.h
float func(const float** points, int n, int m) {
float result = 0.;
// ...
for (int i=0; i<n; ++i) {
for (int j=0; j<m; ++j) {
// ... points[i][j]
}
}
return result
}
The wrapper will receive a 2D-numpy array. The array doesn't need to be contiguous (for example, compute
can also take array slices: arr[:, 2:-2]
).
# my_tool.pyx
import numpy as np
cimport cython
cdef extern from "my_tool.h":
int func(const float** points, int n_points)
def compute(float[:,:] points):
# Assure float-ndarray.
points = np.asarray(points, dtype=float)
# Create a memoryview.
cdef float[:,:] points_view = points
# >>> The following line will lead to a syntax error:
# >>> "Expected an identifier or literal"
cdef float*[:] points_ptr = [&points_view[i] for i in points.shape[0]]
ret = func(&points_ptr[0], points.shape[0])
return ret
Question: How to pass a memoryview of a 2D-array to func
such that its signature (C-style list of pointers) is matched?
# This is how I want to use my wrapped tool in python.
import mytool
import numpy as np
points = np.random.rand(10,2)
ret = mytool.compute(points)
Update/Solution: The question was answered by this post. My solution looked similar to this:
from cpython.mem cimport PyMem_Malloc, PyMem_Free
def compute(float[:,:] points):
# Assure float-ndarray and create a typed memoryview.
if False:
points = np.asarray(points, dtype=float)
cdef float[:,:] points_view = points
else:
# If a contiguous array is advantageous.
points = np.ascontiguousarray(points, dtype=float)
cdef float[:,::1] points_view = points
# Create a helper container with pointers of each point.
cdef size_t n_bytes = points.shape[0] * sizeof(float*)
cdef float_type** point_ptrs = <float_type **>PyMem_Malloc(n_bytes)
if not point_ptrs:
raise MemoryError
try:
for i in range(points.shape[0]):
point_ptrs[i] = &points[i, 0]
# Call the C function that expects a float_type**
ret = func(point_ptrs, points.shape[0])
finally:
PyMem_Free(point_ptrs)