我有一个C ++ myObject类,我使用包装器结构通过boost python公开:
[
{
"foo":"Bar",
"b": [
{
"c":{
"foo1":"Bar1",
"foo2":"Bar2",
"foo3":"Bar3"
},
"d":{
"foo1":"Bar1",
"foo2":"Bar2",
"foo3":"Bar3"
}
},
{
"c":{
"foo1":"Bar1",
"foo2":"Bar2",
"foo3":"Bar3"
},
"d":{
"foo1":"Bar1",
"foo2":"Bar2",
"foo3":"Bar3"
}
}
// so on ...
]
}
]
Container目前定义为:
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)
;
}
并且暴露给python。
现在我可以在python中使用。
typedef std::valarray<double> Container
这不是非常pythonic。我更愿意这样做:
x = Container()
(status,t) = obj.compute(Container([0.,0.,0.]),0.0,x)
print status, t, x[0]
我可以在python中编写一个额外的包装器,但我宁愿避免添加更多的包装器。
以下代码不会编译:
(status,t,x) = obj.compute(Container([0.,0.,0.]),0.0)
print status, t, x[0]
另外我更喜欢窃取局部变量x的内容并让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);
}
}
我如何实现这一目标?
答案 0 :(得分:3)
简而言之,在免费商店中分配包装器并使用manage_new_object
结果转换将所有权转移到Python对象。这将导致Boost.Python在构造Python对象时复制指针,而不是复制指针。有关详细信息,请参阅this answer。
这是一个将所有权转移到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))