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

Хорошо, я так близок к завершению, что уже чувствую вкус. За последние несколько недель или около того я пытался создать расширение Python для взаимодействия с библиотекой, написанной на C++, через 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/C++, поэтому большая часть этого не имеет для меня особого смысла.

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

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

Edit2: похоже, я понял. В итоге мне пришлось явно указать массив как массив беззнаковых шорт, а не массив константных шорт без знака, чтобы я мог индексировать их с помощью неконстанты. Чтобы достичь этого, я создал еще одну функцию C++, подобную этой (кто-то написал ее для меня, я едва знаю C++):

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

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


person Josiah    schedule 11.03.2011    source источник


Ответы (2)


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.

person Fred Foo    schedule 11.03.2011
comment
Спасибо, обертывание его в класс cdef, похоже, позволяет его скомпилировать, но теперь я не могу понять, как получить к нему доступ в моем тестовом сценарии Python. - person Josiah; 11.03.2011
comment
@Josiah: мне непонятно, как работает индексация в вашей структуре; кажется, вы используете его как матрицу в коде C и как массив в коде Python. Вы можете попытаться определить метод __getindex__ в своем cdef class и сделать оттуда одномерный индекс в массив C. - person Fred Foo; 12.03.2011

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

Вот пример из моих решений Project Euler.

person Wang    schedule 11.03.2011