Skip to content

Commit baf6b35

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).
1 parent d2da33a commit baf6b35

File tree

9 files changed

+671
-277
lines changed

9 files changed

+671
-277
lines changed

docs/advanced/classes.rst

+16-24
Original file line numberDiff line numberDiff line change
@@ -631,27 +631,19 @@ interspersed with alias types and holder types (discussed earlier in this
631631
document)---pybind11 will automatically find out which is which. The only
632632
requirement is that the first template argument is the type to be declared.
633633

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

include/pybind11/attr.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -209,17 +209,17 @@ struct type_record {
209209
/// How large is the underlying C++ type?
210210
size_t type_size = 0;
211211

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

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

218218
/// Function pointer to class_<..>::init_holder
219-
void (*init_holder)(PyObject *, const void *) = nullptr;
219+
void (*init_holder)(instance *, const void *) = nullptr;
220220

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

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

0 commit comments

Comments
 (0)