2

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)
normanius
  • 8,629
  • 7
  • 53
  • 83
  • 1
    [This question](https://stackoverflow.com/questions/62084515/return-a-2d-cython-pointer-to-python-array) might also be worth looking at although it's transferring data in the opposite direction – DavidW Oct 13 '20 at 07:53

0 Answers0