提升python:使用return_internal_reference将参数的生存期与返回值联系起来 [英] boost python: tie lifetime of argument to returned value using return_internal_reference

查看:68
本文介绍了提升python:使用return_internal_reference将参数的生存期与返回值联系起来的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始学习使用boost python并遇到新手问题.

I begin learning to use boost python and have a rookie question.

我想编写一个函数,该函数可以将其参数的生命周期与其结果联系起来,这样,当我调用 r = func(a)时,参数 a r 的引用,它将永远不会被销毁.该文档建议针对此类型的请求使用 return_internal_reference 调用策略.但这是否要求 r 作为 a 的内部引用,如其名称所示?

I would like to write a function that can tie the lifetime of its argument to its results, such that when I call r = func(a), the argument a will never be destroyed if I still have a reference of r. The documentation suggests using return_internal_reference call policy for this type of request. But does this require r to be an internal reference of a, as the name suggested?

在下面的(过度简化的)示例中,假设我想将输入数组 a 的生​​存期与生成的lambda函数联系起来,该函数不是输入 a <的内部引用/code>.

In the (over-simplified) example below, suppose I want to tie the lifetime of input array a with the generated lambda function, which is not an internal reference of input a.

#include <functional>
#include <boost/python.hpp>
#include <boost/python/return_internal_reference.hpp>

using namespace std;
using namespace boost::python;

function<float(int)> func(const float* a) {
  return [=](int n) { return a[n]; };
}

BOOST_PYTHON_MODULE(test) {
  def("func", func, return_internal_reference<1>());
}

我希望能够在python中执行以下操作:

I hope to be able to do the following in python:

f = func(a)   # 'a' can be a temporary variable, say returned by another function
f(5)          # but 'a' should not be destroyed at this step, 
              # because its lifetime is tied to 'f'

当我尝试编译上面的代码时,下面列出了很多错误,但是如果我删除 return_internal_reference< 1>()调用策略,则代码将成功编译.

When I tried to compile the code above, I got a wall of errors listed below, but if I remove the return_internal_reference<1>() call policy, the code compiles successfully.

我很确定我错误地使用了此通话政策,但不确定如何使它正确.任何指针将不胜感激.非常感谢!

I am pretty sure I used this call policy wrong, but am not sure how to make it right. Any pointer will be highly appreciated. Thanks a lot!

$ g++ -std=c++11 -shared Test.cc -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -L/opt/local/lib -lboost_python-mt -lpython2.7 -o test.so
In file included from /opt/local/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:52:0,
                 from /opt/local/include/boost/python/detail/invoke.hpp:63,
                 from /opt/local/include/boost/python/detail/caller.hpp:16,
                 from /opt/local/include/boost/python/object/function_handle.hpp:8,
                 from /opt/local/include/boost/python/converter/arg_to_python.hpp:19,
                 from /opt/local/include/boost/python/call.hpp:15,
                 from /opt/local/include/boost/python/object_core.hpp:14,
                 from /opt/local/include/boost/python/args.hpp:25,
                 from /opt/local/include/boost/python.hpp:11,
                 from Test.cc:2:
/opt/local/include/boost/python/detail/invoke.hpp: In instantiation of 'PyObject* boost::python::detail::invoke(boost::python::detail::invoke_tag_<false, false>, const RC&, F&, AC0&) [with RC = boost::python::detail::reference_existing_object_requires_a_pointer_or_reference_return_type<std::function<float(int)> >; F = std::function<float(int)> (*)(const float*); AC0 = boost::python::arg_from_python<const float*>; PyObject = _object]':
/opt/local/include/boost/python/detail/caller.hpp:223:13:   required from 'PyObject* boost::python::detail::caller_arity<1u>::impl<F, Policies, Sig>::operator()(PyObject*, PyObject*) [with F = std::function<float(int)> (*)(const float*); Policies = boost::python::return_internal_reference<>; Sig = boost::mpl::vector2<std::function<float(int)>, const float*>; PyObject = _object]'
/opt/local/include/boost/python/object/py_function.hpp:38:33:   required from 'PyObject* boost::python::objects::caller_py_function_impl<Caller>::operator()(PyObject*, PyObject*) [with Caller = boost::python::detail::caller<std::function<float(int)> (*)(const float*), boost::python::return_internal_reference<>, boost::mpl::vector2<std::function<float(int)>, const float*> >; PyObject = _object]'
Test.cc:14:1:   required from here
/opt/local/include/boost/python/detail/invoke.hpp:75:82: error: no match for call to '(const boost::python::detail::reference_existing_object_requires_a_pointer_or_reference_return_type<std::function<float(int)> >) (std::function<float(int)>)'
     return rc(f( BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, ac, () BOOST_PP_INTERCEPT) ));
                                                                                  ^
In file included from /opt/local/include/boost/python/object/function_handle.hpp:8:0,
                 from /opt/local/include/boost/python/converter/arg_to_python.hpp:19,
                 from /opt/local/include/boost/python/call.hpp:15,
                 from /opt/local/include/boost/python/object_core.hpp:14,
                 from /opt/local/include/boost/python/args.hpp:25,
                 from /opt/local/include/boost/python.hpp:11,
                 from Test.cc:2:
