В общем, чтобы вызвать исключение с помощью CPython C API, вы вызываете одну из PyErr_Set*
функций , а затем возвращаете специальное значение из вашей функции API . Для функций, которые обычно возвращают объект Python, это специальное значение равно NULL
; для других функций вы должны проверить документацию, которая может существовать только в заголовочных файлах.
В этом случае цитируем pystate.h
(из CPython 3.7):
/* Py_tracefunc return -1 when raising an exception, or 0 for success. */
typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *);
Это означает, что вы должны объявить PythonRunner::tracer
с соответствующей подписью, например:
class PythonRunner {
// ...
public:
static int tracer(PyObject *, struct _frame *, int, PyObject *);
}
И затем вы можете вернуть & минус; 1, чтобы сообщить об исключении, или 0, чтобы продолжить нормально:
int PythonRunner::tracer(PyObject *, struct _frame *, int, PyObject *)
{
...
// Abort requested?
else if (interruptRequested())
{
PyErr_SetString(PyExc_Exception, "ABORT");
return -1;
}
// Otherwise proceed normally.
return 0;
}
Кроме того, ваш вызов PyEval_SetProfile больше не будет нуждаться в приведении.
PyEval_SetProfile(PythonRunner::tracer, NULL);
Я упоминаю об этом, потому что приведение указателей на функции почти всегда является ошибкой само по себе, и в этом случае признание того, что могло бы подсказать вам, в чем заключалась проблема.
(По историческим причинам пример кода в документации Python «Расширение и встраивание» содержит кучу ненужных приведений и функций с неправильными подписями. Всякий раз, когда вы копируете код из этого документа, удаляйте все приведенные приведения, а затем тщательно Изучите жалобы компилятора, настройте сигнатуры типов и верните только те составы, которые неизбежны.)