[英]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应该是相当直接的:
boost::python::return_value_policy
with a boost::python::manage_new_object
result converter. boost::python::manage_new_object
结果转换器,使用boost::python::return_value_policy
CallPolicy进行公开。 unique_ptr
release control via release()
and return a raw pointer release()
获得unique_ptr
释放控制并返回一个原始指针 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
. manage_new_object
策略从C ++返回的指针将通过std::auto_ptr
进行管理。 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 semantics
和unique_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)
按值返回,这意味着以下两点之一:
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.