c++ - boost 函数的Python方法

标签 c++ python boost boost-python

我有一个使用 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?

最佳答案

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

关于c++ - boost 函数的Python方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2179345/

相关文章:

c++ - g++ -Wall 不警告 double-> int cast

python - 使用 python (django) 进行 AWS Elastic Beanstalk 日志记录

c++ - 从表中检索混合数据类型的模板化 get 方法

c++ - 创建静态链接所有依赖项的共享 .so 库

c++ - 分配非 const 变量的只读位置

c++ - 从使用 C++ 中的 new 创建的数组中删除一组元素

c++ - 创建一个简单的脚本 'language' - 类似 VARIANT 的值类型

python - 如何测试自定义 Flask 错误页面?

python - 并行运行一个又一个 exe

c++ - skipper 在 boost::spirit 中不起作用