Cython - преобразование указателей на массивы в объекты Python

Хорошо, я так близок к тому, чтобы закончить это, я могу попробовать его. За последние несколько недель я пытался создать расширение Python для взаимодействия с библиотекой, написанной на С++ через Cython. Немного помогая ребятам здесь и пару друзей, мне удалось получить то, что похоже на 98% пути. Остается только одно: я не могу для жизни понять, как превратить указатель на массив беззнаковых шорт в объект python (предпочтительно список).

Немного фона, я пытаюсь взаимодействовать с частью библиотеки, которая устанавливает функцию обратного вызова, которую я успешно сделал с этим:

global callbackfunc

ctypedef unsigned short const_ushort "const uint16_t"

ctypedef void (*Function1)(const_ushort *data, unsigned width, unsigned height)

cdef extern from "lib.hpp":
    void SetCallback(Function1)

cdef void cSetCallback(Function1 function):
    SetCallback(function)

cdef void callcallback(const_ushort *data, unsigned width, unsigned height):
    global callbackfunc
    callbackfunc(data,width,height)

cSetCallback(callcallback)

def PySetCallback(callbackFunc):
    global callbackfunc
    callbackfunc = callbackFunc

Проблема возникает в функции "callcallback", где я получаю ошибку: "Невозможно преобразовать const_ushort * в объект Python". Моя первая попытка - создать новый список python и пропустить цикл, чтобы каждый элемент массива попал в список python, например:

datalist = []
for i in range(width*height):
    datalist += data[i]

Что, к сожалению, связывает меня с скомпилированным кодом на языке cython, который пытается определить тип как "const const unsigned short", что, очевидно, является проблемой.

Затем я попробовал это:

datalist = []
for i in data:
    datalist += i

Что дает мне "Итерация массива C требует известного конечного индекса". Обратите внимание, что я знаю очень мало C/С++, поэтому большинство из этого не имеет для меня никакого смысла.

Итак, так или иначе, есть ли какой-либо эффективный способ перевода такого указателя в объект python (желательно быстрее, чем цикл через массив, так как обычно это около 57344 элемента, и это довольно чувствительно для времени)

Edit: Еще немного разъяснений, как я уже упоминал, я работаю с обратными вызовами, а функция С++ в библиотеке, которая вызывает это, отправляет указатель на массив "const uint_16", поэтому я определил const_ushort таким образом, потому что в противном случае типы не объединяются. Я не могу каким-либо образом изменить библиотеку.

Edit2: Похоже, я понял. То, что мне в итоге пришлось сделать, было явно отброшено массивом как массив беззнаковых шорт, а не массив const unsigned shorts, чтобы я мог индексировать их с константой. Для этого я создал еще одну С++-функцию, подобную этой (кто-то другой написал ее для меня, я почти не знаю С++):

unsigned short *convert_short(const unsigned short *test){ return const_cast<unsigned short *>(test); }

и это позволило мне создать функцию " getindex" в моем классе и вернуть правильные значения на основе функции. Так что да, Python, кажется, правильно читает массивы и еще много чего, поэтому этот случай кажется закрытым. Большое спасибо.

Ответ 1

ctypedef unsigned short const_ushort "const uint16_t"

отображает const в коде C, поэтому вы получаете typedef unsigned short const uint16_t, в котором const не имеет смысла. (Он также переопределяет стандартный тип uint16_t, который он не должен.)

У вас может быть больше успеха с

ctypedef unsigned short *ushort_p "ushort_p"

По какой-то причине Cython, похоже, не распознает ключевое слово C const. Возможно, вам придется использовать обертку вокруг ваших обратных вызовов для решения проблемы const.

Если вы хотите, чтобы указатель переводился в объект Python, оберните его в cdef class. Убедитесь, что класс записывает высоту и ширину, поскольку они не записываются с массивом C.

Ответ 2

Это не совсем то, о чем вы просите, но если вы хотите сделать, это работать с массивом неподписанных шорт в Cython, а затем сделать ваш ответ доступным на стороне Python, вы можете создать Numpy ndarray на Python и передать его в Cython, где он примерно такой же, как многомерный массив. Таким образом, вы получаете управление памятью Python, некоторый удобный синтаксис индексирования и скорость работы с реальным массивом в Cython.

Здесь пример из моих решений Project Euler.