【问题标题】:Exposing C++ functions, that return pointer using Boost.Python公开 C++ 函数,使用 Boost.Python 返回指针
【发布时间】:2014-08-25 10:49:23
【问题描述】:

我想使用 Boost.Python 向 Python 公开以下 C++ 函数:

int* test1() {
    return new int(42);
}

// Now exposing the function with Boost.Python

BOOST_PYTHON_MODULE(libtest1)
{
    using namespace boost::python;
    def("test1", test1);
}

当我尝试编译这个库时,由于(我的猜测)Boost.Python 不知道如何将 int* 转换为 PyObject。

我认为需要做的是定义转换结构,如下所示:

template<class T>
struct int_ptr_to_python
{
   static PyObject* convert(int* i_ptr)
   {
        return i_ptr;
   }
};

并将其传递给 BOOST_PYTHON_MODULE 声明:

BOOST_PYTHON_MODULE(libtest1)
{
    using namespace boost::python;
    def("test1", test1);
    to_python_converter<int*, int_ptr_to_python<int*> >();
}

但它也不起作用。而且我找不到有关如何处理返回指针的函数的任何信息。

有人可以帮忙吗?

【问题讨论】:

    标签: python c++ boost boost-python


    【解决方案1】:

    简而言之,不能使用 Boost.Python 直接公开返回 int* 的函数,因为在 Python 中没有有意义的对应类型,因为整数是不可变的。

    • 如果目标是向 Python 返回一个数字,则按值返回 int。这可能需要使用辅助函数来适配旧版 API。
    • 如果目标是返回指向使用 new 表达式分配的对象的指针,则该对象必须是类或联合,并且必须使用特定策略公开函数以指示所有权责任。

    与其立即提出最终解决方案,我更愿意花时间逐步解决编译器错误。对于 Boost.Python,有时使用 C++11 之前的静态断言在编译器消息中提供指令。不幸的是,在繁重的模板中很难找到它们。

    以下代码:

    #include <boost/python.hpp>
    
    int* make_int() { return new int(42); }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
      python::def("make_int", &make_int);
    }
    

    在 clang 中生成以下相关输出,其中重要的细节以粗体突出显示:

    .../boost/python/detail/caller.hpp:102:98: 错误: 'boost::python::detail:: 中没有名为 'get_pytype' 的成员 指定_a_return_value_policy_to_wrap_functions_returning' ...create_result_converter((PyObject*)0, (ResultConverter *)0, (ResultConverter *)0).g...

    Boost.Python 通知我们需要为返回 int* 的函数指定 boost::python::return_value_policyResultConverterGenerators有多种型号。通常,这些策略用于控制返回对象的所有权或生命周期语义。由于原始代码中的函数直接返回一个指向 Python 的新指针,因此如果希望调用者负责删除该对象,则 boost::python::manage_new_object 是合适的。

    为对象管理指定策略仍然失败:

    #include <boost/python.hpp>
    
    int* make_int() { return new int(42); }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
      python::def("make_int", &make_int,
        python::return_value_policy<python::manage_new_object>());
    }
    

    产生以下相关输出:

    .../boost/python/object/make_instance.hpp:27:9: 错误:没有匹配的函数调用“assertion_failed” BOOST_MPL_ASSERT((mpl::or_, is_union >));

    在这种情况下,Boost.Python 通知我们从具有managed_new_object ResultConverterGenerator 的函数返回的对象必须是classunion。对于int*,最合适的解决方案是通过Boost.Python层时按值返回int。但是,为了完整起见,以下演示:

    • 使用辅助函数来适配旧版 API。
    • 如何使用返回指针的工厂函数来限制用户定义类型的创建。
    #include <boost/python.hpp>
    
    /// Legacy API.
    int* make_int() { return new int(42); }
    
    /// Auxiliary function that adapts the legacy API to Python.
    int py_make_int()
    {
      std::auto_ptr<int> ptr(make_int());
      return *ptr;
    }
    
    /// Auxiliary class that adapts the legacy API to Python.
    class holder
      : private boost::noncopyable
    {
    public:
      holder()
        : value_(make_int())
      {}
    
      int get_value() const     { return *value_; }
      void set_value(int value) { *value_ = value; }
    
    private:
      std::auto_ptr<int> value_;
    };
    
    /// Factory function for the holder class.
    holder* make_holder()
    {
      return new holder();
    }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
      python::def("make_int", &py_make_int);
    
      python::class_<holder, boost::noncopyable>("Holder", python::no_init)
        .add_property("value",
          python::make_function(&holder::get_value),
          python::make_function(&holder::set_value))
        ;
    
      python::def("make_holder", &make_holder,
        python::return_value_policy<python::manage_new_object>());
    }
    

    互动使用:

    >>> import example
    >>> assert(42 == example.make_int())
    >>> holder = example.Holder()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    RuntimeError: This class cannot be instantiated from Python
    >>> holder = example.make_holder()
    >>> assert(42 == holder.value)
    >>> holder.value *= 2
    >>> assert(84 == holder.value)
    

    【讨论】:

    • 感谢您的详细解答!
    • 你是整个堆栈溢出中唯一理解这个混淆系统的人
    猜你喜欢
    • 1970-01-01
    • 2012-03-31
    • 1970-01-01
    • 2010-12-25
    • 2010-11-03
    • 2012-02-26
    • 2020-04-29
    • 2015-08-05
    • 1970-01-01
    相关资源
    最近更新 更多