Обтекание функции C в Cython и NumPy

Я бы хотел вызвать функцию C из Python, чтобы манипулировать некоторыми массивами NumPy. Функция такова:

void c_func(int *in_array, int n, int *out_array);

где результаты представлены в out_array, размер которых я знаю заранее (а не моя функция, фактически). Я пытаюсь сделать в соответствующем .pyx файле следующее, чтобы передать входной сигнал функции из массива NumPy и сохранить результат в массиве NumPy:

def pyfunc(np.ndarray[np.int32_t, ndim=1] in_array):    
    n = len(in_array)
    out_array = np.zeros((512,), dtype = np.int32)
    mymodule.c_func(<int *> in_array.data, n, <int *> out_array.data)
    return out_array

Но я получаю "Python objects cannot be cast to pointers of primitive types" ошибка для назначения вывода. Как это сделать?

(Если мне требуется, чтобы вызывающий абонент Python выделял правильный выходной массив, я могу сделать

def pyfunc(np.ndarray[np.int32_t, ndim=1] in_array, np.ndarray[np.int32_t, ndim=1] out_array):  
    n = len(in_array)
    mymodule.cfunc(<int *> in_array.data, n, <int*> out_array.data)

Но могу ли я сделать это так, чтобы вызывающему абоненту не приходилось предварительно выделять выходной массив соответствующего размера?

Ответ 1

Вы должны добавить cdef np.ndarray перед назначением out_array:

def pyfunc(np.ndarray[np.int32_t, ndim=1] in_array):    
    cdef np.ndarray out_array = np.zeros((512,), dtype = np.int32)
    n = len(in_array)
    mymodule.c_func(<int *> in_array.data, n, <int *> out_array.data)
    return out_array

Ответ 2

Вот пример, как манипулировать массивами NumPy, используя код, написанный на C/С++ через ctypes. Я написал небольшую функцию в C, взяв квадрат чисел из первого массива и записывая результат во второй массив. Число элементов задается третьим параметром. Этот код скомпилирован как общий объект.

squares.c скомпилирован в squares.so:

void square(double* pin, double* pout, int n) {
    for (int i=0; i<n; ++i) {
        pout[i] = pin[i] * pin[i];
    }
}

В python вы просто загружаете библиотеку с помощью ctypes и вызываете функцию. Указатели массива получают из интерфейса ctypes NumPy.

import numpy as np
import ctypes

n = 5
a = np.arange(n, dtype=np.double)
b = np.zeros(n, dtype=np.double)

square = ctypes.cdll.LoadLibrary("./square.so")

aptr = a.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
bptr = b.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
square.square(aptr, bptr, n)

print b

Это будет работать для любой c-библиотеки, вам просто нужно знать, какие типы аргументов пройти, возможно, перестроить c-structs в python с помощью ctypes.