/opt/local/include/boost/python/detail/caller.hpp: In instantiation of 'static const PyTypeObject* boost::python::detail::converter_target_type<ResultConverter>::get_pytype() [with ResultConverter = boost::python::detail::reference_existing_object_requires_a_pointer_or_reference_return_type<std::function<float(int)> >; PyTypeObject = _typeobject]':
/opt/local/include/boost/python/detail/caller.hpp:240:19:   required from 'static boost::python::detail::py_func_sig_info boost::python::detail::caller_arity<1u>::impl<F, Policies, Sig>::signature() [with F = std::function<float(int)> (*)(const float*); Policies = boost::python::return_internal_reference<>; Sig = boost::mpl::vector2<std::function<float(int)>, const float*>]'
/opt/local/include/boost/python/object/py_function.hpp:48:35:   required from 'boost::python::detail::py_func_sig_info boost::python::objects::caller_py_function_impl<Caller>::signature() const [with Caller = boost::python::detail::caller<std::function<float(int)> (*)(const float*), boost::python::return_internal_reference<>, boost::mpl::vector2<std::function<float(int)>, const float*> >]'
Test.cc:14:1:   required from here
/opt/local/include/boost/python/detail/caller.hpp:102:109: error: 'struct boost::python::detail::reference_existing_object_requires_a_pointer_or_reference_return_type<std::function<float(int)> >' has no member named 'get_pytype'
         return create_result_converter((PyObject*)0, (ResultConverter *)0, (ResultConverter *)0).get_pytype();
                                                                                                             ^

推荐答案

考虑使用

Consider using the with_custodian_and_ward_postcall CallPolicy. This policy allows for the return types to be returned by value, while still extending the lifetime of another object to be at least as long as that of the returned object.

BOOST_PYTHON_MODULE(test) {
  def("func", func, with_custodian_and_ward_postcall<0, 1>());
}


return_internal_reference 文档,返回的对象引用了一个现有的内部对象:


As noted in the return_internal_reference documentation, the object returned references an existing internal object:

return_internal_reference [...]允许安全地返回指向内部保存的对象的指针和引用,而无需复制参考对象.

return_internal_reference [...] allow pointers and references to objects held internally [...] to be returned safely without making a copy of the referent.

文档还简要提及了其对 with_custodian_and_ward_ward_postcall 的使用.总而言之, return_internal_reference 对要公开的函数有两个值得注意的影响:

The documentation also briefly mentions its use of with_custodian_and_ward_postcall. In summary, return_internal_reference has two note worthy effects on the function being exposed:

  • 返回的Python对象既不具有所引用的C ++对象的显式所有权也不具有共享所有权.
  • 返回的Python对象是一个托管人,它将延长由 owner_arg 指示的病房对象的寿命至少与该托管人一样长.
  • The returned Python object neither has explicit nor shared ownership of the referenced C++ object.
  • The returned Python object is a custodian, and will extend the lifetime of the ward object, indicated by the owner_arg, to be at least as long as the custodian.

由于返回的Python对象引用了既不具有明确所有权也不具有共享所有权的现有对象,因此Boost.Python执行类型检查以防止创建悬空引用.在示例代码中, func()按值返回函子,导致编译器错误,提示返回类型必须是指针或引用:

As the returned Python object refers to an existing object for which it has neither explicit nor shared ownership, Boost.Python performs type checking to prevent creating a dangling reference. In the example code, func() returns a functor by value, resulting in a compiler error hinting that the return type must be either a pointer or reference:

struct boost::python::detail::
reference_existing_object_requires_a_pointer_or_reference_return_type

要显式控制返回对象的生存期,应考虑使用政策组成:

To explicitly control the lifetime of the returned object, one should consider using return_value_policy and models of ResultConverterGenerators. For example, if func() returned a functor by pointer that was created by new(), and wished to pass ownership of the object to Python while still maintaining the custodian and ward relationship, then one could chain policies with policy composition:

BOOST_PYTHON_MODULE(test) {
  def("func", func, 
    return_value_policy<manage_new_object,
      with_custodian_and_ward_postcall<0, 1> >());
}


这是一个基于原始代码的完整的最小示例,其详细输出到演示 with_custodian_and_ward_postcall 行为:

#include <boost/python.hpp>
#include <iostream>

/// @brief Mockup class with verbose construction and destruction.
class foo
{
public:
  foo() { std::cout << "foo() " << this << std::endl; }
  foo(const foo&) { std::cout << "foo(const foo&) " << this << std::endl; }
  ~foo() { std::cout << "~foo() " << this << std::endl; }
};

/// @brief Mockup class with verbose construction and destruction.
class bar
{
public:
  bar() { std::cout << "bar() " << this << std::endl; }
  bar(const bar&) { std::cout << "bar(const bar&) " << this << std::endl; }
  ~bar() { std::cout << "~bar() " << this << std::endl; }
};

/// @brief Mockup factory function.
foo make_foo(bar& /* unused */)
{
  return foo();
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  // Do not allow Foo to be explicitly created from its type.
  python::class_<foo>("Foo", python::no_init);
  python::class_<bar>("Bar", python::init<>());

  // Expose make_foo, that returns a foo object when provided a
  // bar object.  The bar object's lifetime will be extended to
  // be at least as long as that of the returned foo object.
  python::def("make_foo", &make_foo,
    python::with_custodian_and_ward_postcall<
      0, // custodian = returned Foo object
      1  // ward = provided Bar object
    >());
}

互动用法:

>>> import example
>>> bar = example.Bar()
bar() 0x125ac30
>>> foo = example.make_foo(bar)
foo() 0x7fffa9b5efff
foo(const foo&) 0x7f1fcbe40090
~foo() 0x7fffa9b5efff
>>> bar = None
>>> foo = None
~foo() 0x7f1fcbe40090
~bar() 0x125ac30

请注意,尽管将 bar 变量设置为 None ,但实际的病房对象仍处于活动状态,直到销毁 foo 保管人为止.

Notice that despite the bar variable being set to None, the actual ward object remains alive until the foo custodian is destroyed.

这篇关于提升python:使用return_internal_reference将参数的生存期与返回值联系起来的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