我有一个虚拟对象,我使用pybind11在Python子类化和实现,然后在C++代码中使用该对象,但是当python脚本结束时,python对象超出了范围,python垃圾收集器在我可以在C++端使用它之前销毁了我的对象。
为了避免销毁,我在绑定代码中添加了一个"keep“方法来增加ref_count,这样python垃圾收集器就不会销毁它了。
绑定的C++代码
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代码
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方法感觉像是一个可怕的黑客攻击。实现这一点的正确方法是什么?
发布于 2020-11-13 06:31:11
我不认为弄乱裁判数量是最好的解决方案。如果你想在C++中使用一个对象,使用C++生命周期管理工具。
Pybind将生命周期管理集成到语言绑定中,您只需更改它的行为。引用docs
类的绑定生成器
class_可以传递一个模板类型,该模板类型表示用于管理对对象的引用的特殊持有者类型。如果没有给出这样的持有者类型模板参数,则名为std::unique_ptr<Type>的类型的默认值为Python,这意味着当Python的引用计数变为零时,对象将被释放。
可以切换到其他类型的引用计数包装器或智能指针,这在依赖它们的代码库中很有用。例如,下面的代码片段导致使用std::shared_ptr。
下面是一个从tests中摘录的示例
// 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窃取数据
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);
}from example import Parent
print(Parent().get_child()) // UB or Segfault虽然这些问题在我看来并不是unique_ptr上的shared_ptr所独有的。
https://stackoverflow.com/questions/64654945
复制相似问题