使用boost::python将C++类实例传递给Python

14 投票
2 回答
5498 浏览
提问于 2025-04-16 01:53

我有一个库,它可以创建对象(也就是类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的部分。我没有粘贴那部分代码,但可以说我使用了回调机制来实现这个目的。

这段代码可以正常工作,但我有几个问题:

  1. 在Count函数(以及其他所有C++操作函数)中,像这样传递a可以吗?还是说更好用const shared_ptr<A>&?在我看到的Python Boost文档中的代码片段里,通常会使用引用,但我不太明白这有什么区别(当然,引用计数更高是一个区别)。

  2. 这段代码“安全”吗?当我把existing_instance传给Python时,它的计数会增加(只增加一次,即使在Python中我复制了这个对象,当然),所以只要Python至少持有一个“副本”,C++代码就不可能销毁这个对象。我这样理解对吗?我尝试过使用指针,似乎我的理解是对的,我只是想确认一下。

  3. 我想防止Python创建A的实例。它们应该只从C++代码中传递过来。我该怎么做?编辑:找到了,我只需要使用no_init和noncopyable:class_<A, boost::noncopyable>("A", no_init)

2 个回答

1

在这种情况下,我的代码会像这样(针对你的例子):

...

BOOST_PYTHON_MODULE(libp)
{
    class_<A, boost::shared_ptr<A>, boost::noncopyable >("A")
       .def("f", &A::f)
       .def("Count", &Count)
     ;
 } 

重要的是要禁止boost::python进行复制,但如果你使用的是shared_ptr,那么你可能只在一些特定的情况下需要复制。

14

boost::pythonboost::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(...))。)

撰写回答