2012-07-11 6 views
8

Ho un widget wx.py.Shell.shell che consente all'utente di eseguire codice python che interagisce con il mio programma. Voglio essere in grado di passare una funzione che l'utente definisce in questo spazio al mio codice C++ (attraverso il wrapper generato da wxswig attorno al mio widget personalizzato) ed eseguirlo.Python Callback da SWIG PyObject_Call Segfault

Nel mio codice C++ sto usando uno std :: funzione <> classe per richiamare le funzioni Bound (C++ o Python)

così ho creato una semplice classe per avvolgere il PyObject con l'operatore funzione di chiamata. Tuttavia ottengo un segfault quando provo a chiamare 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); 
} 

Nei miei tentativi di trovare ciò che stava accadendo, ho messo un sacco di dichiarazioni di stampa. Uno dei quali stampa il nome del tipo e il riferimento conteggia la linea prima del segfault. Questo si traduce in "funzione 3", quindi devo supporre che la funzione non è stata ancora distrutta.

sto passando il testo seguente sorso:

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

In cui costruisco un PyMenuCallback

Sono in perdita per che cosa sta causando il segfault, tutte le idee?

+1

credo che tu abbia violato la [regola del tre] (http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) non fornendo un 'operator =' per 'PyMenuCallback'. Non sono sicuro se questo sia il problema qui o no, ma certamente ha il potenziale per causare problemi. – Flexo

+0

Non sono riuscito a riprodurre questo sul mio computer con un test case. Sono riuscito a confermare che 'operator =' non viene utilizzato per errore, ma il codice ha funzionato e non ha generato alcun avviso da parte di valgrind. Puoi espandere e semplificare un po 'il tuo caso di test usando '% inline' e'% {%} 'per fare in modo che tu abbia solo un singolo file di interfaccia? Per esempio. Ho usato: [this] (http://pastebin.com/XYXj3a4p) per testare quali potrebbero avere sottili differenze rispetto a ciò che stai usando/avvolgendo. – Flexo

+1

In effetti, buona cattura, ho dimenticato l'operatore =. Tuttavia non è usato al momento, ma lo aggiungerò. – Tocs

risposta

3

Poiché la C++ chiamando la callback pitone è all'interno di un wxWidget, e l'involucro sorso viene generato dallo speciale wxPython sorso (wxswig?) C'è una certa protezione filo necessario intorno alla chiamata di funzione ...

L'fissa operatore dovrebbe assomigliare a questo

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

Assicurati di includere

#include "wx/wxPython/wxPython.h" 
#include "wx/wxPython/wxPython_int.h"