Использование Py_buffer и PyMemoryView_FromBuffer с различными размерами элементов
Этот вопрос связан с предыдущим вопросом, который я задавал. А именно этот, если кому-то интересно. По сути, я хочу открыть массив C для Python, используя Py_buffer
завернутый в memoryview
-объект. Я получил его на работу с помощью PyBuffer_FillInfo
(работа = я могу манипулировать данными в Python и записать их в стандартный вывод на C), но если я попытаюсь развернуть свой собственный буфер, я получу ошибку по умолчанию после возврата из функции C.
Мне нужно создать свой собственный буфер, потому что PyBuffer_FillInfo предполагает, что формат имеет тип char, что делает поле 1 размером элемента. Мне нужно иметь возможность предоставлять элементы размером 1, 2, 4 и 8.
Некоторый код, это рабочий пример:
Py_buffer *buf = (Py_buffer *) malloc(sizeof(*buf));
int r = PyBuffer_FillInfo(buf, NULL, malloc(sizeof(char) * 4), 4, 0, PyBUF_CONTIG);
PyObject *mv = PyMemoryView_FromBuffer(buf);
//Pack the memoryview object into an argument list and call the Python function
for (blah)
printf("%c\n", *buf->buf++); //this prints the values i set in the Python function
Глядя на реализацию PyBuffer_FillInfo
, что действительно просто, я свернул свою собственную функцию, чтобы иметь возможность предоставлять пользовательские размеры элементов:
//buffer creation function
Py_buffer *getReadWriteBuffer(int nitems, int itemsize, char *fmt) {
Py_buffer *buf = (Py_buffer *) malloc(sizeof(*buf));
buf->obj = NULL
buf->buf = malloc(nitems * itemsize);
buf->len = nitems * itemsize;
buf->readonly = 0;
buf->itemsize = itemsize;
buf->format = fmt;
buf->ndim = 1;
buf->shape = NULL;
buf->strides = NULL;
buf->suboffsets = NULL;
buf->internal = NULL;
return buf;
}
Как я использую это:
Py_buffer *buf = getReadWriteBuffer(32, 2, "h");
PyObject *mv = PyMemoryView_FromBuffer(buf);
// pack the memoryview into an argument list and call the Python function as before
for (blah)
printf("%d\n", *buf->buf); //this prints all zeroes even though i modify the array in Python
return 0;
//the segfault happens somewhere after here
Результатом использования моего собственного объекта буфера является segfault после возврата из функции C. Я действительно не понимаю, почему это вообще происходит. Любая помощь будет наиболее ценной.
РЕДАКТИРОВАТЬ Согласно этому вопросу, который я не смог найти ранее, itemsize > 1 может даже вообще не поддерживаться. Что делает этот вопрос еще более интересным. Может быть, я мог бы использовать PyBuffer_FillInfo
с достаточно большим блоком памяти, чтобы вместить то, что я хочу (32 C плавает, например). В этом случае вопрос больше о том, как назначить Python-плавающие memoryview
объект в функции Python. Вопросы вопросы.
1 ответ
Таким образом, из-за отсутствия ответов я решил использовать другой подход, нежели тот, который изначально предполагал. Оставьте это здесь на тот случай, если кто-то еще столкнется с тем же препятствием.
По сути, вместо создания буфера (или bytearray, эквивалент.) В C и передачи его в Python для изменения пользователем расширения. Я просто немного переработал код, чтобы пользователь возвращал байтовый массив (или любой тип, который поддерживает интерфейс буфера) из функции обратного вызова Python. Таким образом, мне даже не нужно беспокоиться о размере элементов, поскольку в моем случае все, что делает код C с возвращенным объектом, - это извлечь его буфер и скопировать его в другой буфер с помощью простого memcpy
,
Код:
PYGILSTATE_ACQUIRE; //a macro i made
PyObject *result = PyEval_CallObject(python_callback, NULL);
if (!PyObject_CheckBuffer(result))
; //raise exception
Py_buffer *view = (Py_buffer *) malloc(sizeof(*view));
int error = PyObject_GetBuffer(result, view, PyBUF_SIMPLE);
if (error)
; //raise exception
memcpy(my_other_buffer, view->buf, view->len);
PyBuffer_Release(view);
Py_DECREF(result);
PYGILSTATE_RELEASE; //another macro
Я надеюсь, что это помогает кому-то.