Fail to compile with MI via class_ ctor parameters

We can't support this for classes from imported modules (which is the
primary purpose of a ctor argument base class) because we *have* to
have both parent and derived to properly extract a multiple-inheritance
base class pointer from a derived class pointer.

We could support this for actual `class_<Base, ...> instances, but since
in that case the `Base` is already present in the code, it seems more
consistent to simply always require MI to go via template options.
This commit is contained in:
Jason Rhinelander 2017-03-16 20:10:48 -03:00
parent d51acb6873
commit b961626c0c
3 changed files with 31 additions and 12 deletions

View File

@ -333,7 +333,7 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
}
};
/// Process a parent class attribute
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that)
template <typename T>
struct process_attribute<T, enable_if_t<is_pyobject<T>::value>> : process_attribute_default<handle> {
static void init(const handle &h, type_record *r) { r->bases.append(h); }

View File

@ -885,11 +885,21 @@ public:
template <typename... Extra>
class_(handle scope, const char *name, const Extra &... extra) {
detail::type_record record;
using namespace detail;
// MI can only be specified via class_ template options, not constructor parameters
static_assert(
none_of<is_pyobject<Extra>...>::value || // no base class arguments, or:
( constexpr_sum(is_pyobject<Extra>::value...) == 1 && // Exactly one base
constexpr_sum(is_base<options>::value...) == 0 && // no template option bases
none_of<std::is_same<multiple_inheritance, Extra>...>::value), // no multiple_inheritance attr
"Error: multiple inheritance bases must be specified via class_ template options");
type_record record;
record.scope = scope;
record.name = name;
record.type = &typeid(type);
record.type_size = sizeof(detail::conditional_t<has_alias, type_alias, type>);
record.type_size = sizeof(conditional_t<has_alias, type_alias, type>);
record.instance_size = sizeof(instance_type);
record.init_holder = init_holder;
record.dealloc = dealloc;
@ -900,12 +910,12 @@ public:
(void) unused;
/* Process optional arguments, if any */
detail::process_attributes<Extra...>::init(extra..., &record);
process_attributes<Extra...>::init(extra..., &record);
detail::generic_type::initialize(record);
generic_type::initialize(record);
if (has_alias) {
auto &instances = pybind11::detail::get_internals().registered_types_cpp;
auto &instances = get_internals().registered_types_cpp;
instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))];
}
}

View File

@ -31,18 +31,27 @@ struct MIType : Base12 {
};
test_initializer multiple_inheritance([](py::module &m) {
py::class_<Base1>(m, "Base1")
.def(py::init<int>())
.def("foo", &Base1::foo);
py::class_<Base1> b1(m, "Base1");
b1.def(py::init<int>())
.def("foo", &Base1::foo);
py::class_<Base2>(m, "Base2")
.def(py::init<int>())
.def("bar", &Base2::bar);
py::class_<Base2> b2(m, "Base2");
b2.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>());
// Uncommenting this should result in a compile time failure (MI can only be specified via
// template parameters because pybind has to know the types involved; see discussion in #742 for
// details).
// struct Base12v2 : Base1, Base2 {
// Base12v2(int i, int j) : Base1(i), Base2(j) { }
// };
// py::class_<Base12v2>(m, "Base12v2", b1, b2)
// .def(py::init<int, int>());
});
/* Test the case where not all base classes are specified,