【问题标题】:boost python returning tuple containing custom typesboost python返回包含自定义类型的元组
【发布时间】:2016-03-12 11:29:51
【问题描述】:

我有一个 C++ myObject 类,我使用包装器结构通过 boost python 公开:

struct myObjectWrapper{
   static tuple compute(myObject& o,const Container& x0, const double& t0, Container& x){
      double t;
      int stat = o.evaluate(x0,t0,x,t);
      return make_tuple(stat,t);
   }
}

BOOST_PYTHON_MODULE(myModule)
{
   // not shown here is code to expose Container class

   class_<myObject>("MyObject")
      .def("compute",&myObjectWrapper::compute)
   ;
}

容器目前定义为:

typedef std::valarray<double> Container

并且暴露于 python。

现在在 python 中我可以做到。

x = Container()
(status,t) = obj.compute(Container([0.,0.,0.]),0.0,x)
print status, t, x[0]

这不是很pythonic。我更愿意这样做:

(status,t,x) = obj.compute(Container([0.,0.,0.]),0.0)
print status, t, x[0]    

我可以在 python 中编写一个额外的包装器,但我宁愿避免添加更多的包装器。

以下代码无法编译:

struct myObjectWrapper{
   static tuple compute(myObject& o,const Container& x0, const double& t0){
      double t;
      Container x;
      int stat = o.evaluate(x0,t0,x,t);
      return make_tuple(stat,t,x);
   }
}

另外我宁愿窃取局部变量 x 的内容并让 python 管理它而不是复制它:

return make_tuple(stat,t,std::move(x));

我如何做到这一点?

【问题讨论】:

    标签: python c++ boost tuples boost-python


    【解决方案1】:

    简而言之,在免费存储上分配包装器并使用 manage_new_object 结果转换将所有权转移到 Python 对象。这将导致 Boost.Python 在构造 Python 对象时复制指针,而不是复制指针。更多详情请见this答案。

    这是一个辅助函数,它将所有权转移给 Python 对象:

    /// @brief Transfer ownership to a Python object.  If the transfer fails,
    ///        then object will be destroyed and an exception is thrown.
    template <typename T>
    boost::python::object transfer_to_python(T* t)
    {
      // Transfer ownership to a smart pointer, allowing for proper cleanup
      // incase Boost.Python throws.
      std::unique_ptr<T> ptr(t);
    
      // Use the manage_new_object generator to transfer ownership to Python.
      namespace python = boost::python;
      typename python::manage_new_object::apply<T*>::type converter;
    
      // Transfer ownership to the Python handler and release ownership
      // from C++.
      python::handle<> handle(converter(*ptr));
      ptr.release();
    
      return python::object(handle);
    }
    

    并且可以按如下方式使用它:

    boost::python::tuple myObjectWrapper::compute(
      myObject& o, const Container& x0, const double& t0)
    {
      auto x1 = std::make_unique<container>();
      double t1 = 0;
      int stat = self.evaluate(x0, t0, *x1, t1);
      return boost::python::make_tuple(stat, t1, transfer_to_python(x1.release()));
    }
    

    这是一个完整的例子,基于原始问题demonstrates使用transfer_to_python辅助函数。

    #include <boost/python.hpp>
    #include <cassert>
    #include <memory> // std::unique_ptr
    
    // Mock legacy API.
    struct container
    {
      container() {}
      container(boost::python::object) {}
    
      container(const container&)
      {
        // For this example, guarantee copy is not made.
        assert(false);
       }
    };
    
    struct my_object
    {
      int evaluate(container&, double&, container&, double&) { return 42; }
    };
    
    /// @brief Transfer ownership to a Python object.  If the transfer fails,
    ///        then object will be destroyed and an exception is thrown.
    template <typename T>
    boost::python::object transfer_to_python(T* t)
    {
      // Transfer ownership to a smart pointer, allowing for proper cleanup
      // incase Boost.Python throws.
      std::unique_ptr<T> ptr(t);
    
      // Use the manage_new_object generator to transfer ownership to Python.
      namespace python = boost::python;
      typename python::manage_new_object::apply<T*>::type converter;
    
      // Transfer ownership to the Python handler and release ownership
      // from C++.
      python::handle<> handle(converter(*ptr));
      ptr.release();
    
      return python::object(handle);
    }
    
    // API wrapper.
    boost::python::tuple my_object_compute(
      my_object& self, container& x0, double t0)
    {
      auto x1 = std::make_unique<container>();
      double t1 = 21;
      int stat = self.evaluate(x0, t0, *x1, t1);
      return boost::python::make_tuple(stat, t1, transfer_to_python(x1.release()));
    }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
    
      python::class_<container>("Container")
        .def(python::init<python::object>())
        ;
    
      python::class_<my_object>("MyObject")
        .def("compute", &my_object_compute)
        ;
    }
    

    互动使用:

    >>> import example
    >>> my_object = example.MyObject()
    >>> status, t, x = my_object.compute(example.Container([1, 2, 3]), 4)
    >>> assert(status == 42)
    >>> assert(t == 21)
    >>> assert(isinstance(x, example.Container))
    

    【讨论】:

      猜你喜欢
      • 2020-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-16
      • 2010-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多