【问题标题】:Python method to boost function提升功能的Python方法
【发布时间】:2011-01-11 20:51:07
【问题描述】:

我有一个使用 boost python 将 boost::function 作为参数导出到 Python 的方法。

从我读到的boost::python应该支持boost::function而不用大惊小怪,但是当我尝试用python方法调用函数时,它给了我这个错误

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)

我用这段代码从 python 调用它

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

在 C++ 中它被定义为

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

这里的目标是一个计时器类,我可以在其中做类似的事情

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

创建一个执行 funcWithArgs 的计时器。多亏了 boost bind,这几乎可以与任何函数或方法一起使用。

那么我需要使用什么语法让 boost::python 接受我的 python 函数作为 boost::function?

【问题讨论】:

    标签: c++ python boost boost-python


    【解决方案1】:

    在 python 邮件列表上得到了答案,经过一些修改和更多研究后,我得到了我想要的 :)

    我确实在 mithrandi 之前看过那篇文章,但我不喜欢必须像这样声明函数的想法。使用一些花哨的包装器和一点 python 魔法,这可以同时工作并看起来很好!

    首先,用这样的代码包装你的 python 对象

    struct timer_func_wrapper_t
    {
        timer_func_wrapper_t( bp::object callable ) : _callable( callable ) {}
    
        bool operator()()
        {
            // These GIL calls make it thread safe, may or may not be needed depending on your use case
            PyGILState_STATE gstate = PyGILState_Ensure();
            bool ret = _callable();
            PyGILState_Release( gstate );
            return ret;
        }
    
        bp::object _callable;
    };
    
    boost::int32_t createTimerWrapper( Class* class, boost::uint64_t interval, bp::object function, bool recurring = false )
    {
        return class->createTimer( interval, boost::function<bool ()>( timer_func_wrapper_t( function ) ), recurring );
    }
    

    在你的类中定义这样的方法

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

    有了一点点包装,你就可以像这样施展魔法

    import MyLib
    import time
    
    def callMePls():
        print( "Hello world" )
        return True
    
    class = MyLib.Class()
    
    class.createTimer( 3, callMePls )
    
    time.sleep( 1 )
    

    为了完全模仿 C++,我们还需要一个 boost::bind 实现,可以在这里找到:http://code.activestate.com/recipes/440557/

    有了它,我们现在可以做这样的事情了

    import MyLib
    import time
    
    def callMePls( str ):
        print( "Hello", str )
        return True
    
    class = MyLib.Class()
    
    class.createTimer( 3, bind( callMePls, "world" ) )
    
    time.sleep( 1 )
    

    编辑:

    我喜欢尽可能跟进我的问题。我成功地使用了这段代码一段时间,但我发现当你想在对象构造函数中使用 boost::function 时,它会崩溃。 有一种方法可以使其工作方式与此类似,但您构造的新对象最终会具有不同的签名,并且不会与像它自己一样的其他对象一起工作。

    这终于让我感到厌烦,以至于我不得不做点什么,因为我对 boost::python 有了更多的了解,现在我想出了一个非常好的使用转换器的“万能”解决方案。 此处的代码会将 python 可调用对象转换为 boost::python 对象,可以轻松修改它以转换为其他 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;
        }
    };
    

    然后在你的初始化代码中,即BOOST_PYTHON_MODULE(),通过创建结构体来注册类型

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-25
      • 2016-05-16
      • 2020-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多