Skip to content

Commit d087c32

Browse files
committed
Suggestions from code review
1) Renaming: is_recursive_container and MutuallyRecursiveContainerPair(MV|VM) 2) Avoid ambiguous specializations of is_recursive_container
1 parent 523c874 commit d087c32

File tree

3 files changed

+39
-34
lines changed

3 files changed

+39
-34
lines changed

include/pybind11/detail/type_caster_base.h

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -829,30 +829,38 @@ struct is_copy_constructible : std::is_copy_constructible<T> {};
829829

830830
// True if Container has a dependent type mapped_type that is equivalent
831831
// to Container itself
832-
// Actual implementation in the SFINAE specializations below
832+
// Actual implementation in the SFINAE specialization below
833833
template <typename Container, typename MappedType = Container>
834-
struct sfinae_is_container_with_self_referential_mapped_type : std::false_type {};
834+
struct is_container_with_recursive_mapped_type : std::false_type {};
835835

836-
// Tie-breaking between the mapped_type and the value_type specializations is trivial:
837-
// The specializations are only valid if both conditions are fulfilled:
838-
// 1) The mapped_type (respectively value_type) exists
836+
// This specialization is only valid if both conditions are fulfilled:
837+
// 1) The mapped_type exists
839838
// 2) And it is equivalent to Container
840-
// So, in each case, only one specialization will activate.
841839
template <typename Container>
842-
struct sfinae_is_container_with_self_referential_mapped_type<Container,
843-
typename Container::mapped_type>
840+
struct is_container_with_recursive_mapped_type<Container, typename Container::mapped_type>
844841
: std::true_type {};
845842

843+
// True if Container has a dependent type value_type that is equivalent
844+
// to Container itself
845+
// Actual implementation in the SFINAE specialization below
846+
template <typename Container, typename MappedType = Container>
847+
struct is_container_with_recursive_value_type : std::false_type {};
848+
849+
// This specialization is only valid if both conditions are fulfilled:
850+
// 1) The value_type exists
851+
// 2) And it is equivalent to Container
846852
template <typename Container>
847-
struct sfinae_is_container_with_self_referential_mapped_type<Container,
848-
typename Container::value_type>
853+
struct is_container_with_recursive_value_type<Container, typename Container::value_type>
849854
: std::true_type {};
850855

851-
// Use a helper struct in order to give this a nicer public API without helper template parameter.
852-
// This makes this struct nicer to specialize by users.
853-
template <typename Container>
854-
struct is_container_with_self_referential_mapped_type
855-
: sfinae_is_container_with_self_referential_mapped_type<Container> {};
856+
// True constant if the type contains itself recursively.
857+
// By default, this will check the mapped_type and value_type dependent types.
858+
// In more complex recursion patterns, users can specialize this struct.
859+
// The second template parameter SFINAE=void is for use of std::enable_if in specializations.
860+
// An example is found in tests/test_stl_binders.cpp.
861+
template <typename Container, typename SFINAE = void>
862+
struct is_recursive_container : any_of<is_container_with_recursive_value_type<Container>,
863+
is_container_with_recursive_mapped_type<Container>> {};
856864

857865
// Specialization for types that appear to be copy constructible but also look like stl containers
858866
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
@@ -864,7 +872,7 @@ struct is_copy_constructible<
864872
all_of<std::is_copy_constructible<Container>,
865873
std::is_same<typename Container::value_type &, typename Container::reference>,
866874
// Avoid infinite recursion
867-
negation<is_container_with_self_referential_mapped_type<Container>>>::value>>
875+
negation<is_recursive_container<Container>>>::value>>
868876
: is_copy_constructible<typename Container::value_type> {};
869877

870878
// Likewise for std::pair
@@ -885,7 +893,7 @@ struct is_copy_assignable<
885893
all_of<std::is_copy_assignable<Container>,
886894
std::is_same<typename Container::value_type &, typename Container::reference>,
887895
// Avoid infinite recursion
888-
negation<is_container_with_self_referential_mapped_type<Container>>>::value>>
896+
negation<is_recursive_container<Container>>>::value>>
889897
: is_copy_assignable<typename Container::value_type> {};
890898
template <typename T1, typename T2>
891899
struct is_copy_assignable<std::pair<T1, T2>>

include/pybind11/stl_bind.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,10 @@ struct is_comparable<
6161
/* For a vector/map data structure, recursively check the value type
6262
(which is std::pair for maps) */
6363
template <typename T>
64-
struct is_comparable<
65-
T,
66-
enable_if_t<container_traits<T>::is_vector &&
67-
// Avoid this instantiation if the type is recursive
68-
negation<is_container_with_self_referential_mapped_type<T>>::value>> {
64+
struct is_comparable<T,
65+
enable_if_t<container_traits<T>::is_vector &&
66+
// Avoid this instantiation if the type is recursive
67+
negation<is_recursive_container<T>>::value>> {
6968
static constexpr const bool value = is_comparable<typename T::value_type>::value;
7069
};
7170

tests/test_stl_binders.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,20 +90,18 @@ struct RecursiveMap : std::map<int, RecursiveMap> {
9090
* pybind11::detail::is_container_with_self_referential_mapped_type, thus
9191
* manually telling pybind11 about the recursion.
9292
*/
93-
struct MutuallyRecursiveContainerPairA;
94-
struct MutuallyRecursiveContainerPairB;
93+
struct MutuallyRecursiveContainerPairMV;
94+
struct MutuallyRecursiveContainerPairVM;
9595

96-
struct MutuallyRecursiveContainerPairA : std::map<int, MutuallyRecursiveContainerPairB> {};
97-
struct MutuallyRecursiveContainerPairB : std::vector<MutuallyRecursiveContainerPairA> {};
96+
struct MutuallyRecursiveContainerPairMV : std::map<int, MutuallyRecursiveContainerPairVM> {};
97+
struct MutuallyRecursiveContainerPairVM : std::vector<MutuallyRecursiveContainerPairMV> {};
9898

9999
namespace pybind11 {
100100
namespace detail {
101-
template <>
102-
struct is_container_with_self_referential_mapped_type<MutuallyRecursiveContainerPairB>
103-
: std::true_type {};
104-
template <>
105-
struct is_container_with_self_referential_mapped_type<MutuallyRecursiveContainerPairA>
106-
: std::true_type {};
101+
template <typename SFINAE>
102+
struct is_recursive_container<MutuallyRecursiveContainerPairMV, SFINAE> : std::true_type {};
103+
template <typename SFINAE>
104+
struct is_recursive_container<MutuallyRecursiveContainerPairVM, SFINAE> : std::true_type {};
107105
} // namespace detail
108106
} // namespace pybind11
109107

@@ -169,8 +167,8 @@ TEST_SUBMODULE(stl_binders, m) {
169167
// Bind recursive container types
170168
py::bind_vector<RecursiveVector>(m, "RecursiveVector");
171169
py::bind_map<RecursiveMap>(m, "RecursiveMap");
172-
py::bind_map<MutuallyRecursiveContainerPairA>(m, "MutuallyRecursiveContainerPairA");
173-
py::bind_vector<MutuallyRecursiveContainerPairB>(m, "MutuallyRecursiveContainerPairB");
170+
py::bind_map<MutuallyRecursiveContainerPairMV>(m, "MutuallyRecursiveContainerPairMV");
171+
py::bind_vector<MutuallyRecursiveContainerPairVM>(m, "MutuallyRecursiveContainerPairVM");
174172

175173
// The rest depends on numpy:
176174
try {

0 commit comments

Comments
 (0)