Skip to content

Commit

Permalink
inherit GC traverse/clear callbacks from base object
Browse files Browse the repository at this point in the history
  • Loading branch information
wjakob committed Oct 27, 2022
1 parent 38ba18a commit 42ce5e7
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 20 deletions.
17 changes: 8 additions & 9 deletions docs/typeslots_and_gc.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ nb::class_<MyClass>(m, "MyClass", nb::type_slots(slots));
Here, ``slots`` should refer to an array of function pointers
that are tagged with a corresponding slot identifier. For example,
here is a table and example function that overrides the addition
operator so that it behaves like a multiplication.
operator so that it behaves like a multiplication.


```cpp
Expand All @@ -30,15 +30,14 @@ PyType_Slot slots[] = {
```
The last entry of the ``slots`` table must equal ``{ 0, nullptr }`` and
indicates the end of this arbitrary-length data structure.
This is a somewhat silly example, because this could have done more easily
using builtin _nanobind_ features. A more interesting example is given below.
More information on type slots can be found in the CPython documentation of
[type objects](https://docs.python.org/3/c-api/typeobj.html) and [type
indicates the end of this arbitrary-length data structure. Information on type
slots can be found in the CPython documentation of [type
objects](https://docs.python.org/3/c-api/typeobj.html) and [type
construction](https://docs.python.org/3/c-api/type.html).
This example is somewhat silly because it could have easily been accomplished
using builtin _nanobind_ features. The next section introduces a more
interesting example.
## Integrating C++ class bindings with Python's cyclic garbage collector
Expand Down Expand Up @@ -153,7 +152,7 @@ struct Wrapper {
};
```

This can also be used to create a reference cycle! For example,
This can also be used to create a reference cycle! For example,
in Python we could write

```python
Expand Down
33 changes: 22 additions & 11 deletions src/nb_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ PyObject *nb_type_new(const type_data *t) noexcept {
char *name_copy = NB_STRDUP(name.c_str());

constexpr size_t nb_enum_max_slots = 21,
nb_type_max_slots = 8,
nb_type_max_slots = 10,
nb_extra_slots = 80,
nb_total_slots = nb_enum_max_slots +
nb_type_max_slots +
Expand Down Expand Up @@ -414,17 +414,24 @@ PyObject *nb_type_new(const type_data *t) noexcept {
nb_enum_prepare(&s, is_arithmetic);

for (PyType_Slot *ts = slots; ts != s; ++ts) {
if (ts->slot == Py_tp_traverse ||
ts->slot == Py_tp_clear)
if (ts->slot == Py_tp_traverse)
spec.flags |= Py_TPFLAGS_HAVE_GC;
}

if (has_dynamic_attr) {
if (spec.flags & Py_TPFLAGS_HAVE_GC)
fail("nanobind::detail::nb_type_new(\"%s\"): internal error -- "
"attempted to enable dynamic attributes in a type with its "
"own garbage collection hooks!", t->name);
/// Inherit GC-related slots from parent class if none were specified
if (base && (spec.flags & Py_TPFLAGS_HAVE_GC) == 0) {
void *tp_traverse = PyType_GetSlot((PyTypeObject *) base, Py_tp_traverse),
*tp_clear = PyType_GetSlot((PyTypeObject *) base, Py_tp_clear);

if (tp_traverse) {
*s++ = { Py_tp_traverse, tp_traverse };
if (tp_clear)
*s++ = { Py_tp_clear, tp_clear };
spec.flags |= Py_TPFLAGS_HAVE_GC;
}
}

if (has_dynamic_attr) {
// realign to sizeof(void*), add one pointer
basicsize = (basicsize + ptr_size - 1) / ptr_size * ptr_size;
basicsize += ptr_size;
Expand All @@ -433,11 +440,15 @@ PyObject *nb_type_new(const type_data *t) noexcept {
(Py_ssize_t) (basicsize - ptr_size), READONLY,
nullptr };
*s++ = { Py_tp_members, (void *) members };
*s++ = { Py_tp_traverse, (void *) inst_traverse };
*s++ = { Py_tp_clear, (void *) inst_clear };

// Install GC traverse and clear routines if not inherited/overridden
if ((spec.flags & Py_TPFLAGS_HAVE_GC) == 0) {
*s++ = { Py_tp_traverse, (void *) inst_traverse };
*s++ = { Py_tp_clear, (void *) inst_clear };
spec.flags |= Py_TPFLAGS_HAVE_GC;
}

spec.basicsize = (int) basicsize;
spec.flags |= Py_TPFLAGS_HAVE_GC;
}

*s++ = { 0, nullptr };
Expand Down

0 comments on commit 42ce5e7

Please sign in to comment.