Skip to content

Commit 71598fa

Browse files
authored
Merge pull request #30035 from rwgk/pywrapcc_merge_sh
git merge smart_holder
2 parents 1c97128 + 5f27016 commit 71598fa

File tree

7 files changed

+486
-34
lines changed

7 files changed

+486
-34
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ repos:
101101

102102
# Check static types with mypy
103103
- repo: https://github.com/pre-commit/mirrors-mypy
104-
rev: "v1.1.1"
104+
rev: "v1.2.0"
105105
hooks:
106106
- id: mypy
107107
args: []
@@ -144,7 +144,7 @@ repos:
144144

145145
# Clang format the codebase automatically
146146
- repo: https://github.com/pre-commit/mirrors-clang-format
147-
rev: "v16.0.0"
147+
rev: "v16.0.2"
148148
hooks:
149149
- id: clang-format
150150
types_or: [c++, c, cuda]

include/pybind11/detail/type_caster_base.h

Lines changed: 179 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -826,26 +826,179 @@ using movable_cast_op_type
826826
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
827827
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
828828

829-
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
830-
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
831-
template <typename T, typename SFINAE = void>
832-
struct is_copy_constructible : std::is_copy_constructible<T> {};
829+
// Does the container have a mapped type and is it recursive?
830+
// Implemented by specializations below.
831+
template <typename Container, typename SFINAE = void>
832+
struct container_mapped_type_traits {
833+
static constexpr bool has_mapped_type = false;
834+
static constexpr bool has_recursive_mapped_type = false;
835+
};
836+
837+
template <typename Container>
838+
struct container_mapped_type_traits<
839+
Container,
840+
typename std::enable_if<
841+
std::is_same<typename Container::mapped_type, Container>::value>::type> {
842+
static constexpr bool has_mapped_type = true;
843+
static constexpr bool has_recursive_mapped_type = true;
844+
};
845+
846+
template <typename Container>
847+
struct container_mapped_type_traits<
848+
Container,
849+
typename std::enable_if<
850+
negation<std::is_same<typename Container::mapped_type, Container>>::value>::type> {
851+
static constexpr bool has_mapped_type = true;
852+
static constexpr bool has_recursive_mapped_type = false;
853+
};
854+
855+
// Does the container have a value type and is it recursive?
856+
// Implemented by specializations below.
857+
template <typename Container, typename SFINAE = void>
858+
struct container_value_type_traits : std::false_type {
859+
static constexpr bool has_value_type = false;
860+
static constexpr bool has_recursive_value_type = false;
861+
};
862+
863+
template <typename Container>
864+
struct container_value_type_traits<
865+
Container,
866+
typename std::enable_if<
867+
std::is_same<typename Container::value_type, Container>::value>::type> {
868+
static constexpr bool has_value_type = true;
869+
static constexpr bool has_recursive_value_type = true;
870+
};
871+
872+
template <typename Container>
873+
struct container_value_type_traits<
874+
Container,
875+
typename std::enable_if<
876+
negation<std::is_same<typename Container::value_type, Container>>::value>::type> {
877+
static constexpr bool has_value_type = true;
878+
static constexpr bool has_recursive_value_type = false;
879+
};
880+
881+
/*
882+
* Tag to be used for representing the bottom of recursively defined types.
883+
* Define this tag so we don't have to use void.
884+
*/
885+
struct recursive_bottom {};
886+
887+
/*
888+
* Implementation detail of `recursive_container_traits` below.
889+
* `T` is the `value_type` of the container, which might need to be modified to
890+
* avoid recursive types and const types.
891+
*/
892+
template <typename T, bool is_this_a_map>
893+
struct impl_type_to_check_recursively {
894+
/*
895+
* If the container is recursive, then no further recursion should be done.
896+
*/
897+
using if_recursive = recursive_bottom;
898+
/*
899+
* Otherwise yield `T` unchanged.
900+
*/
901+
using if_not_recursive = T;
902+
};
903+
904+
/*
905+
* For pairs - only as value type of a map -, the first type should remove the `const`.
906+
* Also, if the map is recursive, then the recursive checking should consider
907+
* the first type only.
908+
*/
909+
template <typename A, typename B>
910+
struct impl_type_to_check_recursively<std::pair<A, B>, /* is_this_a_map = */ true> {
911+
using if_recursive = typename std::remove_const<A>::type;
912+
using if_not_recursive = std::pair<typename std::remove_const<A>::type, B>;
913+
};
833914

