Skip to content

Commit 08556c3

Browse files
committed
Support multiple inheritance from python
This commit allows multiple inheritance of pybind11 classes from Python, e.g. class MyType(Base1, Base2): def __init__(self): Base1.__init__(self) Base2.__init__(self) where Base1 and Base2 are pybind11-exported classes. This requires collapsing the various builtin base objects (pybind11_object_56, ...) introduced in 2.1 into a single pybind11_object of a fixed size; this fixed size object allocates enough space to contain either a simple object (one base class & small* holder instance), or a pointer to a new allocation that can contain an arbitrary number of base classes and holders, with holder size unrestricted. * "small" here means having a sizeof() of at most 2 pointers, which is enough to fit unique_ptr (sizeof is 1 ptr) and shared_ptr (sizeof is 2 ptrs). To minimize the performance impact, this repurposes `internals::registered_types_py` to store a vector of pybind-registered base types. For direct-use pybind types (e.g. the `PyA` for a C++ `A`) this is simply storing the same thing as before, but now in a vector; for Python-side inherited types, the map lets us avoid having to do a base class traversal as long as we've seen the class before. The change to vector is needed for multiple inheritance: Python types inheriting from multiple registered bases have one entry per base.
1 parent caedf74 commit 08556c3

11 files changed

+833
-346
lines changed

docs/advanced/classes.rst

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -619,27 +619,19 @@ interspersed with alias types and holder types (discussed earlier in this
619619
document)---pybind11 will automatically find out which is which. The only
620620
requirement is that the first template argument is the type to be declared.
621621

622-
There are two caveats regarding the implementation of this feature:
623-
624-
1. When only one base type is specified for a C++ type that actually has
625-
multiple bases, pybind11 will assume that it does not participate in
626-
multiple inheritance, which can lead to undefined behavior. In such cases,
627-
add the tag ``multiple_inheritance``:
628-
629-
.. code-block:: cpp
630-
631-
py::class_<MyType, BaseType2>(m, "MyType", py::multiple_inheritance());
632-
633-
The tag is redundant and does not need to be specified when multiple base
634-
types are listed.
635-
636-
2. As was previously discussed in the section on :ref:`overriding_virtuals`, it
637-
is easy to create Python types that derive from C++ classes. It is even
638-
possible to make use of multiple inheritance to declare a Python class which
639-
has e.g. a C++ and a Python class as bases. However, any attempt to create a
640-
type that has *two or more* C++ classes in its hierarchy of base types will
641-
fail with a fatal error message: ``TypeError: multiple bases have instance
642-
lay-out conflict``. Core Python types that are implemented in C (e.g.
643-
``dict``, ``list``, ``Exception``, etc.) also fall under this combination
644-
and cannot be combined with C++ types bound using pybind11 via multiple
645-
inheritance.
622+
It is also permitted to inherit multiply from exported C++ classes in Python,
623+
as well as inheriting from multiple Python and/or pybind-exported classes.
624+
625+
There is one caveat regarding the implementation of this feature:
626+
627+
When only one base type is specified for a C++ type that actually has multiple
628+
bases, pybind11 will assume that it does not participate in multiple
629+
inheritance, which can lead to undefined behavior. In such cases, add the tag
630+
``multiple_inheritance`` to the class constructor:
631+
632+
.. code-block:: cpp
633+
634+
py::class_<MyType, BaseType2>(m, "MyType", py::multiple_inheritance());
635+
636+
The tag is redundant and does not need to be specified when multiple base types
637+
are listed.

include/pybind11/attr.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,17 +210,17 @@ struct type_record {
210210
/// How large is the underlying C++ type?
211211
size_t type_size = 0;
212212

213-
/// How large is pybind11::instance<type>?
214-
size_t instance_size = 0;
213+
/// How large is the type's holder?
214+
size_t holder_size = 0;
215215

216216
/// The global operator new can be overridden with a class-specific variant
217217
void *(*operator_new)(size_t) = ::operator new;
218218

219219
/// Function pointer to class_<..>::init_holder
220-
void (*init_holder)(PyObject *, const void *) = nullptr;
220+
void (*init_holder)(instance *, const void *) = nullptr;
221221

222222
/// Function pointer to class_<..>::dealloc
223-
void (*dealloc)(PyObject *) = nullptr;
223+
void (*dealloc)(const detail::value_and_holder &) = nullptr;
224224

225225
/// List of base classes of the newly created type
226226
list bases;

0 commit comments

Comments
 (0)