Итак, я хочу вызвать функцию обратного вызова Python из C.
В какой-то момент функция отправляется на C и упаковывается в кортеж, подобный этому
PyObject *userData = Py_BuildValue("Oi",py_callback,some_number);
Где-то в этой области я тоже Py_INCREF(py_callback)
.
В какой-то более поздний момент в программе я хочу вызвать эту функцию
PyObject *py_callback;
int some_number;
PyArg_ParseTuple((PyObject*)userData,"Oi",&py_callback,&some_number); // returns true
PyObject *py_result = PyObject_CallFunctionObjArgs(py_callback,
/* ... */
NULL);
и последний вызов вызывает ошибку сегментации. Есть ли у вас какие-либо идеи, почему бы это сделать?
Когда вы получаете странное поведение из API Python C, всегда стоит дважды проверить, что вы правильно управляете состоянием Global Interpreter Lock (aka "GIL" ): http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock
PyObject_CallFunctionArgs
? &some_number
не будет работать правильно (это неправильный тип), и вы не инициализируете py_some_number
вообще в текущей версии. Поскольку вы хотите перезвонить в Python, используйте "OO"
и сохраните номер в качестве PyObject
.
Я не уверен, почему все говорят о GIL здесь. Вы выпускаете его где угодно? Я замечаю несколько возможных проблем, которые я приведу ниже. Он смешался с предложениями.
Я заметил, что вы не увеличиваете количество ссылок userData
. Почему нет? Вы храните его, не так ли? Почему вы хотите сохранить его в кортеже? Почему бы просто не сохранить две переменные (для обратного вызова и данные), а затем увеличить их количество, чтобы вы имели ссылку?
Прежде всего, проверьте возвращаемые значения всех ваших функций, чтобы увидеть, являются ли они NULL
. Это позволит вам убедиться, что вы действительно работаете должным образом. Также неплохо использовать PyObject_Print
для печати объектов, которые вас интересуют, во всех точках кода, чтобы убедиться, что все идет так, как вы ожидаете.
Из вашего комментария о том, где происходит segfault, я предполагаю, что вы передаете неправильное количество параметров своей функции обратного вызова, когда вы вызываете ее с помощью PyObject_CallFunctionObjArgs
. Вы можете проверить его дважды? Возможно, покажите нам свое определение функции обратного вызова, а затем снова проверьте вызов.
Другая возможная проблема, которую я вижу, заключается в том, что из имен, py_some_number
звучит так, как вы ожидаете целочисленный объект Python. Это не тот случай. После PyArg_ParseTuple
он будет содержать целое число.
userData
может быть только одним указателем. Я проверил и дважды проверил все вызовы и аргументы, и они определенно верны. Спасибо за подсказку об увеличении refcounts, хотя!