Как зарегистрировать деструктор для массива с выделенным Си? - PullRequest
5 голосов
/ 25 июля 2011

Я хочу выделить числа для массива numpy в C / C ++ и передать их в python в виде массива numpy.Что я могу сделать с PyArray_SimpleNewFromData .

Проблема в том, что я также хочу зарегистрировать функцию, которая должна вызываться из Python, когда счетчик ссылок на массивный массив достигает нуля, и это вызвало бы некоторую семантику деструктора на стороне C ... Вот псевдоПример того, что мне нужно:

 float* arr; PyObject* np_arr; void (*destructor)(float* arr);
 // ... C-allocate array on arr, ...
 // ...
 // ... initialize destructor with some suitable value, and then:
 np_arr = /* ... create the array to wrap arr, 
             and to use destructor on some meaningful way ... */

Есть ли простой способ сделать это?

1 Ответ

4 голосов
/ 10 октября 2018

Идея состоит в том, чтобы создать объект Python, который знает, как освободить вашу память при уничтожении, и сделать ее основой возвращаемого массива с выделенным Си. Это звучит сложно, но это может быть легко достигнуто с помощью чего-то, известного как капсул в Python. Позвольте мне привести пример,

Предположим, у вас есть следующий код,

PyObject *arr;
int nd = 2;
npy_intp dims[] = {5, 10};
double *data = some_function_that_returns_a_double_star(x, y, z);

arr = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, (void *)data);
return arr;

Здесь есть очевидная утечка памяти, так как вы не можете освободить данные, пока arr не будет удален, как указано здесь в красном окне предупреждения. Исправить это, с другой стороны, легко. Определите функцию, которая в основном является функцией-деструктором, которая знает, как выполнять сборку мусора.

void capsule_cleanup(PyObject *capsule) {
    void *memory = PyCapsule_GetPointer(capsule, NULL);
    // Use your specific gc implementation in place of free if you have to
    free(memory);
}

Теперь увеличьте ваш код как,

PyObject *arr;
int nd = 2;
npy_intp dims[] = {5, 10};
double *data = some_function_that_returns_a_double_star(x, y, z);

arr = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, (void *)data);
PyObject *capsule = PyCapsule_New(data, NULL, capsule_cleanup);
// NULL can be a string but use the same string while calling PyCapsule_GetPointer inside capsule_cleanup
PyArray_SetBaseObject((PyArrayObject *) arr, capsule);
return arr;

Нет необходимости в Py_DECREF капсуле. Функция PyArray_SetBaseObject ворует справку.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...