代码之家  ›  专栏  ›  技术社区  ›  Charles

python方法提升函数

  •  5
  • Charles  · 技术社区  · 15 年前

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

    根据我读到的Boost::python应该支持Boost::函数而不必太费事,但是当我试图用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::函数呢?

    1 回复  |  直到 8 年前
        1
  •  11
  •   Charles    14 年前

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

    我在密特拉ndi之前看到过这个帖子,但是我不喜欢这样声明函数的想法。有了一些精美的包装纸和一点巨蟒的魔力,这可以工作,同时看起来很好!

    首先,用这样的代码包装您的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++,我们还需要一个Boosix::Band实现,可以在这里找到: 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::函数时,这段代码会崩溃。 有一种方法可以使它与此类似,但是您构造的新对象最终会有一个不同的签名,并且不会与其他对象(如它本身)一起工作。

    这终于让我烦得不知所措了,既然我对boost::python了解得更多,现在我就想出了一个使用转换器的非常好的“适合所有人”的解决方案。 这里的代码将把一个可调用的python转换成boost::python<bool()>对象,可以很容易地修改它来转换成其他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();