It will not work out-of-the-box with numpy-arrays. You will have to make the memory management yourself, for example:
%%cython
from libc.stdlib cimport free
def doit():
cdef int *ptr;
add_array(&ptr, 5)
print(ptr[4])
free(ptr) #memory management
The difference to your attempt: &arr_memview[0]
is pointer to an integer array, but what you need for your function is a pointer to a pointer to an integer array - that is what &ptr
is.
The problem with your function is, that it has too many responsibilities:
- it allocates the memory
- it initializes the memory
It would be easier, if add_array
would only be doing the second part, i.e.
void add_array(int *io_array, int n) {
int i;
for(i = 0; i < n; i++) {
io_array[i] = i;
}
}
And thus any memory could be initialized (also memory which was not allocated with malloc
).
However, it is possible to create a numpy-array using the returned pointer ptr
, it is just less straight forward:
cimport numpy as np
import numpy as np
np.import_array() # needed to initialize numpy-data structures
cdef extern from "numpy/arrayobject.h":
void PyArray_ENABLEFLAGS(np.ndarray arr, int flags) #not include in the Cython default include
def doit():
cdef int *ptr;
add_array(&ptr, 5)
# create numpy-array from data:
cdef np.npy_intp dim = 5
cdef np.ndarray[np.int32_t, ndim=1] arr = np.PyArray_SimpleNewFromData(1, &dim, np.NPY_INT32, ptr)
# transfer ownership of the data to the numpy array:
PyArray_ENABLEFLAGS(arr, np.NPY_OWNDATA)
return arr
The following is worth mentioning:
np.import_array()
is needed to be able to use all of the numpy's functionality. Here is an example of what can happen, if np.import_array()
isn't called.
- After
PyArray_SimpleNewFromData
, the data itself isn't owned by the resulting numpy array, thus we need to enable the OWNDATA
-flag, otherwise there will be a memory leak.
- It is not obvious, that the resulting numpy-array can be responsible for freeing the data. For example instead of using malloc/free it could be using Python's memory allocator.
I would like to elaborate about point 3. above. Numpy uses a special function to allocate/deallocate memory for data - it is PyDataMem_FREE
and uses system's free
for it. So in your case (using system's malloc/free in add_array
) everything is Ok. (PyDataMem_FREE
should not be confused with PyArray_free
, as I did in an earlier version of the answer. PyArray_free
is responsible for freeing other elements (array itself, and dimension/strides data, not data-memory) of the numpy-array, see here and is different depending on Python version).
A more flexible/safe approach is to use PyArray_SetBaseObject
as shown in this SO-post.