834-
template <typename T, typename SFINAE = void>
835-
struct is_move_constructible : std::is_move_constructible<T> {};
915+
/*
916+
* Implementation of `recursive_container_traits` below.
917+
*/
918+
template <typename Container, typename SFINAE = void>
919+
struct impl_recursive_container_traits {
920+
using type_to_check_recursively = recursive_bottom;
921+
};
836922

837-
// Specialization for types that appear to be copy constructible but also look like stl containers
838-
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
839-
// so, copy constructability depends on whether the value_type is copy constructible.
840923
template <typename Container>
841-
struct is_copy_constructible<
924+
struct impl_recursive_container_traits<
842925
Container,
843-
enable_if_t<
844-
all_of<std::is_copy_constructible<Container>,
845-
std::is_same<typename Container::value_type &, typename Container::reference>,
846-
// Avoid infinite recursion
847-
negation<std::is_same<Container, typename Container::value_type>>>::value>>
848-
: is_copy_constructible<typename Container::value_type> {};
926+
typename std::enable_if<container_value_type_traits<Container>::has_value_type>::type> {
927+
static constexpr bool is_recursive
928+
= container_mapped_type_traits<Container>::has_recursive_mapped_type
929+
|| container_value_type_traits<Container>::has_recursive_value_type;
930+
/*
931+
* This member dictates which type Pybind11 should check recursively in traits
932+
* such as `is_move_constructible`, `is_copy_constructible`, `is_move_assignable`, ...
933+
* Direct access to `value_type` should be avoided:
934+
* 1. `value_type` might recursively contain the type again
935+
* 2. `value_type` of STL map types is `std::pair<A const, B>`, the `const`
936+
* should be removed.
937+
*
938+
*/
939+
using type_to_check_recursively = typename std::conditional<
940+
is_recursive,
941+
typename impl_type_to_check_recursively<
942+
typename Container::value_type,
943+
container_mapped_type_traits<Container>::has_mapped_type>::if_recursive,
944+
typename impl_type_to_check_recursively<
945+
typename Container::value_type,
946+
container_mapped_type_traits<Container>::has_mapped_type>::if_not_recursive>::type;
947+
};
948+
949+
/*
950+
* This trait defines the `type_to_check_recursively` which is needed to properly
951+
* handle recursively defined traits such as `is_move_constructible` without going
952+
* into an infinite recursion.
953+
* Should be used instead of directly accessing the `value_type`.
954+
* It cancels the recursion by returning the `recursive_bottom` tag.
955+
*
956+
* The default definition of `type_to_check_recursively` is as follows:
957+
*
958+
* 1. By default, it is `recursive_bottom`, so that the recursion is canceled.
959+
* 2. If the type is non-recursive and defines a `value_type`, then the `value_type` is used.
960+
* If the `value_type` is a pair and a `mapped_type` is defined,
961+
* then the `const` is removed from the first type.
962+
* 3. If the type is recursive and `value_type` is not a pair, then `recursive_bottom` is returned.
963+
* 4. If the type is recursive and `value_type` is a pair and a `mapped_type` is defined,
964+
* then `const` is removed from the first type and the first type is returned.
965+
*
966+
* This behavior can be extended by the user as seen in test_stl_binders.cpp.
967+
*
968+
* This struct is exactly the same as impl_recursive_container_traits.
969+
* The duplication achieves that user-defined specializations don't compete
970+
* with internal specializations, but take precedence.
971+
*/
972+
template <typename Container, typename SFINAE = void>
973+
struct recursive_container_traits : impl_recursive_container_traits<Container> {};
974+
975+
template <typename T>
976+
struct is_move_constructible
977+
: all_of<std::is_move_constructible<T>,
978+
is_move_constructible<
979+
typename recursive_container_traits<T>::type_to_check_recursively>> {};
980+
981+
template <>
982+
struct is_move_constructible<recursive_bottom> : std::true_type {};
983+
984+
// Likewise for std::pair
985+
// (after C++17 it is mandatory that the move constructor not exist when the two types aren't
986+
// themselves move constructible, but this can not be relied upon when T1 or T2 are themselves
987+
// containers).
988+
template <typename T1, typename T2>
989+
struct is_move_constructible<std::pair<T1, T2>>
990+
: all_of<is_move_constructible<T1>, is_move_constructible<T2>> {};
991+
992+
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
993+
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
994+
template <typename T>
995+
struct is_copy_constructible
996+
: all_of<std::is_copy_constructible<T>,
997+
is_copy_constructible<
998+
typename recursive_container_traits<T>::type_to_check_recursively>> {};
999+
1000+
template <>
1001+
struct is_copy_constructible<recursive_bottom> : std::true_type {};
8491002

