|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <initializer_list> |
| 4 | +#include <iterator> |
| 5 | +#include <memory> |
| 6 | +#include <type_traits> |
| 7 | +#include <utility> |
| 8 | +#include <vector> |
| 9 | +#if defined(__has_include) |
| 10 | +#if __has_include(<version>) |
| 11 | +#include <version> |
| 12 | +#endif |
| 13 | +#endif |
| 14 | + |
| 15 | +#include "./operators.hpp" |
| 16 | +#include "./type_traits.hpp" |
| 17 | + |
| 18 | +#include <bsoncxx/config/prelude.hpp> |
| 19 | + |
| 20 | +namespace bsoncxx { |
| 21 | +inline namespace v_noabi { |
| 22 | +namespace detail { |
| 23 | + |
| 24 | +/** |
| 25 | + * @brief Backport of std::pointer_traits with the addition of to_address() for pointers |
| 26 | + */ |
| 27 | +template <typename T> |
| 28 | +struct pointer_traits : std::pointer_traits<T> {}; |
| 29 | + |
| 30 | +template <typename T> |
| 31 | +struct pointer_traits<T*> : std::pointer_traits<T*> { |
| 32 | + using typename std::pointer_traits<T*>::pointer; |
| 33 | + using typename std::pointer_traits<T*>::element_type; |
| 34 | + |
| 35 | + static constexpr pointer to_address(pointer p) noexcept { |
| 36 | + return p; |
| 37 | + } |
| 38 | +}; |
| 39 | + |
| 40 | +struct _to_address_fn { |
| 41 | + struct impl { |
| 42 | + template <typename FancyPointer> |
| 43 | + static constexpr auto x(FancyPointer fp, rank<3>) |
| 44 | + bsoncxx_returns(pointer_traits<FancyPointer>::to_address(fp)); |
| 45 | + |
| 46 | + template <typename Iterator> |
| 47 | + static constexpr auto x(Iterator iter, rank<2>) bsoncxx_returns(iter.operator->()); |
| 48 | + |
| 49 | + template <typename T> |
| 50 | + static constexpr auto x(T* p, rank<1>) bsoncxx_returns(p); |
| 51 | + }; |
| 52 | + |
| 53 | + template <typename Iterator> |
| 54 | + constexpr auto operator()(Iterator iter) const bsoncxx_returns(impl::x(iter, rank<10>{})); |
| 55 | +}; |
| 56 | + |
| 57 | +/** |
| 58 | + * @brief Convert an iterator or pointer-like type to a raw pointer. |
| 59 | + */ |
| 60 | +constexpr static _to_address_fn to_address; |
| 61 | + |
| 62 | +/** |
| 63 | + * @brief The type obtained by to_address() for the given object, if valid |
| 64 | + */ |
| 65 | +template <typename T> |
| 66 | +using to_address_t = decltype(to_address(std::declval<T&>())); |
| 67 | + |
| 68 | +/** |
| 69 | + * @brief The result of applying unary operator* to the given object, if valid |
| 70 | + */ |
| 71 | +template <typename I> |
| 72 | +using dereference_t = decltype(*std::declval<I>()); |
| 73 | + |
| 74 | +/** |
| 75 | + * @brief Detect a type that can be dereferenced (like a pointer) and the result |
| 76 | + * type is non-void |
| 77 | + */ |
| 78 | +template <typename I> |
| 79 | +struct is_dereferencable : conjunction<is_detected<dereference_t, I>, |
| 80 | + // Clang supports dereferencing void*, and we can't detect |
| 81 | + // that easily. Refuse if I is (cv-)void* |
| 82 | + negation<std::is_void<remove_pointer_t<I>>>, |
| 83 | + negation<std::is_void<detected_t<dereference_t, I>>>> {}; |
| 84 | + |
| 85 | +/** |
| 86 | + * @brief Obtain the value type of the given iterator. |
| 87 | + * |
| 88 | + * This is only a very rough approximation for our use cases. A more thorough |
| 89 | + * C++20 impl requiers additional traits |
| 90 | + */ |
| 91 | +template <typename Iter> |
| 92 | +using iter_value_t = |
| 93 | + requires_t<typename std::iterator_traits<Iter>::value_type, is_dereferencable<Iter>>; |
| 94 | + |
| 95 | +/** |
| 96 | + * @brief Obtain the reference type of the given iterator (unless that type is `void`) |
| 97 | + */ |
| 98 | +template <typename Iter> |
| 99 | +using iter_reference_t = requires_t<decltype(*std::declval<Iter&>()), is_dereferencable<Iter>>; |
| 100 | + |
| 101 | +/** |
| 102 | + * @brief Obtain the difference type for the given iterator |
| 103 | + */ |
| 104 | +template <typename Iter> |
| 105 | +using iter_difference_t = typename std::iterator_traits<Iter>::difference_type; |
| 106 | + |
| 107 | +template <typename Iter, typename = void> |
| 108 | +struct is_weakly_incrementable : std::false_type {}; |
| 109 | + |
| 110 | +template <typename Iter> |
| 111 | +struct is_weakly_incrementable< // |
| 112 | + Iter, |
| 113 | + requires_t<void, |
| 114 | + std::is_object<Iter>, |
| 115 | + std::is_assignable<Iter&, Iter>, |
| 116 | + is_detected<iter_difference_t, Iter>, |
| 117 | + true_t<decltype(++std::declval<Iter&>()), // |
| 118 | + decltype(std::declval<Iter&>()++)>, |
| 119 | + std::is_same<decltype(++std::declval<Iter&>()), Iter&>>> : std::true_type {}; |
| 120 | + |
| 121 | +/** |
| 122 | + * @brief Detect a type that may be used as an iterator |
| 123 | + */ |
| 124 | +template <typename T> |
| 125 | +struct is_iterator : conjunction<is_weakly_incrementable<T>, |
| 126 | + is_detected<iter_value_t, T>, |
| 127 | + is_detected<iter_reference_t, T>> {}; |
| 128 | + |
| 129 | +// We want contiguous_iterator_tag. We can't get the full functionality without |
| 130 | +// stdlib support, but we can get reasonable approximation for our purposes |
| 131 | +#if defined(__cpp_lib_ranges) |
| 132 | +using std::contiguous_iterator_tag; |
| 133 | +#else |
| 134 | +struct contiguous_iterator_tag : std::random_access_iterator_tag {}; |
| 135 | +#endif |
| 136 | + |
| 137 | +// Base case, use the iterator to get the actual traits from it. |
| 138 | +template <typename I, typename = void> |
| 139 | +struct ITER_TRAITS_impl { |
| 140 | + using type = I; |
| 141 | +}; |
| 142 | + |
| 143 | +// If std::iterator_traits<I> is not "the default" (which contains no difference_type), |
| 144 | +// then use std::iterator_traits. |
| 145 | +template <typename I> |
| 146 | +struct ITER_TRAITS_impl<I, void_t<typename std::iterator_traits<I>::difference_type>> { |
| 147 | + using type = std::iterator_traits<I>; |
| 148 | +}; |
| 149 | + |
| 150 | +template <typename I> |
| 151 | +using ITER_TRAITS = typename ITER_TRAITS_impl<I>::type; |
| 152 | + |
| 153 | +// Get the iterator concept tag from the given iterator-like type |
| 154 | +struct calc_iterator_concept { |
| 155 | + struct impl { |
| 156 | + template <typename I> |
| 157 | + static auto x(I*, rank<3>) bsoncxx_returns(contiguous_iterator_tag{}); |
| 158 | + template <typename I> |
| 159 | + static auto x(I, rank<2>) bsoncxx_returns(typename ITER_TRAITS<I>::iterator_concept{}); |
| 160 | + template <typename I> |
| 161 | + static auto x(I, rank<1>) bsoncxx_returns(typename ITER_TRAITS<I>::iterator_category{}); |
| 162 | + }; |
| 163 | + template <typename I> |
| 164 | + auto operator()(I) -> decltype(impl::x(I{}, rank<10>{})); |
| 165 | +}; |
| 166 | + |
| 167 | +/** |
| 168 | + * @brief Obtain the iterator concept/category tag type, if present. |
| 169 | + * |
| 170 | + * Without C++20 stdlib support, does not detect contiguous_iterator_tag except |
| 171 | + * for on raw pointers. |
| 172 | + */ |
| 173 | +template <typename I> |
| 174 | +using iterator_concept_t = decltype(calc_iterator_concept{}(I{})); |
| 175 | + |
| 176 | +template <typename Iter, typename Tag> |
| 177 | +struct is_iterator_kind |
| 178 | + : conjunction<is_iterator<Iter>, std::is_base_of<Tag, detected_t<iterator_concept_t, Iter>>> {}; |
| 179 | + |
| 180 | +template <typename I> |
| 181 | +struct is_input_iterator : is_iterator_kind<I, std::input_iterator_tag> {}; |
| 182 | + |
| 183 | +template <typename I> |
| 184 | +struct is_forwrd_iterator : is_iterator_kind<I, std::forward_iterator_tag> {}; |
| 185 | + |
| 186 | +template <typename I> |
| 187 | +struct is_bidirectional_iterator : is_iterator_kind<I, std::bidirectional_iterator_tag> {}; |
| 188 | + |
| 189 | +template <typename I> |
| 190 | +struct is_random_access_iterator : is_iterator_kind<I, std::random_access_iterator_tag> {}; |
| 191 | + |
| 192 | +template <typename I> |
| 193 | +struct is_contiguous_iterator : is_iterator_kind<I, contiguous_iterator_tag> {}; |
| 194 | + |
| 195 | +/** |
| 196 | + * @brief Detect if the type `Sentinel` is a range sentinel for iterator `Iter` |
| 197 | + */ |
| 198 | +template <typename Sentinel, typename Iter> |
| 199 | +struct is_sentinel_for : conjunction<is_equality_comparable<Sentinel, Iter>, is_iterator<Iter>> {}; |
| 200 | + |
| 201 | +template <typename Left, typename Right> |
| 202 | +using difference_t = decltype(std::declval<Left>() - std::declval<Right>()); |
| 203 | + |
| 204 | +/** |
| 205 | + * @brief Detect if the type `Sentinel` is a sentinel for `Iter` and subtraction |
| 206 | + * is defined between them. |
| 207 | + */ |
| 208 | +template <typename Sentinel, typename Iter> |
| 209 | +struct is_sized_sentinel_for |
| 210 | + : conjunction< |
| 211 | + is_sentinel_for<Sentinel, Iter>, |
| 212 | + std::is_convertible<detected_t<difference_t, Sentinel, Iter>, iter_difference_t<Iter>>, |
| 213 | + std::is_convertible<detected_t<difference_t, Iter, Sentinel>, iter_difference_t<Iter>>> { |
| 214 | +}; |
| 215 | + |
| 216 | +} // namespace detail |
| 217 | +} // namespace v_noabi |
| 218 | +} // namespace bsoncxx |
| 219 | + |
| 220 | +#include <bsoncxx/config/postlude.hpp> |
0 commit comments