/src/cppitertools/internal/iterbase.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | #ifndef ITERBASE_HPP_ |
2 | | #define ITERBASE_HPP_ |
3 | | |
4 | | // This file consists of utilities used for the generic nature of the |
5 | | // iterable wrapper classes. As such, the contents of this file should be |
6 | | // considered UNDOCUMENTED and is subject to change without warning. This |
7 | | // also applies to the name of the file. No user code should include |
8 | | // this file directly. |
9 | | |
10 | | #include <cassert> |
11 | | #include <cstddef> |
12 | | #include <functional> |
13 | | #include <iterator> |
14 | | #include <optional> |
15 | | #include <tuple> |
16 | | #include <type_traits> |
17 | | #include <utility> |
18 | | |
19 | | // see gcc bug 87651 |
20 | | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87651 |
21 | | #ifdef __GNUC__ |
22 | | #define NO_GCC_FRIEND_ERROR __GNUC__ < 8 |
23 | | #else |
24 | | #define NO_GCC_FRIEND_ERROR 1 |
25 | | #endif |
26 | | |
27 | | namespace iter { |
28 | | namespace impl { |
29 | | namespace get_iters { |
30 | | // begin() for C arrays |
31 | | template <typename T, std::size_t N> |
32 | | T* get_begin_impl(T (&array)[N], int) { |
33 | | return array; |
34 | | } |
35 | | |
36 | | // Prefer member begin(). |
37 | | template <typename T, typename I = decltype(std::declval<T&>().begin())> |
38 | 6.17k | I get_begin_impl(T& r, int) { |
39 | 6.17k | return r.begin(); |
40 | 6.17k | } std::__1::__wrap_iter<char const*> iter::impl::get_iters::get_begin_impl<std::__1::vector<char, std::__1::allocator<char> > const, std::__1::__wrap_iter<char const*> >(std::__1::vector<char, std::__1::allocator<char> > const&, int) Line | Count | Source | 38 | 1.48k | I get_begin_impl(T& r, int) { | 39 | 1.48k | return r.begin(); | 40 | 1.48k | } |
std::__1::__wrap_iter<int*> iter::impl::get_iters::get_begin_impl<std::__1::vector<int, std::__1::allocator<int> >, std::__1::__wrap_iter<int*> >(std::__1::vector<int, std::__1::allocator<int> >&, int) Line | Count | Source | 38 | 1.48k | I get_begin_impl(T& r, int) { | 39 | 1.48k | return r.begin(); | 40 | 1.48k | } |
std::__1::__bit_iterator<std::__1::vector<bool, std::__1::allocator<bool> >, false, 0ul> iter::impl::get_iters::get_begin_impl<std::__1::vector<bool, std::__1::allocator<bool> >, std::__1::__bit_iterator<std::__1::vector<bool, std::__1::allocator<bool> >, false, 0ul> >(std::__1::vector<bool, std::__1::allocator<bool> >&, int) Line | Count | Source | 38 | 990 | I get_begin_impl(T& r, int) { | 39 | 990 | return r.begin(); | 40 | 990 | } |
std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> iter::impl::get_iters::get_begin_impl<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, int) Line | Count | Source | 38 | 495 | I get_begin_impl(T& r, int) { | 39 | 495 | return r.begin(); | 40 | 495 | } |
std::__1::__wrap_iter<char*> iter::impl::get_iters::get_begin_impl<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__wrap_iter<char*> >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, int) Line | Count | Source | 38 | 1.72k | I get_begin_impl(T& r, int) { | 39 | 1.72k | return r.begin(); | 40 | 1.72k | } |
|
41 | | |
42 | | // Use ADL otherwises. |
43 | | template <typename T, typename I = decltype(begin(std::declval<T&>()))> |
44 | | I get_begin_impl(T& r, long) { |
45 | | return begin(r); |
46 | | } |
47 | | |
48 | | template <typename T> |
49 | 6.17k | auto get_begin(T& t) -> decltype(get_begin_impl(std::declval<T&>(), 42)) { |
50 | 6.17k | return get_begin_impl(t, 42); |
51 | 6.17k | } _ZN4iter4impl9get_iters9get_beginIKNSt3__16vectorIcNS3_9allocatorIcEEEEEEDTcl14get_begin_implclsr3stdE7declvalIRT_EELi42EEESA_ Line | Count | Source | 49 | 1.48k | auto get_begin(T& t) -> decltype(get_begin_impl(std::declval<T&>(), 42)) { | 50 | 1.48k | return get_begin_impl(t, 42); | 51 | 1.48k | } |
_ZN4iter4impl9get_iters9get_beginINSt3__16vectorIiNS3_9allocatorIiEEEEEEDTcl14get_begin_implclsr3stdE7declvalIRT_EELi42EEES9_ Line | Count | Source | 49 | 1.48k | auto get_begin(T& t) -> decltype(get_begin_impl(std::declval<T&>(), 42)) { | 50 | 1.48k | return get_begin_impl(t, 42); | 51 | 1.48k | } |
_ZN4iter4impl9get_iters9get_beginINSt3__16vectorIbNS3_9allocatorIbEEEEEEDTcl14get_begin_implclsr3stdE7declvalIRT_EELi42EEES9_ Line | Count | Source | 49 | 990 | auto get_begin(T& t) -> decltype(get_begin_impl(std::declval<T&>(), 42)) { | 50 | 990 | return get_begin_impl(t, 42); | 51 | 990 | } |
_ZN4iter4impl9get_iters9get_beginINSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEEEEDTcl14get_begin_implclsr3stdE7declvalIRT_EELi42EEESE_ Line | Count | Source | 49 | 495 | auto get_begin(T& t) -> decltype(get_begin_impl(std::declval<T&>(), 42)) { | 50 | 495 | return get_begin_impl(t, 42); | 51 | 495 | } |
_ZN4iter4impl9get_iters9get_beginINSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEDTcl14get_begin_implclsr3stdE7declvalIRT_EELi42EEESB_ Line | Count | Source | 49 | 1.72k | auto get_begin(T& t) -> decltype(get_begin_impl(std::declval<T&>(), 42)) { | 50 | 1.72k | return get_begin_impl(t, 42); | 51 | 1.72k | } |
|
52 | | |
53 | | // end() for C arrays |
54 | | template <typename T, std::size_t N> |
55 | | T* get_end_impl(T (&array)[N], int) { |
56 | | return array + N; |
57 | | } |
58 | | |
59 | | // Prefer member end(). |
60 | | template <typename T, typename I = decltype(std::declval<T&>().end())> |
61 | 329k | I get_end_impl(T& r, int) { |
62 | 329k | return r.end(); |
63 | 329k | } std::__1::__wrap_iter<char const*> iter::impl::get_iters::get_end_impl<std::__1::vector<char, std::__1::allocator<char> > const, std::__1::__wrap_iter<char const*> >(std::__1::vector<char, std::__1::allocator<char> > const&, int) Line | Count | Source | 61 | 4.45k | I get_end_impl(T& r, int) { | 62 | 4.45k | return r.end(); | 63 | 4.45k | } |
std::__1::__wrap_iter<int*> iter::impl::get_iters::get_end_impl<std::__1::vector<int, std::__1::allocator<int> >, std::__1::__wrap_iter<int*> >(std::__1::vector<int, std::__1::allocator<int> >&, int) Line | Count | Source | 61 | 3.46k | I get_end_impl(T& r, int) { | 62 | 3.46k | return r.end(); | 63 | 3.46k | } |
std::__1::__bit_iterator<std::__1::vector<bool, std::__1::allocator<bool> >, false, 0ul> iter::impl::get_iters::get_end_impl<std::__1::vector<bool, std::__1::allocator<bool> >, std::__1::__bit_iterator<std::__1::vector<bool, std::__1::allocator<bool> >, false, 0ul> >(std::__1::vector<bool, std::__1::allocator<bool> >&, int) Line | Count | Source | 61 | 1.98k | I get_end_impl(T& r, int) { | 62 | 1.98k | return r.end(); | 63 | 1.98k | } |
std::__1::__wrap_iter<int const*> iter::impl::get_iters::get_end_impl<std::__1::vector<int, std::__1::allocator<int> > const, std::__1::__wrap_iter<int const*> >(std::__1::vector<int, std::__1::allocator<int> > const&, int) Line | Count | Source | 61 | 990 | I get_end_impl(T& r, int) { | 62 | 990 | return r.end(); | 63 | 990 | } |
std::__1::__bit_iterator<std::__1::vector<bool, std::__1::allocator<bool> >, true, 0ul> iter::impl::get_iters::get_end_impl<std::__1::vector<bool, std::__1::allocator<bool> > const, std::__1::__bit_iterator<std::__1::vector<bool, std::__1::allocator<bool> >, true, 0ul> >(std::__1::vector<bool, std::__1::allocator<bool> > const&, int) Line | Count | Source | 61 | 990 | I get_end_impl(T& r, int) { | 62 | 990 | return r.end(); | 63 | 990 | } |
std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> iter::impl::get_iters::get_end_impl<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, int) Line | Count | Source | 61 | 1.48k | I get_end_impl(T& r, int) { | 62 | 1.48k | return r.end(); | 63 | 1.48k | } |
std::__1::__wrap_iter<char*> iter::impl::get_iters::get_end_impl<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__wrap_iter<char*> >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, int) Line | Count | Source | 61 | 315k | I get_end_impl(T& r, int) { | 62 | 315k | return r.end(); | 63 | 315k | } |
|
64 | | |
65 | | // Use ADL otherwise. |
66 | | template <typename T, typename I = decltype(end(std::declval<T&>()))> |
67 | | I get_end_impl(T& r, long) { |
68 | | return end(r); |
69 | | } |
70 | | |
71 | | template <typename T> |
72 | 329k | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { |
73 | 329k | return get_end_impl(t, 42); |
74 | 329k | } _ZN4iter4impl9get_iters7get_endIKNSt3__16vectorIcNS3_9allocatorIcEEEEEEDTcl12get_end_implclsr3stdE7declvalIRT_EELi42EEESA_ Line | Count | Source | 72 | 4.45k | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { | 73 | 4.45k | return get_end_impl(t, 42); | 74 | 4.45k | } |
_ZN4iter4impl9get_iters7get_endINSt3__16vectorIiNS3_9allocatorIiEEEEEEDTcl12get_end_implclsr3stdE7declvalIRT_EELi42EEES9_ Line | Count | Source | 72 | 3.46k | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { | 73 | 3.46k | return get_end_impl(t, 42); | 74 | 3.46k | } |
_ZN4iter4impl9get_iters7get_endINSt3__16vectorIbNS3_9allocatorIbEEEEEEDTcl12get_end_implclsr3stdE7declvalIRT_EELi42EEES9_ Line | Count | Source | 72 | 1.98k | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { | 73 | 1.98k | return get_end_impl(t, 42); | 74 | 1.98k | } |
_ZN4iter4impl9get_iters7get_endIKNSt3__16vectorIiNS3_9allocatorIiEEEEEEDTcl12get_end_implclsr3stdE7declvalIRT_EELi42EEESA_ Line | Count | Source | 72 | 990 | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { | 73 | 990 | return get_end_impl(t, 42); | 74 | 990 | } |
_ZN4iter4impl9get_iters7get_endIKNSt3__16vectorIbNS3_9allocatorIbEEEEEEDTcl12get_end_implclsr3stdE7declvalIRT_EELi42EEESA_ Line | Count | Source | 72 | 990 | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { | 73 | 990 | return get_end_impl(t, 42); | 74 | 990 | } |
_ZN4iter4impl9get_iters7get_endINSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEEEEDTcl12get_end_implclsr3stdE7declvalIRT_EELi42EEESE_ Line | Count | Source | 72 | 1.48k | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { | 73 | 1.48k | return get_end_impl(t, 42); | 74 | 1.48k | } |
_ZN4iter4impl9get_iters7get_endINSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEDTcl12get_end_implclsr3stdE7declvalIRT_EELi42EEESB_ Line | Count | Source | 72 | 315k | auto get_end(T& t) -> decltype(get_end_impl(std::declval<T&>(), 42)) { | 73 | 315k | return get_end_impl(t, 42); | 74 | 315k | } |
|
75 | | } |
76 | | using get_iters::get_begin; |
77 | | using get_iters::get_end; |
78 | | |
79 | | template <typename T> |
80 | | struct type_is { |
81 | | using type = T; |
82 | | }; |
83 | | |
84 | | template <typename T> |
85 | | using AsConst = decltype(std::as_const(std::declval<T&>())); |
86 | | |
87 | | // iterator_type<C> is the type of C's iterator |
88 | | // TODO: See bug |
89 | | // https://developercommunity.visualstudio.com/content/problem/252157/sfinae-error-depends-on-name-of-template-parameter.html |
90 | | // for why we use T instead of Container. Should be |
91 | | // changed back to Container when that bug is fixed in |
92 | | // MSVC. |
93 | | template <typename T> |
94 | | using iterator_type = decltype(get_begin(std::declval<T&>())); |
95 | | |
96 | | // iterator_type<C> is the type of C's iterator |
97 | | template <typename Container> |
98 | | using const_iterator_type = decltype(get_begin( |
99 | | std::declval<const std::remove_reference_t<Container>&>())); |
100 | | |
101 | | // iterator_deref<C> is the type obtained by dereferencing an iterator |
102 | | // to an object of type C |
103 | | template <typename Container> |
104 | | using iterator_deref = decltype(*std::declval<iterator_type<Container>&>()); |
105 | | |
106 | | // const_iteator_deref is the type obtained through dereferencing |
107 | | // a const iterator& (note: not a const_iterator). ie: the result |
108 | | // of Container::iterator::operator*() const |
109 | | template <typename Container> |
110 | | using const_iterator_deref = |
111 | | decltype(*std::declval<const iterator_type<Container>&>()); |
112 | | |
113 | | // the type of dereferencing a const_iterator |
114 | | template <typename Container> |
115 | | using const_iterator_type_deref = |
116 | | decltype(*std::declval<const_iterator_type<Container>&>()); |
117 | | |
118 | | template <typename Container> |
119 | | using iterator_traits_deref = |
120 | | std::remove_reference_t<iterator_deref<Container>>; |
121 | | |
122 | | template <typename T, typename = void> |
123 | | struct IsIterable : std::false_type {}; |
124 | | |
125 | | // Assuming that if a type works with begin, it is an iterable. |
126 | | template <typename T> |
127 | | struct IsIterable<T, std::void_t<iterator_type<T>>> : std::true_type {}; |
128 | | |
129 | | template <typename T> |
130 | | constexpr bool is_iterable = IsIterable<T>::value; |
131 | | |
132 | | struct Identity { |
133 | | template <typename T> |
134 | | const T& operator()(const T& t) const { |
135 | | return t; |
136 | | } |
137 | | }; |
138 | | |
139 | | namespace detail { |
140 | | template <typename T, typename = void> |
141 | | struct ArrowHelper { |
142 | | using type = void; |
143 | | void operator()(T&) const noexcept {} |
144 | | }; |
145 | | |
146 | | template <typename T> |
147 | | struct ArrowHelper<T*, void> { |
148 | | using type = T*; |
149 | | constexpr type operator()(T* t) const noexcept { |
150 | | return t; |
151 | | } |
152 | | }; |
153 | | |
154 | | template <typename T> |
155 | | struct ArrowHelper<T, |
156 | | std::void_t<decltype(std::declval<T&>().operator->())>> { |
157 | | using type = decltype(std::declval<T&>().operator->()); |
158 | | type operator()(T& t) const { |
159 | | return t.operator->(); |
160 | | } |
161 | | }; |
162 | | |
163 | | template <typename T> |
164 | | using arrow = typename detail::ArrowHelper<T>::type; |
165 | | } |
166 | | |
167 | | // type of C::iterator::operator->, also works with pointers |
168 | | // void if the iterator has no operator-> |
169 | | template <typename C> |
170 | | using iterator_arrow = detail::arrow<iterator_type<C>>; |
171 | | |
172 | | // applys the -> operator to an object, if the object is a pointer, |
173 | | // it returns the pointer |
174 | | template <typename T> |
175 | | detail::arrow<T> apply_arrow(T& t) { |
176 | | return detail::ArrowHelper<T>{}(t); |
177 | | } |
178 | | |
179 | | // For iterators that have an operator* which returns a value |
180 | | // they can return this type from their operator-> instead, which will |
181 | | // wrap an object and allow it to be used with arrow |
182 | | template <typename T> |
183 | | class ArrowProxy { |
184 | | private: |
185 | | using TPlain = typename std::remove_reference<T>::type; |
186 | | T obj; |
187 | | |
188 | | public: |
189 | | constexpr ArrowProxy(T&& in_obj) : obj(std::forward<T>(in_obj)) {} |
190 | | |
191 | | TPlain* operator->() { |
192 | | return &obj; |
193 | | } |
194 | | }; |
195 | | |
196 | | template <typename, typename = void> |
197 | | struct is_random_access_iter : std::false_type {}; |
198 | | |
199 | | template <typename T> |
200 | | struct is_random_access_iter<T, |
201 | | std::enable_if_t< |
202 | | std::is_same<typename std::iterator_traits<T>::iterator_category, |
203 | | std::random_access_iterator_tag>::value>> : std::true_type {}; |
204 | | |
205 | | template <typename T> |
206 | | using has_random_access_iter = is_random_access_iter<iterator_type<T>>; |
207 | | // because std::advance assumes a lot and is actually smart, I need a dumb |
208 | | |
209 | | // version that will work with most things |
210 | | template <typename InputIt, typename Distance = std::size_t> |
211 | 345k | void dumb_advance_unsafe(InputIt& iter, Distance distance) { |
212 | 461k | for (Distance i(0); i < distance; ++i) { |
213 | 116k | ++iter; |
214 | 116k | } |
215 | 345k | } void iter::impl::dumb_advance_unsafe<std::__1::__wrap_iter<char*>, long>(std::__1::__wrap_iter<char*>&, long) Line | Count | Source | 211 | 312k | void dumb_advance_unsafe(InputIt& iter, Distance distance) { | 212 | 345k | for (Distance i(0); i < distance; ++i) { | 213 | 33.5k | ++iter; | 214 | 33.5k | } | 215 | 312k | } |
void iter::impl::dumb_advance_unsafe<std::__1::__wrap_iter<char*>, unsigned long>(std::__1::__wrap_iter<char*>&, unsigned long) Line | Count | Source | 211 | 33.5k | void dumb_advance_unsafe(InputIt& iter, Distance distance) { | 212 | 116k | for (Distance i(0); i < distance; ++i) { | 213 | 82.5k | ++iter; | 214 | 82.5k | } | 215 | 33.5k | } |
|
216 | | |
217 | | template <typename Iter, typename EndIter, typename Distance> |
218 | | void dumb_advance_impl( |
219 | | Iter& iter, const EndIter& end, Distance distance, std::false_type) { |
220 | | for (Distance i(0); i < distance && iter != end; ++i) { |
221 | | ++iter; |
222 | | } |
223 | | } |
224 | | |
225 | | template <typename Iter, typename EndIter, typename Distance> |
226 | | void dumb_advance_impl( |
227 | 1.72k | Iter& iter, const EndIter& end, Distance distance, std::true_type) { |
228 | 1.72k | if (static_cast<Distance>(end - iter) < distance) { |
229 | 0 | iter = end; |
230 | 1.72k | } else { |
231 | 1.72k | iter += distance; |
232 | 1.72k | } |
233 | 1.72k | } |
234 | | |
235 | | // iter will not be incremented past end |
236 | | template <typename Iter, typename EndIter, typename Distance = std::size_t> |
237 | 1.72k | void dumb_advance(Iter& iter, const EndIter& end, Distance distance) { |
238 | 1.72k | dumb_advance_impl(iter, end, distance, is_random_access_iter<Iter>{}); |
239 | 1.72k | } |
240 | | |
241 | | template <typename ForwardIt, typename Distance = std::size_t> |
242 | 345k | ForwardIt dumb_next(ForwardIt it, Distance distance = 1) { |
243 | 345k | dumb_advance_unsafe(it, distance); |
244 | 345k | return it; |
245 | 345k | } std::__1::__wrap_iter<char*> iter::impl::dumb_next<std::__1::__wrap_iter<char*>, long>(std::__1::__wrap_iter<char*>, long) Line | Count | Source | 242 | 312k | ForwardIt dumb_next(ForwardIt it, Distance distance = 1) { | 243 | 312k | dumb_advance_unsafe(it, distance); | 244 | 312k | return it; | 245 | 312k | } |
std::__1::__wrap_iter<char*> iter::impl::dumb_next<std::__1::__wrap_iter<char*>, unsigned long>(std::__1::__wrap_iter<char*>, unsigned long) Line | Count | Source | 242 | 33.5k | ForwardIt dumb_next(ForwardIt it, Distance distance = 1) { | 243 | 33.5k | dumb_advance_unsafe(it, distance); | 244 | 33.5k | return it; | 245 | 33.5k | } |
|
246 | | |
247 | | template <typename ForwardIt, typename Distance = std::size_t> |
248 | | ForwardIt dumb_next( |
249 | | ForwardIt it, const ForwardIt& end, Distance distance = 1) { |
250 | | dumb_advance(it, end, distance); |
251 | | return it; |
252 | | } |
253 | | |
254 | | template <typename Container, typename Distance = std::size_t> |
255 | | Distance dumb_size(Container&& container) { |
256 | | Distance d{0}; |
257 | | auto end_it = get_end(container); |
258 | | for (auto it = get_begin(container); it != end_it; ++it) { |
259 | | ++d; |
260 | | } |
261 | | return d; |
262 | | } |
263 | | |
264 | | template <typename... Ts> |
265 | | struct are_same : std::true_type {}; |
266 | | |
267 | | template <typename T, typename U, typename... Ts> |
268 | | struct are_same<T, U, Ts...> |
269 | | : std::integral_constant<bool, |
270 | | std::is_same<T, U>::value && are_same<T, Ts...>::value> {}; |
271 | | |
272 | | // DerefHolder holds the value gotten from an iterator dereference |
273 | | // if the iterate dereferences to an lvalue references, a pointer to the |
274 | | // element is stored |
275 | | // if it does not, a value is stored instead |
276 | | // get() returns a reference to the held item |
277 | | // get_ptr() returns a pointer to the held item |
278 | | // reset() replaces the currently held item |
279 | | template <typename T> |
280 | | class DerefHolder { |
281 | | private: |
282 | | static_assert(!std::is_lvalue_reference<T>::value, |
283 | | "Non-lvalue-ref specialization used for lvalue ref type"); |
284 | | // it could still be an rvalue reference |
285 | | using TPlain = std::remove_reference_t<T>; |
286 | | |
287 | | std::optional<TPlain> item_p_; |
288 | | |
289 | | public: |
290 | | using reference = TPlain&; |
291 | | using pointer = TPlain*; |
292 | | |
293 | | static constexpr bool stores_value = true; |
294 | | |
295 | | DerefHolder() = default; |
296 | | |
297 | | reference get() { |
298 | | assert(item_p_.has_value()); |
299 | | return *item_p_; |
300 | | } |
301 | | |
302 | | pointer get_ptr() { |
303 | | assert(item_p_.has_value()); |
304 | | return &item_p_.value(); |
305 | | } |
306 | | |
307 | | void reset(T&& item) { |
308 | | item_p_.emplace(std::move(item)); |
309 | | } |
310 | | |
311 | | explicit operator bool() const { |
312 | | return static_cast<bool>(item_p_); |
313 | | } |
314 | | }; |
315 | | |
316 | | // Specialization for when T is an lvalue ref |
317 | | template <typename T> |
318 | | class DerefHolder<T&> { |
319 | | public: |
320 | | using reference = T&; |
321 | | using pointer = T*; |
322 | | |
323 | | private: |
324 | | pointer item_p_{}; |
325 | | |
326 | | public: |
327 | | static constexpr bool stores_value = false; |
328 | | |
329 | 990 | DerefHolder() = default; |
330 | | |
331 | 16.9k | reference get() { |
332 | 16.9k | assert(item_p_); |
333 | 0 | return *item_p_; |
334 | 16.9k | } |
335 | | |
336 | | pointer get_ptr() { |
337 | | assert(item_p_); |
338 | | return item_p_; |
339 | | } |
340 | | |
341 | 12.0k | void reset(reference item) { |
342 | 12.0k | item_p_ = &item; |
343 | 12.0k | } |
344 | | |
345 | | explicit operator bool() const { |
346 | | return item_p_ != nullptr; |
347 | | } |
348 | | }; |
349 | | |
350 | | // allows f(x) to be 'called' as x | f |
351 | | // let the record show I dislike adding yet another syntactical mess to |
352 | | // this clown car of a language. |
353 | | template <typename ItTool> |
354 | | struct Pipeable { |
355 | | template <typename T> |
356 | | friend decltype(auto) operator|(T&& x, const Pipeable& p) { |
357 | | return static_cast<const ItTool&>(p)(std::forward<T>(x)); |
358 | | } |
359 | | }; |
360 | | |
361 | | // Pipeable Callable generator, where ItImpl is templated on the first |
362 | | // argument to the call. |
363 | | template <template <typename> class ItImpl> |
364 | | struct IterToolFn : Pipeable<IterToolFn<ItImpl>> { |
365 | | template <typename T, typename... Ts> |
366 | 495 | ItImpl<T> operator()(T&& t, Ts... ts) const { |
367 | 495 | return {std::forward<T>(t), std::move(ts)...}; |
368 | 495 | } |
369 | | }; |
370 | | |
371 | | // Pipeable callable which allows binding of the first argument |
372 | | // f(a, b) is the same as b | f(a) |
373 | | template <typename F> |
374 | | struct PipeableAndBindFirst : Pipeable<F> { |
375 | | protected: |
376 | | template <typename T> |
377 | | struct FnPartial : Pipeable<FnPartial<T>> { |
378 | | mutable T stored_arg; |
379 | | constexpr FnPartial(T in_t) : stored_arg(in_t) {} |
380 | | |
381 | | template <typename Container> |
382 | | auto operator()(Container&& container) const { |
383 | | return F{}(stored_arg, std::forward<Container>(container)); |
384 | | } |
385 | | }; |
386 | | |
387 | | public: |
388 | | template <typename T, typename = std::enable_if_t<!is_iterable<T>>> |
389 | | FnPartial<std::decay_t<T>> operator()(T&& t) const { |
390 | | return {std::forward<T>(t)}; |
391 | | } |
392 | | }; |
393 | | |
394 | | // Pipeable callable which allows binding of the second argument |
395 | | // f(a, b) is the same as a | f(b) |
396 | | // f(a) with an iterable is the same as f(a, DefaultT{}) |
397 | | template <typename F, typename DefaultT> |
398 | | struct PipeableAndBindOptionalSecond : Pipeable<F> { |
399 | | protected: |
400 | | template <typename T> |
401 | | struct FnPartial : Pipeable<FnPartial<T>> { |
402 | | mutable T stored_arg; |
403 | | constexpr FnPartial(T in_t) : stored_arg(in_t) {} |
404 | | |
405 | | template <typename Container> |
406 | | auto operator()(Container&& container) const { |
407 | | return F{}(std::forward<Container>(container), stored_arg); |
408 | | } |
409 | | }; |
410 | | |
411 | | public: |
412 | | template <typename T, typename = std::enable_if_t<!is_iterable<T>>> |
413 | | FnPartial<std::decay_t<T>> operator()(T&& t) const { |
414 | | return {std::forward<T>(t)}; |
415 | | } |
416 | | |
417 | | template <typename Container, |
418 | | typename = std::enable_if_t<is_iterable<Container>>> |
419 | | auto operator()(Container&& container) const { |
420 | | return static_cast<const F&>(*this)( |
421 | | std::forward<Container>(container), DefaultT{}); |
422 | | } |
423 | | }; |
424 | | |
425 | | // This is a complicated class to generate a callable that can work: |
426 | | // (1) with just a single (iterable) passed, and DefaultT substituted |
427 | | // (2) with an iterable and a callable |
428 | | // (3) with just a callable, to have the iterable passed later via pipe |
429 | | template <template <typename, typename> class ItImpl, typename DefaultT> |
430 | | struct IterToolFnOptionalBindFirst |
431 | | : PipeableAndBindFirst<IterToolFnOptionalBindFirst<ItImpl, DefaultT>> { |
432 | | private: |
433 | | using Base = |
434 | | PipeableAndBindFirst<IterToolFnOptionalBindFirst<ItImpl, DefaultT>>; |
435 | | |
436 | | protected: |
437 | | template <typename Container> |
438 | | auto operator()(Container&& container, std::false_type) const { |
439 | | return static_cast<const Base&>(*this)( |
440 | | std::forward<Container>(container)); |
441 | | } |
442 | | |
443 | | template <typename Container> |
444 | | auto operator()(Container&& container, std::true_type) const { |
445 | | return (*this)(DefaultT{}, std::forward<Container>(container)); |
446 | | } |
447 | | |
448 | | public: |
449 | | template <typename T> |
450 | | auto operator()(T&& t) const { |
451 | | return (*this)(std::forward<T>(t), IsIterable<T>{}); |
452 | | } |
453 | | |
454 | | template <typename T, typename Container, |
455 | | typename = std::enable_if_t<is_iterable<Container>>> |
456 | | ItImpl<T, Container> operator()(T func, Container&& container) const { |
457 | | return {std::move(func), std::forward<Container>(container)}; |
458 | | } |
459 | | }; |
460 | | |
461 | | template <template <typename, typename> class ItImpl, typename DefaultT> |
462 | | struct IterToolFnOptionalBindSecond |
463 | | : Pipeable<IterToolFnOptionalBindSecond<ItImpl, DefaultT>> { |
464 | | private: |
465 | | // T is whatever is being held for later use |
466 | | template <typename T> |
467 | | struct FnPartial : Pipeable<FnPartial<T>> { |
468 | | mutable T stored_arg; |
469 | | constexpr FnPartial(T in_t) : stored_arg(in_t) {} |
470 | | |
471 | | template <typename Container> |
472 | | auto operator()(Container&& container) const { |
473 | | return IterToolFnOptionalBindSecond{}( |
474 | | std::forward<Container>(container), stored_arg); |
475 | | } |
476 | | }; |
477 | | |
478 | | public: |
479 | | template <typename Container, typename T> |
480 | 495 | ItImpl<Container, T> operator()(Container&& container, T func) const { |
481 | 495 | return {std::forward<Container>(container), std::move(func)}; |
482 | 495 | } |
483 | | |
484 | | template <typename T, typename = std::enable_if_t<!is_iterable<T>>> |
485 | | FnPartial<std::decay_t<T>> operator()(T&& func) const { |
486 | | return {std::forward<T>(func)}; |
487 | | } |
488 | | |
489 | | template <typename Container, |
490 | | typename = std::enable_if_t<is_iterable<Container>>> |
491 | | auto operator()(Container&& container) const { |
492 | | return (*this)(std::forward<Container>(container), DefaultT{}); |
493 | | } |
494 | | }; |
495 | | |
496 | | template <template <typename> class ItImpl> |
497 | | struct IterToolFnBindSizeTSecond { // NOTE not pipeable |
498 | | private: |
499 | | using Size = std::size_t; |
500 | | struct FnPartial : Pipeable<FnPartial> { |
501 | | Size sz{}; |
502 | | constexpr FnPartial(Size in_sz) : sz{in_sz} {} |
503 | | |
504 | | template <typename Container> |
505 | | auto operator()(Container&& container) const { |
506 | | return IterToolFnBindSizeTSecond{}( |
507 | | std::forward<Container>(container), sz); |
508 | | } |
509 | | }; |
510 | | |
511 | | public: |
512 | | FnPartial operator()(Size sz) const { |
513 | | return {sz}; |
514 | | } |
515 | | |
516 | | template <typename Container, |
517 | | typename = std::enable_if_t<is_iterable<Container>>> |
518 | 495 | ItImpl<Container> operator()(Container&& container, Size sz) const { |
519 | 495 | return {std::forward<Container>(container), sz}; |
520 | 495 | } |
521 | | }; |
522 | | } |
523 | | } |
524 | | |
525 | | #endif |