简体   繁体   English

Boost.Python:如何公开std :: unique_ptr

[英]Boost.Python: How to expose std::unique_ptr

I am fairly new to boost.python and trying to expose the return value of a function to python. 我对boost.python相当新,并尝试将函数的返回值公开给python。

The function signature looks like this: 函数签名如下所示:

 std::unique_ptr<Message> someFunc(const std::string &str) const;

When calling the function in python, I get the following error: 在python中调用函数时,我收到以下错误:

TypeError: No to_python (by-value) converter found for C++ type: std::unique_ptr<Message, std::default_delete<Message> >

My function call in python looks like this: 我在python中的函数调用如下所示:

a = mymodule.MyClass()
a.someFunc("some string here") # error here

I tried to expose the std::unique_ptr but just cant get it to work.. Does someone know how to properly expose the pointer class? 我试图暴露std :: unique_ptr但只是无法让它工作..有人知道如何正确暴露指针类? Thanks! 谢谢!

Edit: I tried the following: 编辑:我尝试了以下内容:

class_<std::unique_ptr<Message, std::default_delete<Message>>, bost::noncopyable ("Message", init<>())

;

This example compiles, but I still get the error mentioned above. 这个例子编译,但我仍然得到上面提到的错误。 Also, I tried to expose the class Message itself 此外,我试图公开类Message本身

class_<Message>("Message", init<unsigned>())

        .def(init<unsigned, unsigned>()) 
        .def("f", &Message::f)
;

In short, Boost.Python does not support move-semantics, and therefore does not support std::unique_ptr . 简而言之,Boost.Python不支持move-semantics,因此不支持std::unique_ptr Boost.Python's news/change log has no indication that it has been updated for C++11 move-semantics. Boost.Python的新闻/更改日志没有任何迹象表明它已针对C ++ 11移动语义进行了更新。 Additionally, this feature request for unique_ptr support has not been touched for over a year. 此外,针对unique_ptr支持的此功能请求已超过一年未被触及。

Nevertheless, Boost.Python supports transferring exclusive ownership of an object to and from Python via std::auto_ptr . 然而,Boost.Python支持通过std::auto_ptr将对象的独占所有权转移到Python或从Python转移。 As unique_ptr is essentially a safer version of auto_ptr , it should be fairly straight forward to adapt an API using unique_ptr to an API that uses auto_ptr : 由于unique_ptr本质上是一个更安全的auto_ptr版本,因此使用unique_ptr将API调整为使用auto_ptr的API应该是相当直接的:

  • When C++ transfers ownership to Python, the C++ function must: 当C ++将所有权转移到Python时,C ++函数必须:
  • When Python transfers ownership to C++, the C++ function must: 当Python将所有权转移到C ++时,C ++函数必须:
    • accept the instance via auto_ptr . 通过auto_ptr接受实例。 The FAQ mentions that pointers returned from C++ with a manage_new_object policy will be managed via std::auto_ptr . FAQ提到使用manage_new_object策略从C ++返回的指针将通过std::auto_ptr进行管理。
    • have auto_ptr release control to a unique_ptr via release() 通过release()auto_ptr释放控制权发送到unique_ptr

Given an API/library that cannot be changed: 给定一个无法更改的API /库:

/// @brief Mockup Spam class.
struct Spam;

/// @brief Mockup factory for Spam.
struct SpamFactory
{
  /// @brief Create Spam instances.
  std::unique_ptr<Spam> make(const std::string&);

  /// @brief Delete Spam instances.
  void consume(std::unique_ptr<Spam>);
};

The SpamFactory::make() and SpamFactory::consume() need to be wrapped via auxiliary functions. 需要通过辅助函数包装SpamFactory::make()SpamFactory::consume()

Functions transferring ownership from C++ to Python can be generically wrapped by a function that will create Python function objects: 将所有权从C ++转移到Python的功能通常可以由创建Python函数对象的函数包装:

/// @brief Adapter a member function that returns a unique_ptr to
///        a python function object that returns a raw pointer but
///        explicitly passes ownership to Python.
template <typename T,
          typename C,
          typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...))
{
  return boost::python::make_function(
      [fn](C& self, Args... args) { return (self.*fn)(args...).release(); },
      boost::python::return_value_policy<boost::python::manage_new_object>(),
      boost::mpl::vector<T*, C&, Args...>()
    );
}

