首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >增加C++中pybind11句柄的ref_count

增加C++中pybind11句柄的ref_count
EN

Stack Overflow用户
提问于 2020-11-03 07:56:13
回答 1查看 325关注 0票数 2

我有一个虚拟对象,我使用pybind11在Python子类化和实现,然后在C++代码中使用该对象,但是当python脚本结束时,python对象超出了范围,python垃圾收集器在我可以在C++端使用它之前销毁了我的对象。

为了避免销毁,我在绑定代码中添加了一个"keep“方法来增加ref_count,这样python垃圾收集器就不会销毁它了。

绑定的C++代码

代码语言:javascript
复制
VNCS::Real VNCS::py::BlendingField::blending(const VNCS::Point_3 &p) const
{
    std::array<double, 3> pyP{p[0], p[1], p[2]};
    PYBIND11_OVERLOAD_PURE(VNCS::Real,          /* Return type */
                           VNCS::BlendingField, /* Parent class */
                           blending,            /* Name of function in C++ (must match Python name) */
                           pyP                  /* Argument(s) */
    );
}

void VNCS::py::module::blendingField(pybind11::module &m)
{
    pybind11::class_<VNCS::BlendingField, VNCS::py::BlendingField, std::shared_ptr<VNCS::BlendingField>>(
        m, "BlendingField")
        .def(pybind11::init(
            [](pybind11::args &args, pybind11::kwargs &kwargs) { return std::make_shared<VNCS::py::BlendingField>(); }))
        .def("keep", [](pybind11::handle handle) { handle.inc_ref(); });
}

Python代码

代码语言:javascript
复制
class BlendingField(PyVNCS.BlendingField):
   def __init__(self, *args, **kwargs):
      PyVNCS.BlendingField.__init__(self, *args, **kwargs)

   def __del__(self):
       print("dtor from python")

   def blending(self, point):
       return point[0]

blendingField = BlendingField()
blendingField.keep()
simCreator = initObject.addObject("SimCreator",
                     name="simCreator",
                     coarseMesh="@lowResTopology",
                     detailMesh="@highResTopology")
simCreator.setBlendingField(blendingField)

这看起来不是很好,因为keep方法感觉像是一个可怕的黑客攻击。实现这一点的正确方法是什么?

EN

回答 1

Stack Overflow用户

发布于 2020-11-13 06:31:11

我不认为弄乱裁判数量是最好的解决方案。如果你想在C++中使用一个对象,使用C++生命周期管理工具。

Pybind将生命周期管理集成到语言绑定中,您只需更改它的行为。引用docs

类的绑定生成器class_可以传递一个模板类型,该模板类型表示用于管理对对象的引用的特殊持有者类型。如果没有给出这样的持有者类型模板参数,则名为std::unique_ptr<Type>的类型的默认值为Python,这意味着当Python的引用计数变为零时,对象将被释放。

可以切换到其他类型的引用计数包装器或智能指针,这在依赖它们的代码库中很有用。例如,下面的代码片段导致使用std::shared_ptr

下面是一个从tests中摘录的示例

代码语言:javascript
复制
// Object managed by a std::shared_ptr<>
class MyObject2 {
public:
    MyObject2(const MyObject2 &) = default;
    MyObject2(int value) : value(value) { print_created(this, toString()); }
    std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; }
    virtual ~MyObject2() { print_destroyed(this); }
private:
    int value;
};
py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2")
    .def(py::init<int>());
m.def("make_myobject2_1", []() { return new MyObject2(6); });
m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); });
m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); });
m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); });
m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); });
m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); });

如果你真的走了我推荐的路线,这个问题将是this one的复制品。

请注意,这里有一些小陷阱,特别是当您的代码库在python代码周围使用原始指针时。文档指出,如果从python调用返回指向共享对象的原始指针的C++方法,则可能存在双释放。再次从docs窃取数据

代码语言:javascript
复制
class Child { };

class Parent {
public:
   Parent() : child(std::make_shared<Child>()) { }
   Child *get_child() { return child.get(); }  /* Hint: ** DON'T DO THIS ** */
private:
    std::shared_ptr<Child> child;
};

PYBIND11_MODULE(example, m) {
    py::class_<Child, std::shared_ptr<Child>>(m, "Child");

    py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent")
       .def(py::init<>())
       .def("get_child", &Parent::get_child);
}
代码语言:javascript
复制
from example import Parent
print(Parent().get_child()) // UB or Segfault

虽然这些问题在我看来并不是unique_ptr上的shared_ptr所独有的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64654945

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档