Skip to content

Commit 39f8aa2

Browse files
Iterator utilities
1 parent f79efdb commit 39f8aa2

File tree

5 files changed

+338
-4
lines changed

5 files changed

+338
-4
lines changed

src/bsoncxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ set_dist_list(src_bsoncxx_include_DIST
5656
bsoncxx/v_noabi/bsoncxx/exception/exception.hpp
5757
bsoncxx/v_noabi/bsoncxx/json.hpp
5858
bsoncxx/v_noabi/bsoncxx/oid.hpp
59+
bsoncxx/v_noabi/bsoncxx/stdx/iterator.hpp
5960
bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp
6061
bsoncxx/v_noabi/bsoncxx/stdx/optional.hpp
6162
bsoncxx/v_noabi/bsoncxx/stdx/operators.hpp
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
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>

src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ DECL_ALIAS(make_unsigned);
3333
DECL_ALIAS(remove_reference);
3434
DECL_ALIAS(remove_const);
3535
DECL_ALIAS(remove_volatile);
36+
DECL_ALIAS(remove_pointer);
3637
DECL_ALIAS(remove_cv);
3738
DECL_ALIAS(add_pointer);
3839
DECL_ALIAS(add_const);
@@ -56,13 +57,25 @@ using remove_cvref_t = remove_cv_t<remove_reference_t<T>>;
5657
template <typename T>
5758
using const_reference_t = add_lvalue_reference_t<const remove_cvref_t<T>>;
5859

60+
// Workaround for CWG issue 1558
61+
template <typename...>
62+
struct _just_void_ {
63+
using type = void;
64+
};
5965
/**
6066
* @brief A "do-nothing" alias template that always evaluates to void
6167
*
6268
* @tparam Ts Zero or more type arguments, all discarded
6369
*/
6470
template <typename... Ts>
65-
using void_t = void;
71+
using void_t =
72+
#if defined(_MSC_VER) && _MSC_VER < 1910
73+
// Old MSVC requires that the type parameters actually be "used" to trigger SFINAE at caller.
74+
// This was resolved by CWG issue 1558
75+
typename _just_void_<Ts...>::type;
76+
#else
77+
void;
78+
#endif
6679

6780
/**
6881
* @brief Alias for integral_constant<bool, B>
@@ -95,7 +108,7 @@ std::true_type is_detected_f(mp_list<Args...>*);
95108

96109
// Failure case for is_detected. Because this function takes an elipsis, this is
97110
// less preferred than the above overload that accepts a pointer type directly.
98-
template <bsoncxx_ttparam Oper, typename... Args>
111+
template <bsoncxx_ttparam Oper>
99112
std::false_type is_detected_f(...);
100113

101114
// Provides the detected_or impl
@@ -117,6 +130,18 @@ struct detection<true> {
117130
using f = Oper<Args...>;
118131
};
119132

133+
/// Workaround: MSVC 14.0 forgets whether a type resulting from the evaluation
134+
/// of a template-template parameter to an alias template is a reference.
135+
template <typename Dflt, typename Void, bsoncxx_ttparam Oper, typename... Args>
136+
struct vc140_detection {
137+
using type = Dflt;
138+
};
139+
140+
template <typename Dflt, bsoncxx_ttparam Oper, typename... Args>
141+
struct vc140_detection<Dflt, void_t<Oper<Args...>>, Oper, Args...> {
142+
using type = Oper<Args...>;
143+
};
144+
120145
} // namespace impl_detection
121146

122147
/**
@@ -149,8 +174,14 @@ struct is_detected
149174
* @tparam Args The arguments to give to the Oper metafunction
150175
*/
151176
template <typename Dflt, bsoncxx_ttparam Oper, typename... Args>
152-
using detected_or = typename impl_detection::detection<
153-
is_detected<Oper, Args...>::value>::template f<Dflt, Oper, Args...>;
177+
using detected_or =
178+
#if defined(_MSC_VER) && _MSC_VER < 1910
179+
typename impl_detection::vc140_detection<Dflt, void, Oper, Args...>::type
180+
#else
181+
typename impl_detection::detection<
182+
is_detected<Oper, Args...>::value>::template f<Dflt, Oper, Args...>
183+
#endif
184+
;
154185

155186
/**
156187
* @brief If Oper<Args...> evaluates to a type, yields that type. Otherwise, yields

src/bsoncxx/test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ add_executable(test_bson
3939
view_or_value.cpp
4040
make_unique.test.cpp
4141
type_traits.test.cpp
42+
iterator.test.cpp
4243
)
4344

4445
# Common target properties for test executables.
@@ -123,4 +124,5 @@ set_dist_list(src_bsoncxx_test_DIST
123124
view_or_value.cpp
124125
type_traits.test.cpp
125126
make_unique.test.cpp
127+
iterator.test.cpp
126128
)

0 commit comments

Comments
 (0)