File tree 3 files changed +36
-8
lines changed
3 files changed +36
-8
lines changed Original file line number Diff line number Diff line change @@ -1384,13 +1384,21 @@ You can do that using ``py::custom_type_setup``:
1384
1384
auto *type = &heap_type->ht_type;
1385
1385
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
1386
1386
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
1387
- auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
1388
- Py_VISIT(self.value.ptr());
1387
+ // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
1388
+ #if PY_VERSION_HEX >= 0x03090000
1389
+ Py_VISIT(Py_TYPE(self_base));
1390
+ #endif
1391
+ if (py::detail::is_holder_constructed(self_base)) {
1392
+ auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
1393
+ Py_VISIT(self.value.ptr());
1394
+ }
1389
1395
return 0;
1390
1396
};
1391
1397
type->tp_clear = [](PyObject *self_base) {
1392
- auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
1393
- self.value = py::none();
1398
+ if (py::detail::is_holder_constructed(self_base)) {
1399
+ auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
1400
+ self.value = py::none();
1401
+ }
1394
1402
return 0;
1395
1403
};
1396
1404
}));
Original file line number Diff line number Diff line change @@ -74,5 +74,17 @@ struct value_and_holder {
74
74
}
75
75
};
76
76
77
+ // This is a semi-public API to check if the corresponding instance has been constructed with a
78
+ // holder. That is, if the instance has been constructed with a holder, the `__init__` method is
79
+ // called and the C++ object is valid. Otherwise, the C++ object might only be allocated, but not
80
+ // initialized. This will lead to **SEGMENTATION FAULTS** if the C++ object is used in any way.
81
+ // Example usage: https://pybind11.readthedocs.io/en/stable/advanced/classes.html#custom-type-setup
82
+ // for `tp_traverse` and `tp_clear` implementations.
83
+ // WARNING: The caller is responsible for ensuring that the `reinterpret_cast` is valid.
84
+ inline bool is_holder_constructed (PyObject *obj) {
85
+ auto *const instance = reinterpret_cast <pybind11::detail::instance *>(obj);
86
+ return instance->get_value_and_holder ().holder_constructed ();
87
+ }
88
+
77
89
PYBIND11_NAMESPACE_END (detail)
78
90
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
Original file line number Diff line number Diff line change @@ -26,13 +26,21 @@ TEST_SUBMODULE(custom_type_setup, m) {
26
26
auto *type = &heap_type->ht_type ;
27
27
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
28
28
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
29
- auto &self = py::cast<OwnsPythonObjects &>(py::handle (self_base));
30
- Py_VISIT (self.value .ptr ());
29
+ // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
30
+ #if PY_VERSION_HEX >= 0x03090000
31
+ Py_VISIT (Py_TYPE (self_base));
32
+ #endif
33
+ if (py::detail::is_holder_constructed (self_base)) {
34
+ auto &self = py::cast<OwnsPythonObjects &>(py::handle (self_base));
35
+ Py_VISIT (self.value .ptr ());
36
+ }
31
37
return 0 ;
32
38
};
33
39
type->tp_clear = [](PyObject *self_base) {
34
- auto &self = py::cast<OwnsPythonObjects &>(py::handle (self_base));
35
- self.value = py::none ();
40
+ if (py::detail::is_holder_constructed (self_base)) {
41
+ auto &self = py::cast<OwnsPythonObjects &>(py::handle (self_base));
42
+ self.value = py::none ();
43
+ }
36
44
return 0 ;
37
45
};
38
46
}));
You can’t perform that action at this time.
0 commit comments