2010-02-01 11 views
5

Tengo un método exportado a Python usando boost python que toma una función boost :: como argumento.Método Python para aumentar la función

De lo que he leído impulso :: pitón debe apoyar impulso :: función sin mucho alboroto, pero cuando trato de llamar a la función con un método pitón me da este error

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) 

estoy llamando desde pitón con este código

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

y en C++ se define como

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

El objetivo aquí es una clase de temporizador en el que puedo hacer algo como

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

para crear un temporizador que ejecuta las funcWithArgs. Gracias a boost bind esto funcionará con casi cualquier función o método.

Entonces, ¿cuál es la sintaxis que necesito usar para boost :: python para aceptar mis funciones de python como una función boost ::?

Respuesta

10

obtuvo una respuesta en la lista de correo pitón, y después de un poco de reelaboración y más investigación me dieron exactamente lo que quería :)

vi ese puesto antes mithrandi pero no me gusta la idea de tener para declarar las funciones así. ¡Con algunas envolturas elegantes y un poco de magia de pitón esto puede funcionar y verse bien al mismo tiempo!

Para empezar, envuelva su objeto Python con código como este

cuando se encuentra en su clase de definir el método como tal

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

Con ese poco de envoltura se puede trabajar magia como esto

import MyLib 
import time 

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

class = MyLib.Class() 

class.createTimer(3, callMePls) 

time.sleep(1) 

Para imitar el C++ por completo, también necesitamos un boost :: bind implementar ación que se puede encontrar aquí: http://code.activestate.com/recipes/440557/

Con esto, ahora podemos hacer algo como esto

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:

me gusta hacer un seguimiento de mis preguntas cuando pueda. Estuve usando este código exitosamente por un tiempo, pero descubrí que esto se desmorona cuando quieres tomar la función boost :: en constructores de objetos. Hay una manera de hacerlo funcionar de manera similar a esto, pero el nuevo objeto que construye termina con una firma diferente y no funcionará con otros objetos como sí mismo.

Esto finalmente me molestó lo suficiente como para hacer algo al respecto y dado que sé más acerca de boost :: python ahora se me ocurrió una solución bastante buena para todos los convertidores. Este código aquí convertirá un pitón que se puede llamar a un objeto boost :: python < bool()>, se puede modificar fácilmente para convertirlo a otras funciones de impulso.

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

Luego, en el código de inicio, es decir, BOOST_PYTHON_MODULE(), simplemente grabar el tipo mediante la creación de la estructura

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