Multiple inheritance support

This commit is contained in:
Wenzel Jakob 2016-09-11 20:00:40 +09:00
parent bad589a477
commit 8e5dceb6a6
15 changed files with 421 additions and 94 deletions

View File

@ -45,10 +45,12 @@ pybind11 can map the following core C++ features to Python
- Arbitrary exception types - Arbitrary exception types
- Enumerations - Enumerations
- Callbacks - Callbacks
- Iterators and ranges
- Custom operators - Custom operators
- Single and multiple inheritance
- STL data structures - STL data structures
- Iterators and ranges - Iterators and ranges
- Smart pointers with reference counting like `std::shared_ptr` - Smart pointers with reference counting like ``std::shared_ptr``
- Internal references with correct reference counting - Internal references with correct reference counting
- C++ classes with virtual (and pure virtual) methods can be extended in Python - C++ classes with virtual (and pure virtual) methods can be extended in Python

View File

@ -217,6 +217,8 @@ The following interactive session shows how to call them from Python.
that demonstrates how to work with callbacks and anonymous functions in that demonstrates how to work with callbacks and anonymous functions in
more detail. more detail.
.. _overriding_virtuals:
Overriding virtual functions in Python Overriding virtual functions in Python
====================================== ======================================
@ -2151,3 +2153,45 @@ type is explicitly allowed.
}; };
} }
}; };
Multiple Inheritance
====================
pybind11 can create bindings for types that derive from multiple base types
(aka. *multiple inheritance*). To do so, specify all bases in the template
arguments of the ``class_`` declaration:
.. code-block:: cpp
py::class_<MyType, BaseType1, BaseType2, BaseType3>(m, "MyType")
...
The base types can be specified in arbitrary order, and they can even be
interspersed with alias types and holder types (discussed earlier in this
document)---pybind11 will automatically find out which is which. The only
requirement is that the first template argument is the type to be declared.
There are two caveats regarding the implementation of this feature:
1. When only one base type is specified for a C++ type that actually has
multiple bases, pybind11 will assume that it does not participate in
multiple inheritance, which can lead to undefined behavior. In such cases,
add the tag ``multiple_inheritance``:
.. code-block:: cpp
py::class_<MyType, BaseType2>(m, "MyType", py::multiple_inheritance());
The tag is redundant and does not need to be specified when multiple base
types are listed.
2. As was previously discussed in the section on :ref:`overriding_virtuals`, it
is easy to create Python types that derive from C++ classes. It is even
possible to make use of multiple inheritance to declare a Python class which
has e.g. a C++ and a Python class as bases. However, any attempt to create a
type that has *two or more* C++ classes in its hierarchy of base types will
fail with a fatal error message: ``TypeError: multiple bases have instance
lay-out conflict``. Core Python types that are implemented in C (e.g.
``dict``, ``list``, ``Exception``, etc.) also fall under this combination
and cannot be combined with C++ types bound using pybind11 via multiple
inheritance.

View File

@ -35,12 +35,14 @@ The following core C++ features can be mapped to Python
- Instance methods and static methods - Instance methods and static methods
- Overloaded functions - Overloaded functions
- Instance attributes and static attributes - Instance attributes and static attributes
- Exceptions - Arbitrary exception types
- Enumerations - Enumerations
- Iterators and ranges
- Callbacks - Callbacks
- Iterators and ranges
- Custom operators - Custom operators
- Single and multiple inheritance
- STL data structures - STL data structures
- Iterators and ranges
- Smart pointers with reference counting like ``std::shared_ptr`` - Smart pointers with reference counting like ``std::shared_ptr``
- Internal references with correct reference counting - Internal references with correct reference counting
- C++ classes with virtual (and pure virtual) methods can be extended in Python - C++ classes with virtual (and pure virtual) methods can be extended in Python

View File

