Регистрация вызываемых Python в классах C ++ - PullRequest
0 голосов
/ 10 сентября 2010

Я пишу программу на python, которая должна иметь возможность передавать «вызываемые», которые зарегистрированы в классе C ++. До сих пор я написал следующий код:

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

Код компилируется, но когда я вызываю метод из python, передавая вызываемый (в моем случае, другой метод экземпляра), я получаю следующую ошибку времени выполнения:

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

Я думаю, что мне нужен способ сказать, что boost может безопасно передавать вызываемый объект каждый раз, когда требуется boost :: function. Все работает, если я использую решение ручной работы:

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

Поскольку у меня есть только несколько таких функций регистрации, я мог бы придерживаться ручного решения (с помощью макросов, чтобы помочь), но мне интересно, как я могу сказать, чтобы boost :: python сделал преобразование автоматически. Глядя на документацию, я нашел директиву to_python_converter, которая, по иронии судьбы, делает в точности то, что мне нужно ...

1 Ответ

1 голос
/ 11 сентября 2010

Как вы уже поняли, MyClass передается как boost::python::object, а register_callback хочет boost::function.К счастью, решение достаточно простое - я думаю.Ваш код будет выглядеть что-то вот так ( не проверено ) (адаптировано из 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);
...

Кроме того, вы можете посмотреть на py_boost_function.hpp

...