@@ -691,26 +691,25 @@ template<typename... Iters> struct ZipTupleType {
691
691
using type = std::tuple<decltype (*declval<Iters>())...>;
692
692
};
693
693
694
- template <typename ZipType, typename ... Iters>
694
+ template <typename ZipType, typename ReferenceTupleType, typename ... Iters>
695
695
using zip_traits = iterator_facade_base<
696
696
ZipType,
697
697
std::common_type_t <
698
698
std::bidirectional_iterator_tag,
699
699
typename std::iterator_traits<Iters>::iterator_category...>,
700
700
// ^ TODO: Implement random access methods.
701
- typename ZipTupleType<Iters...>::type ,
701
+ ReferenceTupleType ,
702
702
typename std::iterator_traits<
703
703
std::tuple_element_t <0 , std::tuple<Iters...>>>::difference_type,
704
704
// ^ FIXME: This follows boost::make_zip_iterator's assumption that all
705
705
// inner iterators have the same difference_type. It would fail if, for
706
706
// instance, the second field's difference_type were non-numeric while the
707
707
// first is.
708
- typename ZipTupleType<Iters...>::type *,
709
- typename ZipTupleType<Iters...>::type>;
708
+ ReferenceTupleType *, ReferenceTupleType>;
710
709
711
- template <typename ZipType, typename ... Iters>
712
- struct zip_common : public zip_traits <ZipType, Iters...> {
713
- using Base = zip_traits<ZipType, Iters...>;
710
+ template <typename ZipType, typename ReferenceTupleType, typename ... Iters>
711
+ struct zip_common : public zip_traits <ZipType, ReferenceTupleType, Iters...> {
712
+ using Base = zip_traits<ZipType, ReferenceTupleType, Iters...>;
714
713
using IndexSequence = std::index_sequence_for<Iters...>;
715
714
using value_type = typename Base::value_type;
716
715
@@ -760,17 +759,22 @@ struct zip_common : public zip_traits<ZipType, Iters...> {
760
759
};
761
760
762
761
template <typename ... Iters>
763
- struct zip_first : zip_common<zip_first<Iters...>, Iters...> {
764
- using zip_common<zip_first, Iters...>::zip_common;
762
+ struct zip_first : zip_common<zip_first<Iters...>,
763
+ typename ZipTupleType<Iters...>::type, Iters...> {
764
+ using zip_common<zip_first, typename ZipTupleType<Iters...>::type,
765
+ Iters...>::zip_common;
765
766
766
767
bool operator ==(const zip_first &other) const {
767
768
return std::get<0 >(this ->iterators ) == std::get<0 >(other.iterators );
768
769
}
769
770
};
770
771
771
772
template <typename ... Iters>
772
- struct zip_shortest : zip_common<zip_shortest<Iters...>, Iters...> {
773
- using zip_common<zip_shortest, Iters...>::zip_common;
773
+ struct zip_shortest
774
+ : zip_common<zip_shortest<Iters...>, typename ZipTupleType<Iters...>::type,
775
+ Iters...> {
776
+ using zip_common<zip_shortest, typename ZipTupleType<Iters...>::type,
777
+ Iters...>::zip_common;
774
778
775
779
bool operator ==(const zip_shortest &other) const {
776
780
return any_iterator_equals (other, std::index_sequence_for<Iters...>{});
@@ -2168,113 +2172,195 @@ template <typename T> struct deref {
2168
2172
2169
2173
namespace detail {
2170
2174
2171
- template <typename R> class enumerator_iter ;
2175
+ // / Tuple-like type for `zip_enumerator` dereference.
2176
+ template <typename ... Refs> struct enumerator_result ;
2172
2177
2173
- template <typename R> struct result_pair {
2174
- using value_reference =
2175
- typename std::iterator_traits<IterOfRange<R>>::reference;
2176
-
2177
- friend class enumerator_iter <R>;
2178
-
2179
- result_pair (std::size_t Index, IterOfRange<R> Iter)
2180
- : Index(Index), Iter(Iter) {}
2181
-
2182
- std::size_t index () const { return Index; }
2183
- value_reference value () const { return *Iter; }
2184
-
2185
- private:
2186
- std::size_t Index = std::numeric_limits<std::size_t >::max();
2187
- IterOfRange<R> Iter;
2188
- };
2189
-
2190
- template <std::size_t i, typename R>
2191
- decltype (auto ) get(const result_pair<R> &Pair) {
2192
- static_assert (i < 2 );
2193
- if constexpr (i == 0 ) {
2194
- return Pair.index ();
2195
- } else {
2196
- return Pair.value ();
2197
- }
2198
- }
2199
-
2200
- template <typename R>
2201
- class enumerator_iter
2202
- : public iterator_facade_base<enumerator_iter<R>, std::forward_iterator_tag,
2203
- const result_pair<R>> {
2204
- using result_type = result_pair<R>;
2205
-
2206
- public:
2207
- explicit enumerator_iter (IterOfRange<R> EndIter)
2208
- : Result(std::numeric_limits<size_t >::max(), EndIter) {}
2209
-
2210
- enumerator_iter (std::size_t Index, IterOfRange<R> Iter)
2211
- : Result(Index, Iter) {}
2212
-
2213
- const result_type &operator *() const { return Result; }
2178
+ template <typename ... Iters>
2179
+ using EnumeratorTupleType = enumerator_result<decltype (*declval<Iters>())...>;
2180
+
2181
+ // / Zippy iterator that uses the second iterator for comparisons. For the
2182
+ // / increment to be safe, the second range has to be the shortest.
2183
+ // / Returns `enumerator_result` on dereference to provide `.index()` and
2184
+ // / `.value()` member functions.
2185
+ // / Note: Because the dereference operator returns `enumerator_result` as a
2186
+ // / value instead of a reference and does not strictly conform to the C++17's
2187
+ // / definition of forward iterator. However, it satisfies all the
2188
+ // / forward_iterator requirements that the `zip_common` and `zippy` depend on
2189
+ // / and fully conforms to the C++20 definition of forward iterator.
2190
+ // / This is similar to `std::vector<bool>::iterator` that returns bit reference
2191
+ // / wrappers on dereference.
2192
+ template <typename ... Iters>
2193
+ struct zip_enumerator : zip_common<zip_enumerator<Iters...>,
2194
+ EnumeratorTupleType<Iters...>, Iters...> {
2195
+ static_assert (sizeof ...(Iters) >= 2 , " Expected at least two iteratees" );
2196
+ using zip_common<zip_enumerator<Iters...>, EnumeratorTupleType<Iters...>,
2197
+ Iters...>::zip_common;
2214
2198
2215
- enumerator_iter &operator ++() {
2216
- assert (Result.Index != std::numeric_limits<size_t >::max ());
2217
- ++Result.Iter ;
2218
- ++Result.Index ;
2219
- return *this ;
2199
+ bool operator ==(const zip_enumerator &Other) const {
2200
+ return std::get<1 >(this ->iterators ) == std::get<1 >(Other.iterators );
2220
2201
}
2202
+ };
2221
2203
2222
- bool operator ==(const enumerator_iter &RHS) const {
2223
- // Don't compare indices here, only iterators. It's possible for an end
2224
- // iterator to have different indices depending on whether it was created
2225
- // by calling std::end() versus incrementing a valid iterator.
2226
- return Result.Iter == RHS.Result .Iter ;
2204
+ template <typename ... Refs> struct enumerator_result <std::size_t , Refs...> {
2205
+ static constexpr std::size_t NumRefs = sizeof ...(Refs);
2206
+ static_assert (NumRefs != 0 );
2207
+ // `NumValues` includes the index.
2208
+ static constexpr std::size_t NumValues = NumRefs + 1 ;
2209
+
2210
+ // Tuple type whose element types are references for each `Ref`.
2211
+ using range_reference_tuple = std::tuple<Refs...>;
2212
+ // Tuple type who elements are references to all values, including both
2213
+ // the index and `Refs` reference types.
2214
+ using value_reference_tuple = std::tuple<std::size_t , Refs...>;
2215
+
2216
+ enumerator_result (std::size_t Index, Refs &&...Rs)
2217
+ : Idx(Index), Storage(std::forward<Refs>(Rs)...) {}
2218
+
2219
+ // / Returns the 0-based index of the current position within the original
2220
+ // / input range(s).
2221
+ std::size_t index () const { return Idx; }
2222
+
2223
+ // / Returns the value(s) for the current iterator. This does not include the
2224
+ // / index.
2225
+ decltype (auto ) value() const {
2226
+ if constexpr (NumRefs == 1 )
2227
+ return std::get<0 >(Storage);
2228
+ else
2229
+ return Storage;
2230
+ }
2231
+
2232
+ // / Returns the value at index `I`. This case covers the index.
2233
+ template <std::size_t I, typename = std::enable_if_t <I == 0 >>
2234
+ friend std::size_t get (const enumerator_result &Result) {
2235
+ return Result.Idx ;
2236
+ }
2237
+
2238
+ // / Returns the value at index `I`. This case covers references to the
2239
+ // / iteratees.
2240
+ template <std::size_t I, typename = std::enable_if_t <I != 0 >>
2241
+ friend decltype (auto )
2242
+ get(const enumerator_result &Result) {
2243
+ // Note: This is a separate function from the other `get`, instead of an
2244
+ // `if constexpr` case, to work around an MSVC 19.31.31XXX compiler
2245
+ // (Visual Studio 2022 17.1) return type deduction bug.
2246
+ return std::get<I - 1 >(Result.Storage );
2247
+ }
2248
+
2249
+ template <typename ... Ts>
2250
+ friend bool operator ==(const enumerator_result &Result,
2251
+ const std::tuple<std::size_t , Ts...> &Other) {
2252
+ static_assert (NumRefs == sizeof ...(Ts), " Size mismatch" );
2253
+ if (Result.Idx != std::get<0 >(Other))
2254
+ return false ;
2255
+ return Result.is_value_equal (Other, std::make_index_sequence<NumRefs>{});
2227
2256
}
2228
2257
2229
2258
private:
2230
- result_type Result;
2259
+ template <typename Tuple, std::size_t ... Idx>
2260
+ bool is_value_equal (const Tuple &Other, std::index_sequence<Idx...>) const {
2261
+ return ((std::get<Idx>(Storage) == std::get<Idx + 1 >(Other)) && ...);
2262
+ }
2263
+
2264
+ std::size_t Idx;
2265
+ // Make this tuple mutable to avoid casts that obfuscate const-correctness
2266
+ // issues. Const-correctness of references is taken care of by `zippy` that
2267
+ // defines const-non and const iterator types that will propagate down to
2268
+ // `enumerator_result`'s `Refs`.
2269
+ // Note that unlike the results of `zip*` functions, `enumerate`'s result are
2270
+ // supposed to be modifiable even when defined as
2271
+ // `const`.
2272
+ mutable range_reference_tuple Storage;
2231
2273
};
2232
2274
2233
- template <typename R> class enumerator {
2234
- public:
2235
- explicit enumerator (R &&Range) : TheRange(std::forward<R>(Range)) {}
2275
+ // / Infinite stream of increasing 0-based `size_t` indices.
2276
+ struct index_stream {
2277
+ struct iterator : iterator_facade_base<iterator, std::forward_iterator_tag,
2278
+ const iterator> {
2279
+ iterator &operator ++() {
2280
+ assert (Index != std::numeric_limits<std::size_t >::max () &&
2281
+ " Attempting to increment end iterator" );
2282
+ ++Index;
2283
+ return *this ;
2284
+ }
2236
2285
2237
- enumerator_iter<R> begin () {
2238
- return enumerator_iter<R>( 0 , adl_begin (TheRange));
2239
- }
2240
- enumerator_iter<R> begin () const {
2241
- return enumerator_iter<R>( 0 , adl_begin (TheRange));
2242
- }
2286
+ // Note: This dereference operator returns a value instead of a reference
2287
+ // and does not strictly conform to the C++17's definition of forward
2288
+ // iterator. However, it satisfies all the forward_iterator requirements
2289
+ // that the `zip_common` depends on and fully conforms to the C++20
2290
+ // definition of forward iterator.
2291
+ std:: size_t operator *() const { return Index; }
2243
2292
2244
- enumerator_iter<R> end () { return enumerator_iter<R>(adl_end (TheRange)); }
2245
- enumerator_iter<R> end () const {
2246
- return enumerator_iter<R>(adl_end (TheRange));
2247
- }
2293
+ friend bool operator ==(const iterator &Lhs, const iterator &Rhs) {
2294
+ return Lhs.Index == Rhs.Index ;
2295
+ }
2248
2296
2249
- private:
2250
- R TheRange;
2297
+ std::size_t Index = 0 ;
2298
+ };
2299
+
2300
+ iterator begin () const { return {}; }
2301
+ iterator end () const {
2302
+ // We approximate 'infinity' with the max size_t value, which should be good
2303
+ // enough to index over any container.
2304
+ iterator It;
2305
+ It.Index = std::numeric_limits<std::size_t >::max ();
2306
+ return It;
2307
+ }
2251
2308
};
2252
2309
2253
2310
} // end namespace detail
2254
2311
2255
- // / Given an input range, returns a new range whose values are are pair (A,B)
2256
- // / such that A is the 0-based index of the item in the sequence, and B is
2257
- // / the value from the original sequence. Example:
2312
+ // / Given two or more input ranges, returns a new range whose values are are
2313
+ // / tuples (A, B, C, ...), such that A is the 0-based index of the item in the
2314
+ // / sequence, and B, C, ..., are the values from the original input ranges. All
2315
+ // / input ranges are required to have equal lengths. Note that the returned
2316
+ // / iterator allows for the values (B, C, ...) to be modified. Example:
2317
+ // /
2318
+ // / ```c++
2319
+ // / std::vector<char> Letters = {'A', 'B', 'C', 'D'};
2320
+ // / std::vector<int> Vals = {10, 11, 12, 13};
2258
2321
// /
2259
- // / std::vector<char> Items = {'A', 'B', 'C', 'D'};
2260
- // / for (auto X : enumerate(Items)) {
2261
- // / printf("Item %zu - %c\n", X.index(), X.value()) ;
2322
+ // / for (auto [Index, Letter, Value] : enumerate(Letters, Vals)) {
2323
+ // / printf("Item %zu - %c: %d\n", Index, Letter, Value);
2324
+ // / Value -= 10 ;
2262
2325
// / }
2326
+ // / ```
2263
2327
// /
2264
- // / or using structured bindings:
2328
+ // / Output:
2329
+ // / Item 0 - A: 10
2330
+ // / Item 1 - B: 11
2331
+ // / Item 2 - C: 12
2332
+ // / Item 3 - D: 13
2265
2333
// /
2266
- // / for (auto [Index, Value] : enumerate(Items)) {
2267
- // / printf("Item %zu - %c\n", Index, Value);
2334
+ // / or using an iterator:
2335
+ // / ```c++
2336
+ // / for (auto it : enumerate(Vals)) {
2337
+ // / it.value() += 10;
2338
+ // / printf("Item %zu: %d\n", it.index(), it.value());
2268
2339
// / }
2340
+ // / ```
2269
2341
// /
2270
2342
// / Output:
2271
- // / Item 0 - A
2272
- // / Item 1 - B
2273
- // / Item 2 - C
2274
- // / Item 3 - D
2343
+ // / Item 0: 20
2344
+ // / Item 1: 21
2345
+ // / Item 2: 22
2346
+ // / Item 3: 23
2275
2347
// /
2276
- template <typename R> detail::enumerator<R> enumerate(R &&TheRange) {
2277
- return detail::enumerator<R>(std::forward<R>(TheRange));
2348
+ template <typename FirstRange, typename ... RestRanges>
2349
+ auto enumerate(FirstRange &&First, RestRanges &&...Rest) {
2350
+ if constexpr (sizeof ...(Rest) != 0 ) {
2351
+ #ifndef NDEBUG
2352
+ // Note: Create an array instead of an initializer list to work around an
2353
+ // Apple clang 14 compiler bug.
2354
+ size_t sizes[] = {
2355
+ static_cast <size_t >(std::distance (adl_begin (First), adl_end (First))),
2356
+ static_cast <size_t >(std::distance (adl_begin (Rest), adl_end (Rest)))...};
2357
+ assert (all_equal (sizes) && " Ranges have different length" );
2358
+ #endif
2359
+ }
2360
+ using enumerator = detail::zippy<detail::zip_enumerator, detail::index_stream,
2361
+ FirstRange, RestRanges...>;
2362
+ return enumerator (detail::index_stream{}, std::forward<FirstRange>(First),
2363
+ std::forward<RestRanges>(Rest)...);
2278
2364
}
2279
2365
2280
2366
namespace detail {
@@ -2406,15 +2492,17 @@ template <class T> constexpr T *to_address(T *P) { return P; }
2406
2492
} // end namespace llvm
2407
2493
2408
2494
namespace std {
2409
- template <typename R >
2410
- struct tuple_size <llvm::detail::result_pair<R >>
2411
- : std::integral_constant<std::size_t , 2 > {};
2495
+ template <typename ... Refs >
2496
+ struct tuple_size <llvm::detail::enumerator_result<Refs... >>
2497
+ : std::integral_constant<std::size_t , sizeof ...(Refs) > {};
2412
2498
2413
- template <std::size_t i, typename R>
2414
- struct tuple_element <i, llvm::detail::result_pair<R>>
2415
- : std::conditional<i == 0 , std::size_t ,
2416
- typename llvm::detail::result_pair<R>::value_reference> {
2417
- };
2499
+ template <std::size_t I, typename ... Refs>
2500
+ struct tuple_element <I, llvm::detail::enumerator_result<Refs...>>
2501
+ : std::tuple_element<I, std::tuple<Refs...>> {};
2502
+
2503
+ template <std::size_t I, typename ... Refs>
2504
+ struct tuple_element <I, const llvm::detail::enumerator_result<Refs...>>
2505
+ : std::tuple_element<I, std::tuple<Refs...>> {};
2418
2506
2419
2507
} // namespace std
2420
2508
0 commit comments