Coverage Report

Created: 2023-09-25 06:40

/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