如何将基本类型的shared_ptr导出到python
我正在尝试在Python中使用一个基本类型的共享指针,比如int或double,但我不知道怎么把它导出到Python中:
我有一个这样的类:
class Holder
{
public:
Holder(int v) : value(new int(v)) {};
boost::shared_ptr<int> value;
};
这个类是这样导出的:
class_<Holder>("Holder", init<int>())
.def_readwrite("value", &Holder::value);
在Python代码中,我想用一个已经存在的实例来设置“holder实例.value”。
h1 = mk.Holder(10)
h2 = mk.Holder(20)
h1.value = h2.value
接下来发生了以下情况:
TypeError: No to_python (by-value) converter found for C++ type: class boost::shared_ptr<int>
我的问题是:我该如何把boost::shared_ptr<int>
导出到Python中?
2 个回答
你真的需要这样做吗?Python 自己有一个引用计数的机制,可能直接用这个会更简单一些。不过,这也要看你在 C++ 这边做了什么。
如果不这样的话,你可能需要定义一个 Python 对象来包含共享指针。这其实比较简单:只需要定义一个类似于:
struct PythonWrapper
{
PyObject_HEAD
boost::shared_ptr<int> value;
// Constructors and destructors can go here, to manage
// value.
};
然后像管理其他对象一样来声明和管理它;只要确保在创建这种类型的对象时使用 new
,而且这些对象必须在你提供给 Python 的函数中创建,至少要在 tp_new
字段的函数中创建。同时,在你注册的类型对象的 tp_dealloc
字段中放一个 delete
函数。
可以使用 boost::python::class_
来将 boost::shared_ptr<int>
导出到 Python,就像导出其他类型一样:
boost::python::class_<boost::shared_ptr<int> >(...);
不过,在暴露 shared_ptr
时要小心它带来的语义变化。比如,想象一下,当把一个 Holder.value
赋值给另一个 Holder.value
时,这实际上只是调用了 boost::shared_ptr<int>
的赋值操作符,而 Holder.increment()
是在操作 value
指向的那个 int
,而不是让 value
指向一个新的 int
:
h1 = Holder(10)
h2 = Holder(20)
h1.value = h2.value # h1.value and h2.value point to the same int.
h1.increment() # Change observed in h2.value. The semantics may be unexpected
# by Python developers.
下面是一个完整的例子,展示了如何根据原始代码来暴露 boost::shared_ptr<int>
:
#include <sstream>
#include <string>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
class Holder
{
public:
Holder(int v)
: value(new int(v))
{};
boost::shared_ptr<int> value;
};
std::string holder_value_str(const boost::shared_ptr<int>& value)
{
std::stringstream stream;
stream << value.get() << " contains " << *value;
return stream.str();
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
{
python::scope holder = python::class_<Holder>(
"Holder", python::init<int>())
.def_readwrite("value", &Holder::value)
;
// Holder.Value
python::class_<boost::shared_ptr<int> >("Value", python::no_init)
.def("__str__", &holder_value_str)
;
}
}
交互使用:
>>> import example
>>> h1 = example.Holder(10)
>>> h2 = example.Holder(20)
>>> print h1.value
0x25f4bd0 contains 10
>>> print h2.value
0x253f220 contains 20
>>> h1.value = h2.value
>>> print h1.value
0x253f220 contains 20
另外,如果把 shared_ptr
看作是 C++ 的内存管理代理,那么可以合理地将 Holder.value
在 Python 中暴露为一个 int
,即使 Holder::value
实际上是一个 boost::shared_ptr<int>
。这样可以让 Python 开发者获得预期的语义,并允许像 h1.value = h2.value + 5
这样的语句。下面是一个例子,使用辅助函数将 Holder::value
转换为 int
,而不是直接暴露 boost::shared_ptr<int>
类型:
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
class Holder
{
public:
Holder(int v)
: value(new int(v))
{};
boost::shared_ptr<int> value;
};
/// @brief Auxiliary function used to get Holder.value.
int get_holder_value(const Holder& self)
{
return *(self.value);
}
/// @brief Auxiliary function used to set Holder.value.
void set_holder_value(Holder& self, int value)
{
*(self.value) = value;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::scope holder = python::class_<Holder>(
"Holder", python::init<int>())
.add_property("value",
python::make_function(&get_holder_value),
python::make_function(&set_holder_value))
;
}
交互使用:
>>> import example
>>> h1 = example.Holder(10)
>>> h2 = example.Holder(20)
>>> assert(h1.value == 10)
>>> assert(h2.value == 20)
>>> h1.value = h2.value
>>> assert(h1.value == 20)
>>> h1.value += 22
>>> assert(h1.value == 42)
>>> assert(h2.value == 20)