diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 675e8622dc..f1cf689a0f 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -832,30 +832,38 @@ struct is_move_constructible : std::is_move_constructible {}; // True if Container has a dependent type mapped_type that is equivalent // to Container itself -// Actual implementation in the SFINAE specializations below +// Actual implementation in the SFINAE specialization below template -struct sfinae_is_container_with_self_referential_mapped_type : std::false_type {}; +struct is_container_with_recursive_mapped_type : std::false_type {}; -// Tie-breaking between the mapped_type and the value_type specializations is trivial: -// The specializations are only valid if both conditions are fulfilled: -// 1) The mapped_type (respectively value_type) exists +// This specialization is only valid if both conditions are fulfilled: +// 1) The mapped_type exists // 2) And it is equivalent to Container -// So, in each case, only one specialization will activate. template -struct sfinae_is_container_with_self_referential_mapped_type +struct is_container_with_recursive_mapped_type : std::true_type {}; +// True if Container has a dependent type value_type that is equivalent +// to Container itself +// Actual implementation in the SFINAE specialization below +template +struct is_container_with_recursive_value_type : std::false_type {}; + +// This specialization is only valid if both conditions are fulfilled: +// 1) The value_type exists +// 2) And it is equivalent to Container template -struct sfinae_is_container_with_self_referential_mapped_type +struct is_container_with_recursive_value_type : std::true_type {}; -// Use a helper struct in order to give this a nicer public API without helper template parameter. -// This makes this struct nicer to specialize by users. -template -struct is_container_with_self_referential_mapped_type - : sfinae_is_container_with_self_referential_mapped_type {}; +// True constant if the type contains itself recursively. +// By default, this will check the mapped_type and value_type dependent types. +// In more complex recursion patterns, users can specialize this struct. +// The second template parameter SFINAE=void is for use of std::enable_if in specializations. +// An example is found in tests/test_stl_binders.cpp. +template +struct is_recursive_container : any_of, + is_container_with_recursive_mapped_type> {}; // Specialization for types that appear to be copy constructible but also look like stl containers // (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if @@ -867,7 +875,7 @@ struct is_copy_constructible< all_of, std::is_same, // Avoid infinite recursion - negation>>::value>> + negation>>::value>> : is_copy_constructible {}; // Likewise for std::pair @@ -888,7 +896,7 @@ struct is_copy_assignable< all_of, std::is_same, // Avoid infinite recursion - negation>>::value>> + negation>>::value>> : is_copy_assignable {}; template struct is_copy_assignable> diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 069d9dc70e..82752ecb88 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -61,11 +61,10 @@ struct is_comparable< /* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ template -struct is_comparable< - T, - enable_if_t::is_vector && - // Avoid this instantiation if the type is recursive - negation>::value>> { +struct is_comparable::is_vector && + // Avoid this instantiation if the type is recursive + negation>::value>> { static constexpr const bool value = is_comparable::value; }; diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp index c921033321..9eca353f00 100644 --- a/tests/test_stl_binders.cpp +++ b/tests/test_stl_binders.cpp @@ -90,20 +90,18 @@ struct RecursiveMap : std::map { * pybind11::detail::is_container_with_self_referential_mapped_type, thus * manually telling pybind11 about the recursion. */ -struct MutuallyRecursiveContainerPairA; -struct MutuallyRecursiveContainerPairB; +struct MutuallyRecursiveContainerPairMV; +struct MutuallyRecursiveContainerPairVM; -struct MutuallyRecursiveContainerPairA : std::map {}; -struct MutuallyRecursiveContainerPairB : std::vector {}; +struct MutuallyRecursiveContainerPairMV : std::map {}; +struct MutuallyRecursiveContainerPairVM : std::vector {}; namespace pybind11 { namespace detail { -template <> -struct is_container_with_self_referential_mapped_type - : std::true_type {}; -template <> -struct is_container_with_self_referential_mapped_type - : std::true_type {}; +template +struct is_recursive_container : std::true_type {}; +template +struct is_recursive_container : std::true_type {}; } // namespace detail } // namespace pybind11 @@ -169,8 +167,8 @@ TEST_SUBMODULE(stl_binders, m) { // Bind recursive container types py::bind_vector(m, "RecursiveVector"); py::bind_map(m, "RecursiveMap"); - py::bind_map(m, "MutuallyRecursiveContainerPairA"); - py::bind_vector(m, "MutuallyRecursiveContainerPairB"); + py::bind_map(m, "MutuallyRecursiveContainerPairMV"); + py::bind_vector(m, "MutuallyRecursiveContainerPairVM"); // The rest depends on numpy: try {