2010-02-01 3 views
5

Ho un metodo esportato in Python usando boost python che accetta come argomento una funzione boost ::.Metodo Python per potenziare la funzione

Da quello che ho letto boost :: python dovrebbe sostenere boost :: funzione senza troppo baccano, ma quando provo a chiamare la funzione con un metodo pitone mi dà questo errore

Boost.Python.ArgumentError: Python argument types in 
    Class.createTimer(Class, int, method, bool) 
did not match C++ signature: 
    createTimer(class Class {lvalue}, unsigned long interval, 
    class boost::function<bool _cdecl(void)> function, bool recurring=False) 

sto chiamando da pitone con questo codice

self.__class.createTimer(3, test.timerFunc, False) 

e in C++ è definito come

boost::int32_t createTimer(boost::uint32_t interval, boost::function< bool() > function, bool recurring = false); 

L'obiettivo qui è una classe timer dove posso fare qualcosa di simile

class->createTimer(3, boost::bind(&funcWithArgs, arg1, arg2)) 

per creare un timer che esegue i funcWithArgs. Grazie a boost bind questo funzionerà praticamente con qualsiasi funzione o metodo.

Quindi qual è la sintassi che devo usare per boost :: python per accettare le mie funzioni python come boost :: function?

risposta

10

ottenuto una risposta sulla mailing list di pitone, e dopo un po 'di rielaborazione e più ricerca ho ottenuto esattamente quello che volevo :)

ho fatto vedere che post prima mithrandi ma non mi piace l'idea di avere dichiarare le funzioni in questo modo. Con alcuni involucri di fantasia e un po 'di magia pitone questo può funzionare e sembrare bello allo stesso tempo!

Per cominciare, avvolgere il vostro oggetto pitone con codice come questo

quando nella tua classe definire il metodo in questo modo

.def("createTimer", &createTimerWrapper, (bp::arg("interval"), bp::arg("function"), bp::arg("recurring") = false)) 

Con quel po 'di involucro si può lavorare magia come questo

import MyLib 
import time 

def callMePls(): 
    print("Hello world") 
    return True 

class = MyLib.Class() 

class.createTimer(3, callMePls) 

time.sleep(1) 

Per simulare completamente il C++, abbiamo anche bisogno di un potenziamento :: attua bind zione che può essere trovato qui: http://code.activestate.com/recipes/440557/

Con questo, ora possiamo fare qualcosa di simile

import MyLib 
import time 

def callMePls(str): 
    print("Hello", str) 
    return True 

class = MyLib.Class() 

class.createTimer(3, bind(callMePls, "world")) 

time.sleep(1) 

EDIT:

mi piace dare un seguito a mie domande quando posso. Stavo usando questo codice con successo per un po ', ma ho scoperto che questo cade a pezzi quando si desidera prendere boost :: function nei costruttori di oggetti. C'è un modo per farlo funzionare in modo simile a questo, ma il nuovo oggetto che costruisci finisce con una firma diversa e non funzionerà con altri oggetti come se stesso.

Questo finalmente mi ha infastidito abbastanza da fare qualcosa al riguardo e dato che so di più su boost :: python ora ho trovato una soluzione abbastanza adatta per tutti i convertitori. Questo codice convertirà un python callable in un oggetto boost :: python < bool()>, può essere facilmente modificato per convertirlo in altre funzioni di boost.

// Wrapper for timer function parameter 
struct timer_func_wrapper_t 
{ 
    timer_func_wrapper_t(bp::object callable) : _callable(callable) {} 

    bool operator()() 
    { 
     return _callable(); 
    } 

    bp::object _callable; 
}; 

struct BoostFunc_from_Python_Callable 
{ 
    BoostFunc_from_Python_Callable() 
    { 
     bp::converter::registry::push_back(&convertible, &construct, bp::type_id< boost::function< bool() > >()); 
    } 

    static void* convertible(PyObject* obj_ptr) 
    { 
     if(!PyCallable_Check(obj_ptr)) return 0; 
     return obj_ptr; 
    } 

    static void construct(PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data) 
    { 
     bp::object callable(bp::handle<>(bp::borrowed(obj_ptr))); 
     void* storage = ((bp::converter::rvalue_from_python_storage< boost::function< bool() > >*) data)->storage.bytes; 
     new (storage)boost::function< bool() >(timer_func_wrapper_t(callable)); 
     data->convertible = storage; 
    } 
}; 

Poi nel codice di inizializzazione, vale a dire, BOOST_PYTHON_MODULE(), basta registrare il tipo con la creazione della struct

BOOST_PYTHON_MODULE(Foo) 
{ 
    // Register function converter 
    BoostFunc_from_Python_Callable();