10

I have a wx.py.Shell.shell widget which lets the user execute python code that interacts with my program. I want to be able to pass a function that the user defines in this space to my C++ code (Through the wxswig generated wrapper around my custom widget)and execute it.

In my C++ code I'm using a std::function <> class to invoke bound functions (C++ or Python)

So I created a simple class to wrap the PyObject with the function call operator. However I get a segfault when I try to call the 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);
}

In my attempts to find what was going on, I put a bunch of print statements. One of which prints the type name and reference count the line before the segfault. This results in "function 3" so I have to assume the function has not been destroyed yet.

I'm passing the following to swig:

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

In which I construct a PyMenuCallback

I'm at a loss for what's causing the segfault, any ideas?

Tocs
  • 752
  • 4
  • 17
  • 1
    I think you've violated the [rule of three](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) by not supplying an `operator=` for `PyMenuCallback`. I'm not sure if that's the problem here or not though, but it certainly has the potential to cause problems. – Flexo Jul 11 '12 at 15:49
  • I was unable to reproduce this on my machine with a test case. I managed to confirm that `operator=` isn't being used by accident, but the code worked and didn't generate any warnings from valgrind either. Can you expand and simplify your test case a bit perhaps by using `%inline` and `%{%}` to make it so you just have a single interface file? E.g. I used: [this](http://pastebin.com/XYXj3a4p) to test which may have subtle differences to what you're using/wrapping. – Flexo Jul 11 '12 at 16:00
  • 1
    Indeed, good catch, I forgot operator=. It however is not used at the moment, but I shall add it. – Tocs Jul 11 '12 at 16:03

1 Answers1

4

Since the C++ calling the python callback is within a wxWidget, and the swig wrapper is generated by the special wxPython swig (wxswig?) There is some thread protection required around the function call...

The fixed operator should look like this

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);
}

Make sure to include

#include "wx/wxPython/wxPython.h"
#include "wx/wxPython/wxPython_int.h"
Tocs
  • 752
  • 4
  • 17