The lambda delegates to the original function, and releases() ownership of the instance to Python, and the call policy indicates that Python will take ownership of the value returned from the lambda. lambda委托给原始函数,并将实例的所有权releases()给Python,调用策略表明Python将取得lambda返回的值的所有权。 The mpl::vector describes the call signature to Boost.Python, allowing it to properly manage function dispatching between the languages. mpl::vector描述了Boost.Python的调用签名,允许它正确地管理语言之间的函数调度。

The result of adapt_unique is exposed as SpamFactory.make() : adapt_unique的结果公开为SpamFactory.make()

boost::python::class_<SpamFactory>(...)
  .def("make", adapt_unique(&SpamFactory::make))
  // ...
  ;

Generically adapting SpamFactory::consume() is a more difficult, but it is easy enough to write a simple auxiliary function: 通常适应SpamFactory::consume()是一件比较困难的事情,但编写简单的辅助函数很容易:

/// @brief Wrapper function for SpamFactory::consume_spam().  This
///        is required because Boost.Python will pass a handle to the
///        Spam instance as an auto_ptr that needs to be converted to
///        convert to a unique_ptr.
void SpamFactory_consume(
  SpamFactory& self,
  std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python.
{
  return self.consume(std::unique_ptr<Spam>{ptr.release()});
}

The auxiliary function delegates to the original function, and converts the auto_ptr provided by Boost.Python to the unique_ptr required by the API. 辅助函数委托给原始函数,并将Boost.Python提供的auto_ptr转换为API所需的unique_ptr The SpamFactory_consume auxiliary function is exposed as SpamFactory.consume() : SpamFactory_consume辅助函数公开为SpamFactory.consume()

boost::python::class_<SpamFactory>(...)
  // ...
 .def("consume", &SpamFactory_consume)
 ;

Here is a complete code example: 这是一个完整的代码示例:

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

/// @brief Mockup Spam class.
struct Spam
{
  Spam(std::size_t x) : x(x) { std::cout << "Spam()" << std::endl; }
  ~Spam() { std::cout << "~Spam()" << std::endl; }
  Spam(const Spam&) = delete;
  Spam& operator=(const Spam&) = delete;
  std::size_t x;
};

/// @brief Mockup factor for Spam.
struct SpamFactory
{
  /// @brief Create Spam instances.
  std::unique_ptr<Spam> make(const std::string& str)
  {
    return std::unique_ptr<Spam>{new Spam{str.size()}};
  }

  /// @brief Delete Spam instances.
  void consume(std::unique_ptr<Spam>) {}
};

/// @brief Adapter a non-member function that returns a unique_ptr to
///        a python function object that returns a raw pointer but
///        explicitly passes ownership to Python.
template <typename T,
          typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (*fn)(Args...))
{
  return boost::python::make_function(
      [fn](Args... args) { return fn(args...).release(); },
      boost::python::return_value_policy<boost::python::manage_new_object>(),
      boost::mpl::vector<T*, Args...>()
    );
}

/// @brief Adapter a member function that returns a unique_ptr to
///        a python function object that returns a raw pointer but
///        explicitly passes ownership to Python.
template <typename T,
          typename C,
          typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...))
{
  return boost::python::make_function(
      [fn](C& self, Args... args) { return (self.*fn)(args...).release(); },
      boost::python::return_value_policy<boost::python::manage_new_object>(),
      boost::mpl::vector<T*, C&, Args...>()
    );
}