8501003
// Likewise for std::pair
8511004
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't
@@ -856,14 +1009,16 @@ struct is_copy_constructible<std::pair<T1, T2>>
8561009
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
8571010

8581011
// The same problems arise with std::is_copy_assignable, so we use the same workaround.
859-
template <typename T, typename SFINAE = void>
860-
struct is_copy_assignable : std::is_copy_assignable<T> {};
861-
template <typename Container>
862-
struct is_copy_assignable<Container,
863-
enable_if_t<all_of<std::is_copy_assignable<Container>,
864-
std::is_same<typename Container::value_type &,
865-
typename Container::reference>>::value>>
866-
: is_copy_assignable<typename Container::value_type> {};
1012+
template <typename T>
1013+
struct is_copy_assignable
1014+
: all_of<
1015+
std::is_copy_assignable<T>,
1016+
is_copy_assignable<typename recursive_container_traits<T>::type_to_check_recursively>> {
1017+
};
1018+
1019+
template <>
1020+
struct is_copy_assignable<recursive_bottom> : std::true_type {};
1021+
8671022
template <typename T1, typename T2>
8681023
struct is_copy_assignable<std::pair<T1, T2>>
8691024
: all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};

include/pybind11/stl_bind.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ 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<T, enable_if_t<container_traits<T>::is_vector>> {
65-
static constexpr const bool value = is_comparable<typename T::value_type>::value;
66-
};
64+
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>>
65+
: is_comparable<typename recursive_container_traits<T>::type_to_check_recursively> {};
66+
67+
template <>
68+
struct is_comparable<recursive_bottom> : std::true_type {};
6769

6870
/* For pairs, recursively check the two data types */
6971
template <typename T>

pybind11/setup_helpers.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
144144
self.cxx_std = cxx_std
145145

146146
cflags = []
147-
ldflags = []
148147
if WIN:
149148
cflags += ["/EHsc", "/bigobj"]
150149
else:
@@ -154,11 +153,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
154153
c_cpp_flags = shlex.split(env_cflags) + shlex.split(env_cppflags)
155154
if not any(opt.startswith("-g") for opt in c_cpp_flags):
156155
cflags += ["-g0"]
157-
if MACOS:
158-
cflags += ["-stdlib=libc++"]
159-
ldflags += ["-stdlib=libc++"]
160156
self._add_cflags(cflags)
161-
self._add_ldflags(ldflags)
162157

163158
@property
164159
def cxx_std(self) -> int:

0 commit comments

Comments
 (0)