如何将基本类型的shared_ptr导出到python

0 投票
2 回答
763 浏览
提问于 2025-04-18 09:19

我正在尝试在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 个回答

1

你真的需要这样做吗?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 函数。

2

可以使用 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)

撰写回答