File tree Expand file tree Collapse file tree 3 files changed +36
-8
lines changed Expand file tree Collapse file tree 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``:
13841384 auto *type = &heap_type->ht_type;
13851385 type->tp_flags |= Py_TPFLAGS_HAVE_GC;
13861386 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+ }
13891395 return 0;
13901396 };
13911397 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+ }
13941402 return 0;
13951403 };
13961404 }));
Original file line number Diff line number Diff line change @@ -74,5 +74,17 @@ struct value_and_holder {
7474 }
7575};
7676
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+
7789PYBIND11_NAMESPACE_END (detail)
7890PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
Original file line number Diff line number Diff line change @@ -26,13 +26,21 @@ TEST_SUBMODULE(custom_type_setup, m) {
2626 auto *type = &heap_type->ht_type ;
2727 type->tp_flags |= Py_TPFLAGS_HAVE_GC;
2828 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+ }
3137 return 0 ;
3238 };
3339 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+ }
3644 return 0 ;
3745 };
3846 }));
You can’t perform that action at this time.
0 commit comments