Skip to content

Commit a567979

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.
1 parent dd01665 commit a567979

File tree

8 files changed

+348
-249
lines changed

8 files changed

+348
-249
lines changed

docs/advanced/classes.rst

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

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

include/pybind11/attr.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,6 @@ struct type_record {
171171
/// How large is the underlying C++ type?
172172
size_t type_size = 0;
173173

174-
/// How large is pybind11::instance<type>?
175-
size_t instance_size = 0;
176-
177174
/// Function pointer to class_<..>::init_holder
178175
void (*init_holder)(PyObject *, const void *) = nullptr;
179176

include/pybind11/cast.h

Lines changed: 133 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ NAMESPACE_BEGIN(pybind11)
2020
NAMESPACE_BEGIN(detail)
2121
inline PyTypeObject *make_static_property_type();
2222
inline PyTypeObject *make_default_metaclass();
23+
inline PyObject *make_object_base_type(PyTypeObject *metaclass);
2324

2425
/// Additional type information which does not fit into the PyTypeObject
2526
struct type_info {
@@ -78,20 +79,55 @@ PYBIND11_NOINLINE inline internals &get_internals() {
7879
);
7980
internals_ptr->static_property_type = make_static_property_type();
8081
internals_ptr->default_metaclass = make_default_metaclass();
82+
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
8183
}
8284
return *internals_ptr;
8385
}
8486

85-
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
87+
/** Takes a Python type and returns the pybind11 type_info for it (if it is a pybind11-registered
88+
* type). If it is not (i.e. it is a python type) this walks the type's inheritance tree to any
89+
* inherited pybind11-registered types, which are returned in a vector. (There can be multiple if
90+
* an involved python type inherits from multiple pybind11-registered types). */
91+
PYBIND11_NOINLINE inline std::vector<detail::type_info *> get_all_type_info(PyTypeObject *type) {
92+
std::vector<PyTypeObject *> check(1, type);
93+
std::vector<detail::type_info *> all_type_info;
8694
auto const &type_dict = get_internals().registered_types_py;
87-
do {
95+
for (size_t i = 0; i < check.size(); i++) {
96+
type = check[i];
97+
if (!PyType_Check((PyObject *) type)) continue;
8898
auto it = type_dict.find(type);
89-
if (it != type_dict.end())
90-
return (detail::type_info *) it->second;
91-
type = type->tp_base;
92-
if (!type)
93-
return nullptr;
94-
} while (true);
99+
if (it != type_dict.end()) {
100+
// Make sure we haven't already seen this type (so that we follow Python/virtual C++
101+
// rules that there should only be one instance of a common base).
102+
//
103+
// NB: Could use an set here, rather than a linear search, but since having a large
104+
// number of immediate pybind11-registered types seems fairly unlikely, that probably
105+
// isn't worthwhile).
106+
auto *tinfo = (detail::type_info *) it->second;
107+
bool found = false;
108+
for (auto &known : all_type_info) {
109+
if (known == tinfo) { found = true; break; }
110+
}
111+
if (!found) all_type_info.push_back(tinfo);
112+
} else if (type->tp_bases) {
113+
for (handle parent : reinterpret_borrow<tuple>(type->tp_bases))
114+
check.push_back((PyTypeObject *) parent.ptr());
115+
}
116+
}
117+
return all_type_info;
118+
}
119+
120+
/** Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any
121+
* ancestors are pybind11-registered. Throws an exception if there are multiple bases--get
122+
* `get_all_type_info` directly if you want to support multiple bases.
123+
*/
124+
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
125+
auto all = get_all_type_info(type);
126+
if (all.size() == 0)
127+
return nullptr;
128+
if (all.size() != 1)
129+
pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases");
130+
return all[0];
95131
}
96132

97133
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp,
@@ -114,6 +150,46 @@ PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool t
114150
return handle(type_info ? ((PyObject *) type_info->type) : nullptr);
115151
}
116152

