Skip to content

Commit 863de9e

Browse files
committed
Detect multiple base classes and fail if found
1 parent 0b0c3f1 commit 863de9e

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

include/pybind11/pybind11.h

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,34 @@ struct extract_first<Predicate, T, Ts...> {
820820
>::type;
821821
};
822822

823+
824+
template <template<class> class Predicate, class... Ts>
825+
struct extract_all;
826+
827+
template <template<class> class Predicate>
828+
struct extract_all<Predicate> {
829+
using tuple = std::tuple<>;
830+
};
831+
832+
template <template<class> class Predicate, class T, class... Ts>
833+
struct extract_all<Predicate, T, Ts...> {
834+
using tuple = typename std::conditional<
835+
Predicate<T>::value,
836+
decltype(std::tuple_cat(std::declval<std::tuple<T>>(), std::declval<typename extract_all<Predicate, Ts...>::tuple>())),
837+
typename extract_all<Predicate, Ts...>::tuple
838+
>::type;
839+
};
840+
841+
template <typename... BaseTypes> struct class_bases;
842+
// Multiple inheritance support, if eventually supported, would use this specialization:
843+
//template <typename Base1, typename Base2, typename... Bases> struct class_bases<Base1, Base2, Bases...> {
844+
// static inline void add_to(detail::type_record &) {}
845+
//};
846+
template <typename Base> struct class_bases<Base> { static inline void add_to(detail::type_record &record) { record.base_type = &typeid(Base); } };
847+
template <> struct class_bases<> { static inline void add_to(detail::type_record &) {} };
848+
849+
template <typename... BaseTypes> struct class_bases<std::tuple<BaseTypes...>> : class_bases<BaseTypes...> {};
850+
823851
NAMESPACE_END(detail)
824852

825853
template <typename type_, typename... options>
@@ -828,7 +856,7 @@ class class_ : public detail::generic_type {
828856
template <typename T> using is_subtype = std::is_base_of<type_, T>;
829857
template <typename T> using is_supertype = std::is_base_of<T, type_>;
830858
using extracted_holder = detail::extract_first<is_holder, options...>;
831-
using extracted_base = detail::extract_first<is_supertype, options...>;
859+
using extracted_bases = typename detail::extract_all<is_supertype, options...>::tuple;
832860

833861
public:
834862
using type = type_;
@@ -844,9 +872,12 @@ class class_ : public detail::generic_type {
844872
static_assert(0 == sizeof...(options) -
845873
(int) has_alias -
846874
(int) !std::is_void<typename extracted_holder::type>::value -
847-
(int) !std::is_void<typename extracted_base::type>::value,
875+
(int) std::tuple_size<extracted_bases>::value,
848876
"Unknown/invalid class_ template parameters provided");
849877

878+
static_assert(std::tuple_size<extracted_bases>::value <= 1,
879+
"Invalid class_ base types: multiple inheritance is not supported");
880+
850881
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
851882

852883
template <typename... Extra>
@@ -860,8 +891,7 @@ class class_ : public detail::generic_type {
860891
record.init_holder = init_holder;
861892
record.dealloc = dealloc;
862893

863-
if (!std::is_void<typename extracted_base::type>::value)
864-
record.base_type = &typeid(typename extracted_base::type);
894+
detail::class_bases<extracted_bases>::add_to(record);
865895

866896
/* Process optional arguments, if any */
867897
detail::process_attributes<Extra...>::init(extra..., &record);

tests/test_class_args.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
5858
//// Invalid option (not a subclass or holder)
5959
//typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
6060
//CHECK_BROKEN(5);
61+
//// Invalid option: multiple inheritance not supported:
62+
//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
63+
//typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
64+
//CHECK_BROKEN(8);
6165

6266
test_initializer class_args([](py::module &m) {
6367
// Just test that this compiled okay

0 commit comments

Comments
 (0)