views:

58

answers:

1

I'm writing a program in python which should be able to pass "callables" which are registered in a C++ class. So far I've written the following code:

C++:

class MyClass
{
...
public:
    register_callback(boost::function<void (int)> fun);
};

Python/C API:

class_<MyClass, boost::shared_ptr<MyClass>, boost::noncopyable>("MyClass", no_init)
    .def("register_callback", &MyClass::register_callback);

The code compiles but when I call the method from python, passing a callable (in my case, another instance method) I get the following runtime error:

Boost.Python.ArgumentError: Python argument types in
    MyClass.register_callback(MyClass, instancemethod)
did not match C++ signature:
    register_callback(MyClass {lvalue}, boost::function<void ()(int)>)

I think I need a way to tell boost he can safety pass a callable every time a boost::function is required. Everything works if I use an hand-made solution:

void my_caller(MyClass* obj, object callable)
{
    obj->register_callback(callable);  // Works!
}

class_<MyClass, boost::shared_ptr<MyClass>, boost::noncopyable>("MyClass", no_init)
    .def("register_callback", &my_caller);

Since I only have a few register functions like this I could stick with the hand-made solution (with macros to help), but I wonder how can I tell to boost::python to make the conversion automatically. Looking at the documentation I found the to_python_converter directive which, quite ironically, does exactly the contrary of what I need...

+1  A: 

As you have inferred, MyClass is being passed in as a boost::python::object while register_callback wants a boost::function. Fortunately, the solution is simple enough - I think. Your code would look something like this (untested) (adapted from http://mail.python.org/pipermail/cplusplus-sig/2010-February/015199.html):

...
struct callable_wrapper_t
{
    callable_wrapper_t( boost::python::object callable ) : _callable( callable ) {}

    void operator()()
    {
        // These GIL calls make it thread safe, may or may not be needed depending on your use case
        PyGILState_STATE gstate = PyGILState_Ensure();
        _callable();
        PyGILState_Release( gstate );
    }

    boost::python::object _callable;
};
...
class MyClass
{
...
public:
    register_callback(boost::function<void (int)>(callback_wrapper_t(fun)));
};
...
class_<MyClass, boost::shared_ptr<MyClass>, boost::noncopyable>("MyClass", no_init)
    .def("register_callback", &my_caller);
...

Also, you might want to take a look at py_boost_function.hpp

spenthil