Использование 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

Я надеюсь, что это помогает кому-то.

Другие вопросы по тегам