使用boost::python将C++类实例传递给Python
我有一个库,它可以创建对象(也就是类A的实例),然后把这些对象传给一个Python程序,这个程序应该能够调用它们的方法。
简单来说,我有C++的类实例,想在Python中使用它们。有时候,这些对象还需要传回C++进行一些操作。
我创建了一个包装文件(假设New
函数在C++代码的某个地方被调用):
#include <boost/python.hpp>
#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace boost;
using namespace boost::python;
int calls = 0;
struct A
{
int f() { return calls++; }
~A() { std::cout << "destroyed\n"; }
};
shared_ptr<A> existing_instance;
void New() { existing_instance = shared_ptr<A>( new A() ); }
int Count( shared_ptr<A> a ) { return a.use_count(); }
BOOST_PYTHON_MODULE(libp)
{
class_<A>("A")
.def("f", &A::f)
;
def("Count", &Count);
register_ptr_to_python< shared_ptr<A> >();
}
这段代码缺少了Python如何获取existing_instance
的部分。我没有粘贴那部分代码,但可以说我使用了回调机制来实现这个目的。
这段代码可以正常工作,但我有几个问题:
在Count函数(以及其他所有C++操作函数)中,像这样传递
a
可以吗?还是说更好用const shared_ptr<A>&
?在我看到的Python Boost文档中的代码片段里,通常会使用引用,但我不太明白这有什么区别(当然,引用计数更高是一个区别)。这段代码“安全”吗?当我把
existing_instance
传给Python时,它的计数会增加(只增加一次,即使在Python中我复制了这个对象,当然),所以只要Python至少持有一个“副本”,C++代码就不可能销毁这个对象。我这样理解对吗?我尝试过使用指针,似乎我的理解是对的,我只是想确认一下。我想防止Python创建A的实例。它们应该只从C++代码中传递过来。我该怎么做?编辑:找到了,我只需要使用no_init和noncopyable:
class_<A, boost::noncopyable>("A", no_init)
2 个回答
在这种情况下,我的代码会像这样(针对你的例子):
...
BOOST_PYTHON_MODULE(libp)
{
class_<A, boost::shared_ptr<A>, boost::noncopyable >("A")
.def("f", &A::f)
.def("Count", &Count)
;
}
重要的是要禁止boost::python进行复制,但如果你使用的是shared_ptr,那么你可能只在一些特定的情况下需要复制。
boost::python
对 boost::shared_ptr
非常了解,但你需要告诉它 boost::shared_ptr<A>
是用来保存一个 A
的实例。你可以通过在 class_
的模板参数列表中添加 boost::shared_ptr<A>
来做到这一点。关于这个“持有类型”的更多信息,可以在 boost 文档中找到。
为了防止从 Python 创建实例,你需要在 class_
的构造函数中添加 boost::python::no_init
,这样你就得到了:
boost::python::class_< A, boost::shared_ptr<A> >("A", boost::python::no_init)
//... .def, etc
;
一般来说,你不应该通过引用来传递共享指针,因为如果共享指针的引用失效了,那么它指向的对象的引用也会失效(因为引用共享指针并没有增加指向对象的引用计数)。
将 boost::shared_ptr
对象在 Python 和 C++ 之间传递是完全安全的,只要你不改变 return_value_policy
,引用计数(Python 和 shared_ptr)会被正确管理。如果你改变了在 Python 中暴露的方法的策略,使其返回一个共享指针的引用,那么就可能会出现问题,就像通过 C++ 引用传递共享指针一样。
(另外,你应该优先使用 make_shared<A>(...)
而不是 shared_ptr<A>(new A(...))
。)