Обратный вызов Python из SWIG PyObject_Call Segfault

У меня есть виджет wx.py.Shell.shell, который позволяет пользователю выполнять код Python, взаимодействующий с моей программой. Я хочу иметь возможность передать функцию, которую пользователь определяет в этом пространстве, в мой код C++ (через сгенерированную wxswig оболочку вокруг моего пользовательского виджета) и выполнить ее.

В моем коде C++ я использую класс std::function ‹> для вызова связанных функций (C++ или Python)

Поэтому я создал простой класс, чтобы обернуть PyObject оператором вызова функции. Однако я получаю segfault, когда пытаюсь вызвать PyObject *.

class PyMenuCallback
{
    PyObject *Func;
public:
    PyMenuCallback(const PyMenuCallback &op2);
    PyMenuCallback(PyObject *func);
    ~PyMenuCallback ();

    void operator() (int id);
};
/////////////////////////////////////////////////////////
PyMenuCallback::PyMenuCallback(PyObject *func)
    : Func(func)
{
    Py_XINCREF (Func);
    if(!PyCallable_Check(Func))
        cout << "Not a Callable Callback." << endl; //Throw an exception or something
}

PyMenuCallback::PyMenuCallback(const PyMenuCallback &op2)
    : Func (op2.Func)
{
    Py_XINCREF (Func);
    if(!PyCallable_Check(Func))
        cout << "Not a Callable Callback." << endl;
}

PyMenuCallback::~PyMenuCallback()
{
    Py_XDECREF (Func);
}

void PyMenuCallback::operator() (int id)
{
    cout << "Calling Callback" << endl;
    if (Func == 0 || Func == Py_None || !PyCallable_Check(Func))
        return;
    cout << "Building Args" << endl;   
    PyObject *arglist = Py_BuildValue ("(i)",id);
    cout << "Func: " << Func->ob_type->tp_name << " " << Func->ob_refcnt << endl;
    PyObject *result = PyObject_Call(Func,arglist,0); //<<<<<---SEGFAULTS HERE
    cout << "Executed" << endl;
    Py_DECREF(arglist);
    Py_XDECREF(result);
}

В своих попытках выяснить, что происходит, я поместил кучу печатных утверждений. Один из которых печатает имя типа и количество ссылок на строку перед segfault. Это приводит к «функции 3», поэтому я должен предположить, что функция еще не уничтожена.

Я передаю следующее swig:

void AddOption (std::string name, PyObject *pycallback);

В котором я создаю PyMenuCallback

Я в недоумении, что вызывает segfault, есть идеи?


person Tocs    schedule 11.07.2012    source источник
comment
Я думаю, вы нарушили правило трех, не указав operator= для PyMenuCallback. Я не уверен, проблема в этом или нет, но это, безусловно, может вызвать проблемы.   -  person Flexo    schedule 11.07.2012
comment
Я не смог воспроизвести это на своей машине с помощью тестового примера. Мне удалось убедиться, что operator= используется не случайно, но код работал и не выдавал никаких предупреждений от valgrind. Можете ли вы немного расширить и упростить свой тестовый пример, используя %inline и %{%}, чтобы у вас был только один файл интерфейса? Например. Я использовал: это, чтобы проверить, что может иметь небольшие отличия от того, что вы используете/обертываете.   -  person Flexo    schedule 11.07.2012
comment
Действительно, хороший улов, я забыл оператор =. Правда, пока не используется, но добавлю.   -  person Tocs    schedule 11.07.2012


Ответы (1)


Поскольку С++, вызывающий обратный вызов python, находится внутри wxWidget, а оболочка swig создается специальным swig wxPython (wxswig?), вокруг вызова функции требуется некоторая защита потока...

Фиксированный оператор должен выглядеть так

void PyMenuCallback::operator() (int id)
{
    cout << "Calling Callback" << endl;
    if (Func == 0 || Func == Py_None || !PyCallable_Check(Func))
        return;
    cout << "Building Args" << endl;   
    PyObject *arglist = Py_BuildValue ("(i)",id);
    cout << "Built: " << arglist << endl;
    cout << "Func: " << Func->ob_type->tp_name << " " << Func->ob_refcnt << endl;

    wxPyBlock_t blocked = wxPyBeginBlockThreads(); //Anti-WxSwig 

    PyObject *result = PyObject_Call(Func,arglist,0);

    wxPyEndBlockThreads(blocked);


    cout << "Executed" << endl;
    Py_XDECREF(arglist);
    Py_XDECREF(result);
}

Обязательно включите

#include "wx/wxPython/wxPython.h"
#include "wx/wxPython/wxPython_int.h"
person Tocs    schedule 18.02.2015