153+
struct value_and_holder {
154+
void *&value, *&holder;
155+
value_and_holder(void *&v, void *&h) : value{v}, holder{h} {}
156+
template <typename V> enable_if_t<std::is_pointer<V>::value, V> value_as() const { return reinterpret_cast<V>(value); }
157+
template <typename H> enable_if_t<std::is_pointer<H>::value, H> holder_as() const { return reinterpret_cast<H>(holder); }
158+
};
159+
160+
/** Extracts C++ value and holder pointer references from an instance (which may contain multiple
161+
* values/holders for python-side multiple inheritance) that match the given type. Throws an error
162+
* if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance.
163+
*
164+
* Takes the instance pointer, a vector returned by `get_all_type_info()`, and the desired type.
165+
* See also the version below, which doesn't require the vector.
166+
*/
167+
PYBIND11_NOINLINE inline value_and_holder get_value_and_holder(
168+
instance *inst, const std::vector<type_info *> &all_type_info,
169+
const type_info *find_type) {
170+
size_t i = 0;
171+
for (auto tinfo : all_type_info) {
172+
if (tinfo == find_type) {
173+
return value_and_holder(inst->values_and_holders[i], inst->values_and_holders[i+1]);
174+
}
175+
i += 2;
176+
}
177+
#if defined(NDEBUG)
178+
pybind11_fail("pybind11::detail::get_value_and_holder: "
179+
"type is not a pybind11 base of the given instance "
180+
"(compile in debug mode for type details)");
181+
#else
182+
pybind11_fail("pybind11::detail::get_value_and_holder: `" +
183+
std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" +
184+
std::string(Py_TYPE(inst)->tp_name) + "' instance");
185+
#endif
186+
}
187+
188+
/// Shortcut for get_value_and_holder that gets the get_all_type_info vector when called.
189+
inline value_and_holder get_value_and_holder(instance *inst, const type_info *find_type) {
190+
return get_value_and_holder(inst, get_all_type_info(Py_TYPE(inst)), find_type);
191+
}
192+
117193
PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) {
118194
handle type = detail::get_type_handle(tp, false);
119195
if (!type)
@@ -203,51 +279,31 @@ class type_caster_generic {
203279
: typeinfo(get_type_info(type_info)) { }
204280

205281
PYBIND11_NOINLINE bool load(handle src, bool convert) {
206-
if (!src)
207-
return false;
208-
return load(src, convert, Py_TYPE(src.ptr()));
209-
}
210-
211-
bool load(handle src, bool convert, PyTypeObject *tobj) {
212282
if (!src || !typeinfo)
213283
return false;
214284
if (src.is_none()) {
215285
value = nullptr;
216286
return true;
217287
}
218288

219-
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
220-
/* Check if we can safely perform a reinterpret-style cast */
221-
if (PyType_IsSubtype(tobj, typeinfo->type)) {
222-
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
223-
return true;
224-
}
225-
} else { /* Case 2: multiple inheritance */
226-
/* Check if we can safely perform a reinterpret-style cast */
227-
if (tobj == typeinfo->type) {
228-
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
289+
PyTypeObject *pytype = Py_TYPE(src.ptr());
290+
// First check to see if we can safely perform a reinterpret-style cast: allowed if one of
291+
// the pybind bases of `src` (i.e. following non-pybind inheritance) is the desired type
292+
auto registered_bases = get_all_type_info(pytype);
293+
for (auto base : registered_bases) {
294+
if (base == typeinfo ||
295+
(typeinfo->simple_type && PyType_IsSubtype(base->type, typeinfo->type))) {
296+
value = get_value_and_holder(reinterpret_cast<instance *>(src.ptr()), registered_bases, base).value;
229297
return true;
230298
}
299+
}
231300

232-
/* If this is a python class, also check the parents recursively */
233-
auto const &type_dict = get_internals().registered_types_py;
234-
bool new_style_class = PyType_Check((PyObject *) tobj);
235-
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
236-
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
237-
for (handle parent : parents) {
238-
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
239-
if (result)
240-
return true;
241-
}
242-
}
243-
244-
/* Try implicit casts */
245-
for (auto &cast : typeinfo->implicit_casts) {
246-
type_caster_generic sub_caster(*cast.first);
247-
if (sub_caster.load(src, convert)) {
248-
value = cast.second(sub_caster.value);
249-
return true;
250-
}
301+
/* Try implicit casts */
302+
for (auto &cast : typeinfo->implicit_casts) {
303+
type_caster_generic sub_caster(*cast.first);
304+
if (sub_caster.load(src, convert)) {
305+
value = cast.second(sub_caster.value);
306+
return true;
251307
}
252308
}
253309

@@ -301,29 +357,34 @@ class type_caster_generic {
301357
return handle((PyObject *) it_i->second).inc_ref();
302358
}
303359

360+
// Set up the object manually (rather than calling new): we don't want value pointer
361+
// initialized because we want to set it to our `src` pointer below.
304362
auto inst = reinterpret_steal<object>(PyType_GenericAlloc(tinfo->type, 0));
363+
auto wrapper = (instance *) inst.ptr();
364+
// Since we are a registered type, we know there will only be one value/holder pair:
365+
wrapper->values_and_holders = (void **) ::operator new(2 * sizeof(void *));
366+
void *&valueptr = wrapper->values_and_holders[0];
367+
valueptr = nullptr;
368+
wrapper->values_and_holders[1] = nullptr;
305369

306-
auto wrapper = (instance<void> *) inst.ptr();
307-
308-
wrapper->value = nullptr;
309370
wrapper->owned = false;
310371

311372
switch (policy) {
312373
case return_value_policy::automatic:
313374
case return_value_policy::take_ownership:
314-
wrapper->value = src;
375+
valueptr = src;
315376
wrapper->owned = true;
316377
break;
317378

318379
case return_value_policy::automatic_reference:
319380
case return_value_policy::reference:
320-
wrapper->value = src;
381+
valueptr = src;
321382
wrapper->owned = false;
322383
break;
323384

324385
case return_value_policy::copy:
325386
if (copy_constructor)
326-
wrapper->value = copy_constructor(src);
387+
valueptr = copy_constructor(src);
327388
else
328389
throw cast_error("return_value_policy = copy, but the "
329390
"object is non-copyable!");
@@ -332,17 +393,17 @@ class type_caster_generic {
332393

333394
case return_value_policy::move:
334395
if (move_constructor)
335-
wrapper->value = move_constructor(src);
396+
valueptr = move_constructor(src);
336397
else if (copy_constructor)
337-
wrapper->value = copy_constructor(src);
398+
valueptr = copy_constructor(src);
338399
else
339400
throw cast_error("return_value_policy = move, but the "
340401
"object is neither movable nor copyable!");
341402
wrapper->owned = true;
342403
break;
343404

344405
case return_value_policy::reference_internal:
345-
wrapper->value = src;
406+
valueptr = src;
346407
wrapper->owned = false;
347408
detail::keep_alive_impl(inst, parent);
348409
break;
@@ -353,7 +414,7 @@ class type_caster_generic {
353414

354415
tinfo->init_holder(inst.ptr(), existing_holder);
355416

356-
internals.registered_instances.emplace(wrapper->value, inst.ptr());
417+
internals.registered_instances.emplace(valueptr, inst.ptr());
357418

358419
return inst.release();
359420
}
@@ -590,8 +651,9 @@ template <> class type_caster<void> : public type_caster<void_type> {
590651
}
591652

592653
/* Check if this is a C++ type */
593-
if (get_type_info((PyTypeObject *) h.get_type().ptr())) {
594-
value = ((instance<void> *) h.ptr())->value;
654+
type_info *tinfo = get_type_info((PyTypeObject *) h.get_type().ptr());
655+
if (tinfo) {
656+
value = get_value_and_holder(reinterpret_cast<instance *>(h.ptr()), tinfo).value;
595657
return true;
596658
}
597659

@@ -890,42 +952,28 @@ struct copyable_holder_caster : public type_caster_base<type> {
890952
using base::temp;
891953

892954
PYBIND11_NOINLINE bool load(handle src, bool convert) {
893-
return load(src, convert, Py_TYPE(src.ptr()));
894-
}
895-
896-
bool load(handle src, bool convert, PyTypeObject *tobj) {
897955
if (!src || !typeinfo)
898956
return false;
899957
if (src.is_none()) {
900958
value = nullptr;
901959
return true;
902960
}
903961

904-
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
905-
/* Check if we can safely perform a reinterpret-style cast */
906-
if (PyType_IsSubtype(tobj, typeinfo->type))
907-
return load_value_and_holder(src);
908-
} else { /* Case 2: multiple inheritance */
909-
/* Check if we can safely perform a reinterpret-style cast */
910-
if (tobj == typeinfo->type)
911-
return load_value_and_holder(src);
912-
913-
/* If this is a python class, also check the parents recursively */
914-
auto const &type_dict = get_internals().registered_types_py;
915-
bool new_style_class = PyType_Check((PyObject *) tobj);
916-
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
917-
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
918-
for (handle parent : parents) {
919-
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
920-
if (result)
921-
return true;
922-
}
962+
PyTypeObject *pytype = Py_TYPE(src.ptr());
963+
// First check to see if we can safely perform a reinterpret-style cast: allowed if one of
964+
// the pybind bases of `src` (i.e. following non-pybind inheritance) is the desired type
965+
auto registered_bases = get_all_type_info(pytype);
966+
for (auto base : registered_bases) {
967+
if (base == typeinfo ||
968+
(typeinfo->simple_type && PyType_IsSubtype(base->type, typeinfo->type))) {
969+
return load_value_and_holder(get_value_and_holder(
970+
reinterpret_cast<instance *>(src.ptr()), registered_bases, base));
923971
}
924-
925-
if (try_implicit_casts(src, convert))
926-
return true;
927972
}
928973

974+
if (try_implicit_casts(src, convert))
975+
return true;
976+
929977
if (convert) {
930978
for (auto &converter : typeinfo->implicit_conversions) {
931979
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
@@ -937,11 +985,10 @@ struct copyable_holder_caster : public type_caster_base<type> {
937985
return false;
938986
}
939987

940-
bool load_value_and_holder(handle src) {
941-
auto inst = (instance<type, holder_type> *) src.ptr();
942-
value = (void *) inst->value;
943-
if (inst->holder_constructed) {
944-
holder = inst->holder;
988+
bool load_value_and_holder(const value_and_holder &v_h) {
989+
if (v_h.holder) {
990+
value = v_h.value;
991+
holder = *v_h.holder_as<holder_type *>();
945992
return true;
946993
} else {
947994
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "

0 commit comments

Comments
 (0)