@ -9,15 +9,12 @@ certain limitations:
values. This means that some additional care is needed to avoid bugs that values. This means that some additional care is needed to avoid bugs that
would be caught by the type checker in a traditional C++ program. would be caught by the type checker in a traditional C++ program.
- Multiple inheritance relationships on the C++ side cannot be mapped to
Python.
- The NumPy interface ``pybind11::array`` greatly simplifies accessing - The NumPy interface ``pybind11::array`` greatly simplifies accessing
numerical data from C++ (and vice versa), but it's not a full-blown array numerical data from C++ (and vice versa), but it's not a full-blown array
class like ``Eigen::Array`` or ``boost.multi_array``. class like ``Eigen::Array`` or ``boost.multi_array``.
All of these features could be implemented but would lead to a significant These features could be implemented but would lead to a significant increase in
increase in complexity. I've decided to draw the line here to keep this project complexity. I've decided to draw the line here to keep this project simple and
simple and compact. Users who absolutely require these features are encouraged compact. Users who absolutely require these features are encouraged to fork
to fork pybind11. pybind11.

View File

@ -41,6 +41,9 @@ template <typename T> struct base {
/// Keep patient alive while nurse lives /// Keep patient alive while nurse lives
template <int Nurse, int Patient> struct keep_alive { }; template <int Nurse, int Patient> struct keep_alive { };
/// Annotation indicating that a class is involved in a multiple inheritance relationship
struct multiple_inheritance { };
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
/* Forward declarations */ /* Forward declarations */
enum op_id : int; enum op_id : int;
@ -127,6 +130,8 @@ struct function_record {
/// Special data structure which (temporarily) holds metadata about a bound class /// Special data structure which (temporarily) holds metadata about a bound class
struct type_record { struct type_record {
PYBIND11_NOINLINE type_record() { }
/// Handle to the parent scope /// Handle to the parent scope
handle scope; handle scope;
@ -148,21 +153,36 @@ struct type_record {
/// Function pointer to class_<..>::dealloc /// Function pointer to class_<..>::dealloc
void (*dealloc)(PyObject *) = nullptr; void (*dealloc)(PyObject *) = nullptr;
// Pointer to RTTI type_info data structure of base class /// List of base classes of the newly created type
const std::type_info *base_type = nullptr; list bases;
/// OR: Python handle to base class
handle base_handle;
/// Optional docstring /// Optional docstring
const char *doc = nullptr; const char *doc = nullptr;
/// Multiple inheritance marker
bool multiple_inheritance = false;
PYBIND11_NOINLINE void add_base(const std::type_info *base, void *(*caster)(void *)) {
auto base_info = detail::get_type_info(*base, false);
if (!base_info) {
std::string tname(base->name());
detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(name) +
"\" referenced unknown base type \"" + tname + "\"");
}
bases.append((PyObject *) base_info->type);
if (caster)
base_info->implicit_casts.push_back(std::make_pair(type, caster));
}
}; };
/** /**
* Partial template specializations to process custom attributes provided to * Partial template specializations to process custom attributes provided to
* cpp_function_ and class_. These are either used to initialize the respective * cpp_function_ and class_. These are either used to initialize the respective
* fields in the type_record and function_record data structures or executed * fields in the type_record and function_record data structures or executed at
* at runtime to deal with custom call policies (e.g. keep_alive). * runtime to deal with custom call policies (e.g. keep_alive).
*/ */
template <typename T, typename SFINAE = void> struct process_attribute; template <typename T, typename SFINAE = void> struct process_attribute;
@ -257,13 +277,19 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
/// Process a parent class attribute /// Process a parent class attribute
template <typename T> template <typename T>
struct process_attribute<T, typename std::enable_if<std::is_base_of<handle, T>::value>::type> : process_attribute_default<handle> { struct process_attribute<T, typename std::enable_if<std::is_base_of<handle, T>::value>::type> : process_attribute_default<handle> {
static void init(const handle &h, type_record *r) { r->base_handle = h; } static void init(const handle &h, type_record *r) { r->bases.append(h); }
}; };
/// Process a parent class attribute /// Process a parent class attribute (deprecated, does not support multiple inheritance)
template <typename T> template <typename T>
struct process_attribute<base<T>> : process_attribute_default<base<T>> { struct process_attribute<base<T>> : process_attribute_default<base<T>> {
static void init(const base<T> &, type_record *r) { r->base_type = &typeid(T); } static void init(const base<T> &, type_record *r) { r->add_base(&typeid(T), nullptr); }
};
/// Process a multiple inheritance attribute
template <>
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> {
static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; }
}; };
/*** /***

View File

@ -15,7 +15,6 @@
#include "descr.h" #include "descr.h"
#include <array> #include <array>
#include <limits> #include <limits>
#include <iostream>
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
@ -26,8 +25,12 @@ struct type_info {
size_t type_size; size_t type_size;
void (*init_holder)(PyObject *, const void *); void (*init_holder)(PyObject *, const void *);
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions; std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
void *get_buffer_data = nullptr; void *get_buffer_data = nullptr;
/** A simple type never occurs as a (direct or indirect) parent
* of a class that makes use of multiple inheritance */
bool simple_type = true;
}; };
PYBIND11_NOINLINE inline internals &get_internals() { PYBIND11_NOINLINE inline internals &get_internals() {
@ -72,32 +75,34 @@ PYBIND11_NOINLINE inline internals &get_internals() {
return *internals_ptr; return *internals_ptr;
} }
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type, bool throw_if_missing = true) { PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
auto const &type_dict = get_internals().registered_types_py; auto const &type_dict = get_internals().registered_types_py;
do { do {
auto it = type_dict.find(type); auto it = type_dict.find(type);
if (it != type_dict.end()) if (it != type_dict.end())
return (detail::type_info *) it->second; return (detail::type_info *) it->second;
type = type->tp_base; type = type->tp_base;
if (!type) { if (!type)
if (throw_if_missing)
pybind11_fail("pybind11::detail::get_type_info: unable to find type object!");
return nullptr; return nullptr;
}
} while (true); } while (true);
} }
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp) { PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp, bool throw_if_missing) {
auto &types = get_internals().registered_types_cpp; auto &types = get_internals().registered_types_cpp;
auto it = types.find(std::type_index(tp)); auto it = types.find(std::type_index(tp));
if (it != types.end()) if (it != types.end())
return (detail::type_info *) it->second; return (detail::type_info *) it->second;
if (throw_if_missing) {
std::string tname = tp.name();
detail::clean_type_id(tname);
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\"");
}
return nullptr; return nullptr;
} }
PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp) { PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) {
detail::type_info *type_info = get_type_info(tp); detail::type_info *type_info = get_type_info(tp, throw_if_missing);
return handle(type_info ? ((PyObject *) type_info->type) : nullptr); return handle(type_info ? ((PyObject *) type_info->type) : nullptr);
} }
@ -124,7 +129,7 @@ PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail:
auto &instances = get_internals().registered_instances; auto &instances = get_internals().registered_instances;
auto range = instances.equal_range(ptr); auto range = instances.equal_range(ptr);
for (auto it = range.first; it != range.second; ++it) { for (auto it = range.first; it != range.second; ++it) {
auto instance_type = detail::get_type_info(Py_TYPE(it->second), false); auto instance_type = detail::get_type_info(Py_TYPE(it->second));
if (instance_type && instance_type == type) if (instance_type && instance_type == type)
return handle((PyObject *) it->second); return handle((PyObject *) it->second);
} }
@ -149,18 +154,56 @@ inline void keep_alive_impl(handle nurse, handle patient);
class type_caster_generic { class type_caster_generic {
public: public:
PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info)
: typeinfo(get_type_info(type_info)) { } : typeinfo(get_type_info(type_info, false)) { }
PYBIND11_NOINLINE bool load(handle src, bool convert) { PYBIND11_NOINLINE bool load(handle src, bool convert) {
return load(src, convert, Py_TYPE(src.ptr()));
}
bool load(handle src, bool convert, PyTypeObject *tobj) {
if (!src || !typeinfo) if (!src || !typeinfo)
return false; return false;
if (src.is_none()) { if (src.is_none()) {
value = nullptr; value = nullptr;
return true; return true;
} else if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) { }
value = ((instance<void> *) src.ptr())->value;
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
/* Check if we can safely perform a reinterpret-style cast */
if (PyType_IsSubtype(tobj, typeinfo->type)) {
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
return true; return true;
} }
} else { /* Case 2: multiple inheritance */
/* Check if we can safely perform a reinterpret-style cast */
if (tobj == typeinfo->type) {
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
return true;
}
/* If this is a python class, also check the parents recursively */
auto const &type_dict = get_internals().registered_types_py;
bool new_style_class = PyType_Check(tobj);
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
tuple parents(tobj->tp_bases, true);
for (handle parent : parents) {
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
if (result)
return true;
}
}
/* Try implicit casts */
for (auto &cast : typeinfo->implicit_casts) {
type_caster_generic sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
value = cast.second(sub_caster.value);
return true;
}
}
}
/* Perform an implicit conversion */
if (convert) { if (convert) {
for (auto &converter : typeinfo->implicit_conversions) { for (auto &converter : typeinfo->implicit_conversions) {
temp = object(converter(src.ptr(), typeinfo->type), false); temp = object(converter(src.ptr(), typeinfo->type), false);
@ -201,7 +244,7 @@ public:
auto it_instances = internals.registered_instances.equal_range(src); auto it_instances = internals.registered_instances.equal_range(src);
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
auto instance_type = detail::get_type_info(Py_TYPE(it_i->second), false); auto instance_type = detail::get_type_info(Py_TYPE(it_i->second));
if (instance_type && instance_type == tinfo) if (instance_type && instance_type == tinfo)
return handle((PyObject *) it_i->second).inc_ref(); return handle((PyObject *) it_i->second).inc_ref();
} }
@ -262,7 +305,8 @@ template <typename type> class type_caster_base : public type_caster_generic {
public: public:
static PYBIND11_DESCR name() { return type_descr(_<type>()); } static PYBIND11_DESCR name() { return type_descr(_<type>()); }
type_caster_base() : type_caster_generic(typeid(type)) { } type_caster_base() : type_caster_base(typeid(type)) { }
type_caster_base(const std::type_info &info) : type_caster_generic(info) { }
static handle cast(const itype &src, return_value_policy policy, handle parent) { static handle cast(const itype &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
@ -433,7 +477,7 @@ public:
} }
/* Check if this is a C++ type */ /* Check if this is a C++ type */
if (get_type_info((PyTypeObject *) h.get_type().ptr(), false)) { if (get_type_info((PyTypeObject *) h.get_type().ptr())) {
value = ((instance<void> *) h.ptr())->value; value = ((instance<void> *) h.ptr())->value;
return true; return true;
} }
@ -749,23 +793,57 @@ protected:
/// Type caster for holder types like std::shared_ptr, etc. /// Type caster for holder types like std::shared_ptr, etc.
template <typename type, typename holder_type> class type_caster_holder : public type_caster_base<type> { template <typename type, typename holder_type> class type_caster_holder : public type_caster_base<type> {
public: public:
using type_caster_base<type>::cast; using base = type_caster_base<type>;
using type_caster_base<type>::typeinfo; using base::base;
using type_caster_base<type>::value; using base::cast;
using type_caster_base<type>::temp; using base::typeinfo;
using base::value;
using base::temp;
bool load(handle src, bool convert) { PYBIND11_NOINLINE bool load(handle src, bool convert) {
if (!src || !typeinfo) { return load(src, convert, Py_TYPE(src.ptr()));
}
bool load(handle src, bool convert, PyTypeObject *tobj) {
if (!src || !typeinfo)
return false; return false;
} else if (src.is_none()) { if (src.is_none()) {
value = nullptr; value = nullptr;
return true; return true;
} else if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) { }
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
/* Check if we can safely perform a reinterpret-style cast */
if (PyType_IsSubtype(tobj, typeinfo->type)) {
auto inst = (instance<type, holder_type> *) src.ptr(); auto inst = (instance<type, holder_type> *) src.ptr();
value = (void *) inst->value; value = (void *) inst->value;
holder = inst->holder; holder = inst->holder;
return true; return true;
} }
} else { /* Case 2: multiple inheritance */
/* Check if we can safely perform a reinterpret-style cast */
if (tobj == typeinfo->type) {
auto inst = (instance<type, holder_type> *) src.ptr();
value = (void *) inst->value;
holder = inst->holder;
return true;
}
/* If this is a python class, also check the parents recursively */
auto const &type_dict = get_internals().registered_types_py;
bool new_style_class = PyType_Check(tobj);
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
tuple parents(tobj->tp_bases, true);
for (handle parent : parents) {
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
if (result)
return true;
}
}
if (try_implicit_casts(src, convert))
return true;
}
if (convert) { if (convert) {
for (auto &converter : typeinfo->implicit_conversions) { for (auto &converter : typeinfo->implicit_conversions) {
@ -774,6 +852,23 @@ public:
return true; return true;
} }
} }
return false;
}
template <typename T = holder_type, detail::enable_if_t<!std::is_constructible<T, const T &, type*>::value, int> = 0>
bool try_implicit_casts(handle, bool) { return false; }
template <typename T = holder_type, detail::enable_if_t<std::is_constructible<T, const T &, type*>::value, int> = 0>
bool try_implicit_casts(handle src, bool convert) {
for (auto &cast : typeinfo->implicit_casts) {
type_caster_holder sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
value = cast.second(sub_caster.value);
holder = holder_type(sub_caster.holder, (type *) value);
return true;
}
}
return false; return false;
} }
@ -968,7 +1063,6 @@ template <> inline void cast_safe<void>(object &&) {}
NAMESPACE_END(detail) NAMESPACE_END(detail)
template <return_value_policy policy = return_value_policy::automatic_reference, template <return_value_policy policy = return_value_policy::automatic_reference,
typename... Args> tuple make_tuple(Args&&... args_) { typename... Args> tuple make_tuple(Args&&... args_) {
const size_t size = sizeof...(Args); const size_t size = sizeof...(Args);
@ -1023,7 +1117,7 @@ struct arg_v : arg {
template <typename T> template <typename T>
arg_v arg::operator=(T &&value) const { return {name, std::forward<T>(value)}; } arg_v arg::operator=(T &&value) const { return {name, std::forward<T>(value)}; }
/// Alias for backward compatibility -- to be remove in version 2.0 /// Alias for backward compatibility -- to be removed in version 2.0
template <typename /*unused*/> using arg_t = arg_v; template <typename /*unused*/> using arg_t = arg_v;
inline namespace literals { inline namespace literals {

View File

@ -89,6 +89,7 @@
#include <unordered_map> #include <unordered_map>
#include <memory> #include <memory>
#include <typeindex> #include <typeindex>
#include <type_traits>
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions #if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) #define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)

View File

@ -10,7 +10,6 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <type_traits>
#if defined(__clang__) && !defined(__INTEL_COMPILER) #if defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) # pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type()))

View File

@ -17,6 +17,7 @@
# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted # pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted
# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) # pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name # pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
# pragma warning(disable: 4702) // warning C4702: unreachable code
#elif defined(__INTEL_COMPILER) #elif defined(__INTEL_COMPILER)
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero # pragma warning(disable: 186) // pointless comparison of unsigned integer with zero
@ -576,18 +577,6 @@ public:
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
protected: protected:
void initialize(type_record *rec) { void initialize(type_record *rec) {
if (rec->base_type) {
if (rec->base_handle)
pybind11_fail("generic_type: specified base type multiple times!");
rec->base_handle = detail::get_type_handle(*(rec->base_type));
if (!rec->base_handle) {
std::string tname(rec->base_type->name());
detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(rec->name) +
"\" referenced unknown base type \"" + tname + "\"");
}
}
auto &internals = get_internals(); auto &internals = get_internals();
auto tindex = std::type_index(*(rec->type)); auto tindex = std::type_index(*(rec->type));
@ -617,6 +606,12 @@ protected:
ht_qualname = name; ht_qualname = name;
} }
#endif #endif
size_t num_bases = rec->bases.size();
tuple bases(num_bases);
for (size_t i = 0; i < num_bases; ++i)
bases[i] = rec->bases[i];
std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + rec->name) std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + rec->name)
: std::string(rec->name)); : std::string(rec->name));
@ -629,6 +624,11 @@ protected:
memcpy((void *) tp_doc, rec->doc, size); memcpy((void *) tp_doc, rec->doc, size);
} }
/* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false); object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
auto type = (PyHeapTypeObject*) type_holder.ptr(); auto type = (PyHeapTypeObject*) type_holder.ptr();
@ -646,8 +646,12 @@ protected:
/* Basic type attributes */ /* Basic type attributes */
type->ht_type.tp_name = strdup(full_name.c_str()); type->ht_type.tp_name = strdup(full_name.c_str());
type->ht_type.tp_basicsize = (ssize_t) rec->instance_size; type->ht_type.tp_basicsize = (ssize_t) rec->instance_size;
type->ht_type.tp_base = (PyTypeObject *) rec->base_handle.ptr();
rec->base_handle.inc_ref(); if (num_bases > 0) {
type->ht_type.tp_base = (PyTypeObject *) ((object) bases[0]).inc_ref().ptr();
type->ht_type.tp_bases = bases.release().ptr();
rec->multiple_inheritance |= num_bases > 1;
}
type->ht_name = name.release().ptr(); type->ht_name = name.release().ptr();
@ -689,9 +693,23 @@ protected:
if (rec->scope) if (rec->scope)
rec->scope.attr(handle(type->ht_name)) = *this; rec->scope.attr(handle(type->ht_name)) = *this;
if (rec->multiple_inheritance)
mark_parents_nonsimple(&type->ht_type);
type_holder.release(); type_holder.release();
} }
/// Helper function which tags all parents of a type using mult. inheritance
void mark_parents_nonsimple(PyTypeObject *value) {
tuple t(value->tp_bases, true);
for (handle h : t) {
auto tinfo2 = get_type_info((PyTypeObject *) h.ptr());
if (tinfo2)
tinfo2->simple_type = false;
mark_parents_nonsimple((PyTypeObject *) h.ptr());
}
}
/// Allocate a metaclass on demand (for static properties) /// Allocate a metaclass on demand (for static properties)
handle metaclass() { handle metaclass() {
auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type; auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type;
@ -811,31 +829,18 @@ protected:
static void releasebuffer(PyObject *, Py_buffer *view) { delete (buffer_info *) view->internal; } static void releasebuffer(PyObject *, Py_buffer *view) { delete (buffer_info *) view->internal; }
}; };
template <template<typename> class Predicate, typename... BaseTypes> struct class_selector;
template <template<typename> class Predicate, typename Base, typename... Bases>
struct class_selector<Predicate, Base, Bases...> {
static inline void set_bases(detail::type_record &record) {
if (Predicate<Base>::value) record.base_type = &typeid(Base);
else class_selector<Predicate, Bases...>::set_bases(record);
}
};
template <template<typename> class Predicate>
struct class_selector<Predicate> {
static inline void set_bases(detail::type_record &) {}
};
NAMESPACE_END(detail) NAMESPACE_END(detail)
template <typename type_, typename... options> template <typename type_, typename... options>
class class_ : public detail::generic_type { class class_ : public detail::generic_type {
template <typename T> using is_holder = detail::is_holder_type<type_, T>; template <typename T> using is_holder = detail::is_holder_type<type_, T>;
template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>; template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>;
template <typename T> using is_base_class = detail::bool_constant<std::is_base_of<T, type_>::value && !std::is_same<T, type_>::value>; template <typename T> using is_base = detail::bool_constant<std::is_base_of<T, type_>::value && !std::is_same<T, type_>::value>;
template <typename T> using is_valid_class_option = template <typename T> using is_valid_class_option =
detail::bool_constant< detail::bool_constant<
is_holder<T>::value || is_holder<T>::value ||
is_subtype<T>::value || is_subtype<T>::value ||
is_base_class<T>::value is_base<T>::value
>; >;
public: public:
@ -848,9 +853,6 @@ public:
static_assert(detail::all_of_t<is_valid_class_option, options...>::value, static_assert(detail::all_of_t<is_valid_class_option, options...>::value,
"Unknown/invalid class_ template parameters provided"); "Unknown/invalid class_ template parameters provided");
static_assert(detail::count_t<is_base_class, options...>::value <= 1,
"Invalid class_ base types: multiple inheritance is not supported");
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check) PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
template <typename... Extra> template <typename... Extra>
@ -864,7 +866,9 @@ public:
record.init_holder = init_holder; record.init_holder = init_holder;
record.dealloc = dealloc; record.dealloc = dealloc;
detail::class_selector<is_base_class, options...>::set_bases(record); /* Register base classes specified via template arguments to class_, if any */
bool unused[] = { (add_base<options>(record), false)... };
(void) unused;
/* Process optional arguments, if any */ /* Process optional arguments, if any */
detail::process_attributes<Extra...>::init(extra..., &record); detail::process_attributes<Extra...>::init(extra..., &record);
@ -877,6 +881,16 @@ public:
} }
} }
template <typename Base, std::enable_if_t<is_base<Base>::value, int> = 0>
static void add_base(detail::type_record &rec) {
rec.add_base(&typeid(Base), [](void *src) -> void * {
return static_cast<Base *>(reinterpret_cast<type *>(src));
});
}
template <typename Base, std::enable_if_t<!is_base<Base>::value, int> = 0>
static void add_base(detail::type_record &) { }
template <typename Func, typename... Extra> template <typename Func, typename... Extra>
class_ &def(const char *name_, Func&& f, const Extra&... extra) { class_ &def(const char *name_, Func&& f, const Extra&... extra) {
cpp_function cf(std::forward<Func>(f), name(name_), cpp_function cf(std::forward<Func>(f), name(name_),
@ -1198,7 +1212,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
typedef detail::iterator_state<Iterator, Sentinel, false, Policy> state; typedef detail::iterator_state<Iterator, Sentinel, false, Policy> state;
if (!detail::get_type_info(typeid(state))) { if (!detail::get_type_info(typeid(state), false)) {
class_<state>(handle(), "iterator") class_<state>(handle(), "iterator")
.def("__iter__", [](state &s) -> state& { return s; }) .def("__iter__", [](state &s) -> state& { return s; })
.def("__next__", [](state &s) -> ValueType { .def("__next__", [](state &s) -> ValueType {
@ -1223,7 +1237,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) {
typedef detail::iterator_state<Iterator, Sentinel, true, Policy> state; typedef detail::iterator_state<Iterator, Sentinel, true, Policy> state;
if (!detail::get_type_info(typeid(state))) { if (!detail::get_type_info(typeid(state), false)) {
class_<state>(handle(), "iterator") class_<state>(handle(), "iterator")
.def("__iter__", [](state &s) -> state& { return s; }) .def("__iter__", [](state &s) -> state& { return s; })
.def("__next__", [](state &s) -> KeyType { .def("__next__", [](state &s) -> KeyType {

View File

@ -612,7 +612,7 @@ public:
} }
size_t size() const { return (size_t) PyList_Size(m_ptr); } size_t size() const { return (size_t) PyList_Size(m_ptr); }
detail::list_accessor operator[](size_t index) const { return detail::list_accessor(*this, index); } detail::list_accessor operator[](size_t index) const { return detail::list_accessor(*this, index); }
void append(const object &object) const { PyList_Append(m_ptr, object.ptr()); } void append(handle h) const { PyList_Append(m_ptr, h.ptr()); }
}; };
class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) };

View File

@ -12,8 +12,6 @@
#include "common.h" #include "common.h"
#include "operators.h" #include "operators.h"
#include <type_traits>
#include <utility>
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>

View File

@ -27,9 +27,9 @@ set(PYBIND11_TEST_FILES
test_pickling.cpp test_pickling.cpp
test_python_types.cpp test_python_types.cpp
test_sequences_and_iterators.cpp test_sequences_and_iterators.cpp
test_smart_ptr.cpp
test_stl_binders.cpp test_stl_binders.cpp
test_virtual_functions.cpp test_virtual_functions.cpp
test_multiple_inheritance.cpp
) )
string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")

View File

@ -61,7 +61,7 @@ test_initializer inheritance([](py::module &m) {
.def(py::init<std::string>()); .def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */ /* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit>(m, "Rabbit", py::base<Pet>()) py::class_<Rabbit, Pet>(m, "Rabbit")
.def(py::init<std::string>()); .def(py::init<std::string>());
/* And another: list parent in class template arguments */ /* And another: list parent in class template arguments */

View File

@ -0,0 +1,85 @@
/*
tests/test_multiple_inheritance.cpp -- multiple inheritance,
implicit MI casts
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
struct Base1 {
Base1(int i) : i(i) { }
int foo() { return i; }
int i;
};
struct Base2 {
Base2(int i) : i(i) { }
int bar() { return i; }
int i;
};
struct Base12 : Base1, Base2 {
Base12(int i, int j) : Base1(i), Base2(j) { }
};
struct MIType : Base12 {
MIType(int i, int j) : Base12(i, j) { }
};
test_initializer multiple_inheritance([](py::module &m) {
py::class_<Base1>(m, "Base1")
.def(py::init<int>())
.def("foo", &Base1::foo);
py::class_<Base2>(m, "Base2")
.def(py::init<int>())
.def("bar", &Base2::bar);
py::class_<Base12, Base1, Base2>(m, "Base12");
py::class_<MIType, Base12>(m, "MIType")
.def(py::init<int, int>());
});
/* Test the case where not all base classes are specified,
and where pybind11 requires the py::multiple_inheritance
flag to perform proper casting between types */
struct Base1a {
Base1a(int i) : i(i) { }
int foo() { return i; }
int i;
};
struct Base2a {
Base2a(int i) : i(i) { }
int bar() { return i; }
int i;
};
struct Base12a : Base1a, Base2a {
Base12a(int i, int j) : Base1a(i), Base2a(j) { }
};
test_initializer multiple_inheritance_nonexplicit([](py::module &m) {
py::class_<Base1a, std::shared_ptr<Base1a>>(m, "Base1a")
.def(py::init<int>())
.def("foo", &Base1a::foo);
py::class_<Base2a, std::shared_ptr<Base2a>>(m, "Base2a")
.def(py::init<int>())
.def("bar", &Base2a::bar);
py::class_<Base12a, /* Base1 missing */ Base2a,
std::shared_ptr<Base12a>>(m, "Base12a", py::multiple_inheritance())
.def(py::init<int, int>());
m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
m.def("bar_base2a_sharedptr", [](std::shared_ptr<Base2a> b) { return b->bar(); });
});

View File

@ -0,0 +1,65 @@
import pytest
def test_multiple_inheritance_cpp(msg):
from pybind11_tests import MIType
mt = MIType(3, 4)
assert mt.foo() == 3
assert mt.bar() == 4
def test_multiple_inheritance_mix1(msg):
from pybind11_tests import Base2
class Base1:
def __init__(self, i):
self.i = i
def foo(self):
return self.i
class MITypePy(Base1, Base2):
def __init__(self, i, j):
Base1.__init__(self, i)
Base2.__init__(self, j)
mt = MITypePy(3, 4)
assert mt.foo() == 3
assert mt.bar() == 4
def test_multiple_inheritance_mix2(msg):
from pybind11_tests import Base1
class Base2:
def __init__(self, i):
self.i = i
def bar(self):
return self.i
class MITypePy(Base1, Base2):
def __init__(self, i, j):
Base1.__init__(self, i)
Base2.__init__(self, j)
mt = MITypePy(3, 4)
assert mt.foo() == 3
assert mt.bar() == 4
def test_multiple_inheritance_virtbase(msg):
from pybind11_tests import Base12a, bar_base2a, bar_base2a_sharedptr
class MITypePy(Base12a):
def __init__(self, i, j):
Base12a.__init__(self, i, j)
mt = MITypePy(3, 4)
assert mt.bar() == 4
assert bar_base2a(mt) == 4
assert bar_base2a_sharedptr(mt) == 4