/// @brief Wrapper function for SpamFactory::consume().  This
///        is required because Boost.Python will pass a handle to the
///        Spam instance as an auto_ptr that needs to be converted to
///        convert to a unique_ptr.
void SpamFactory_consume(
  SpamFactory& self,
  std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python.
{
  return self.consume(std::unique_ptr<Spam>{ptr.release()});
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<Spam, boost::noncopyable>(
      "Spam", python::init<std::size_t>())
    .def_readwrite("x", &Spam::x)
    ;

  python::class_<SpamFactory>("SpamFactory", python::init<>())
    .def("make", adapt_unique(&SpamFactory::make))
    .def("consume", &SpamFactory_consume)
    ;
}

Interactive Python: 交互式Python:

>>> import example
>>> factory = example.SpamFactory()
>>> spam = factory.make("a" * 21)
Spam()
>>> spam.x
21
>>> spam.x *= 2
>>> spam.x
42
>>> factory.consume(spam)
~Spam()
>>> spam.x = 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    None.None(Spam, int)
did not match C++ signature:
    None(Spam {lvalue}, unsigned int)

My suggestion is to get the raw pointer from the std::unique_ptr container with get() . 我的建议是使用get()std::unique_ptr容器中get()原始指针。 You will have to careful to keep the unique_ptr in scope for for whole time that you wish to use the raw pointer value, otherwise the object will be deleted and you'll have a pointer to an invalid area of memory. 您将不得不小心地将unique_ptr保留在您希望使用原始指针值的整个时间范围内,否则该对象将被删除,并且您将拥有指向无效内存区域的指针。

Boost supports movable semantics and unique_ptr since v.1.55 . 从v.1.55开始, Boost支持movable semanticsunique_ptr But in my project I used previous version and made such simple wrapper: 但在我的项目中,我使用了以前的版本并制作了这样简单的包装器:

class_<unique_ptr<HierarchyT>, noncopyable>(typpedName<LinksT>("hierarchy", false)
, "hierarchy holder")
    .def("__call__", &unique_ptr<HierarchyT>::get,
        return_internal_reference<>(),
        "get holding hierarchy")
    .def("reset", &unique_ptr<HierarchyT>::reset,
        "reset holding hierarhy")
    ;

to create unique_ptr<HierarchyT> as Python shierarchy and pass it to the function that accepts it by reference. 创建unique_ptr<HierarchyT>像Python shierarchy并将它传递给通过参考接受它的功能。
Python code: Python代码:

hier = mc.shierarchy()
mc.clusterize(hier, nds)

where C++ function is float clusterize(unique_ptr<HierarchyT>& hier,...) . 其中C ++函数是float clusterize(unique_ptr<HierarchyT>& hier,...)
Then to access results in Python make a call hier() to get the wrapped object from the unique_ptr: 然后在Python中访问结果调用hier()从unique_ptr获取包装对象:

output(hier(), nds)

I think nowadays there is no way to do what you are looking for... The reason is because std::unique_ptr<Message> someFunc(const std::string &str) is returning by value, which means one of two things: 我认为现在没有办法做你想要的...原因是因为std::unique_ptr<Message> someFunc(const std::string &str)按值返回,这意味着以下两点之一:

  1. The return value is going to be copied (but unique_ptr is not copyable ); 将复制返回值(但unique_ptr不可复制 );
  2. The return value is going to be moved (now the problem is that boost::python doesn't provide support to move semantics). 返回值将被移动(现在问题是boost :: python不提供移动语义的支持)。 (heyy, I'm using boost 1,53, not sure in the newest versions); (嘿,我正在使用boost 1,53,在最新版本中不确定);

Is someFunc() creating the object? someFunc()是否创建了对象? In case YES, I think the solution is to create a wrapper, in case NO, you can return by reference: 如果是,我认为解决方案是创建一个包装器,如果没有,你可以通过引用返回:

std::unique_ptr<Message>& someFunc(const std::string &str)

expose the class: 公开课:

class_<std::unique_ptr<Message, std::default_delete<Message>>, boost::noncopyable>("unique_ptr_message")
    .def("get", &std::unique_ptr<Message>::get, return_value_policy<reference_existing_object>())
;

and also the functions: 以及功能:

def("someFunc", someFunc,  return_value_policy<reference_existing_object>());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用带有boost python的unique_ptr-boost :: shared_ptr可以工作,但是unique_ptr不能 - using unique_ptr with boost python - boost::shared_ptr works but unique_ptr doesnt 带有静态工厂构造函数和std :: unique_ptr的boost :: python纯虚拟基类 - boost::python pure virtual base class with static factory constructor and std::unique_ptr 如何在boost.python中转换std :: string *? - How to converter std::string* in boost.python? 暴露std :: vector <struct> 用boost.python - Exposing std::vector<struct> with boost.python 暴露std :: vector <double> 使用boost.python - exposing std::vector<double> with boost.python C++ 和 Boost.Python - 如何将变量暴露给 python 并在循环中更新它? - C++ and Boost.Python - how to expose variable to python and update it in loop? 在 boost.python 中; 如何公开包含在另一个类中的类(通过组合)? - In boost.python; how do I expose a class that is contained in another class (via composition)? Boost.Python包装原始指针但公开方法 - Boost.Python wrap raw pointer but expose methods boost.python公开返回vector的函数<MyClass> - boost.python expose function that returns vector<MyClass> 如何使用带有 Boost.Python 的 std::map 或 std::vector 参数的构造函数包装 C++ 类? - How to wrap a C++ class with a constructor that takes a std::map or std::vector argument with Boost.Python?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM