diff options
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/include/llvm/ADT/STLExtras.h | 126 | ||||
-rw-r--r-- | llvm/unittests/ADT/IteratorTest.cpp | 30 |
2 files changed, 155 insertions, 1 deletions
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h index ba31392f6e1..dba7279b204 100644 --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -508,9 +508,11 @@ make_early_inc_range(RangeT &&Range) { EarlyIncIteratorT(std::end(std::forward<RangeT>(Range)))); } -// forward declarations required by zip_shortest/zip_first +// forward declarations required by zip_shortest/zip_first/zip_longest template <typename R, typename UnaryPredicate> bool all_of(R &&range, UnaryPredicate P); +template <typename R, typename UnaryPredicate> +bool any_of(R &&range, UnaryPredicate P); template <size_t... I> struct index_sequence; @@ -661,6 +663,128 @@ detail::zippy<detail::zip_first, T, U, Args...> zip_first(T &&t, U &&u, std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...); } +namespace detail { +template <typename Iter> +static Iter next_or_end(const Iter &I, const Iter &End) { + if (I == End) + return End; + return std::next(I); +} + +template <typename Iter> +static auto deref_or_none(const Iter &I, const Iter &End) + -> llvm::Optional<typename std::remove_const< + typename std::remove_reference<decltype(*I)>::type>::type> { + if (I == End) + return None; + return *I; +} + +template <typename... Iters> struct ZipLongestValueType { + using type = std::tuple< + llvm::Optional<typename std::remove_const<typename std::remove_reference< + decltype(*std::declval<Iters>())>::type>::type>...>; +}; + +template <typename... Iters> +class zip_longest_iterator + : public iterator_facade_base< + zip_longest_iterator<Iters...>, + typename std::common_type< + std::forward_iterator_tag, + typename std::iterator_traits<Iters>::iterator_category...>::type, + typename ZipLongestValueType<Iters...>::type, + typename std::iterator_traits<typename std::tuple_element< + 0, std::tuple<Iters...>>::type>::difference_type, + typename ZipLongestValueType<Iters...>::type *, + typename ZipLongestValueType<Iters...>::type> { +public: + using value_type = typename ZipLongestValueType<Iters...>::type; + +private: + std::tuple<Iters...> iterators; + std::tuple<Iters...> end_iterators; + + template <size_t... Ns> + bool test(const zip_longest_iterator<Iters...> &other, + index_sequence<Ns...>) const { + return llvm::any_of( + std::initializer_list<bool>{std::get<Ns>(this->iterators) != + std::get<Ns>(other.iterators)...}, + identity<bool>{}); + } + + template <size_t... Ns> value_type deref(index_sequence<Ns...>) const { + return value_type( + deref_or_none(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...); + } + + template <size_t... Ns> + decltype(iterators) tup_inc(index_sequence<Ns...>) const { + return std::tuple<Iters...>( + next_or_end(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...); + } + +public: + zip_longest_iterator(std::pair<Iters &&, Iters &&>... ts) + : iterators(std::forward<Iters>(ts.first)...), + end_iterators(std::forward<Iters>(ts.second)...) {} + + value_type operator*() { return deref(index_sequence_for<Iters...>{}); } + + value_type operator*() const { return deref(index_sequence_for<Iters...>{}); } + + zip_longest_iterator<Iters...> &operator++() { + iterators = tup_inc(index_sequence_for<Iters...>{}); + return *this; + } + + bool operator==(const zip_longest_iterator<Iters...> &other) const { + return !test(other, index_sequence_for<Iters...>{}); + } +}; + +template <typename... Args> class zip_longest_range { +public: + using iterator = + zip_longest_iterator<decltype(adl_begin(std::declval<Args>()))...>; + using iterator_category = typename iterator::iterator_category; + using value_type = typename iterator::value_type; + using difference_type = typename iterator::difference_type; + using pointer = typename iterator::pointer; + using reference = typename iterator::reference; + +private: + std::tuple<Args...> ts; + + template <size_t... Ns> iterator begin_impl(index_sequence<Ns...>) const { + return iterator(std::make_pair(adl_begin(std::get<Ns>(ts)), + adl_end(std::get<Ns>(ts)))...); + } + + template <size_t... Ns> iterator end_impl(index_sequence<Ns...>) const { + return iterator(std::make_pair(adl_end(std::get<Ns>(ts)), + adl_end(std::get<Ns>(ts)))...); + } + +public: + zip_longest_range(Args &&... ts_) : ts(std::forward<Args>(ts_)...) {} + + iterator begin() const { return begin_impl(index_sequence_for<Args...>{}); } + iterator end() const { return end_impl(index_sequence_for<Args...>{}); } +}; +} // namespace detail + +/// Iterate over two or more iterators at the same time. Iteration continues +/// until all iterators reach the end. The llvm::Optional only contains a value +/// if the iterator has not reached the end. +template <typename T, typename U, typename... Args> +detail::zip_longest_range<T, U, Args...> zip_longest(T &&t, U &&u, + Args &&... args) { + return detail::zip_longest_range<T, U, Args...>( + std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...); +} + /// Iterator wrapper that concatenates sequences together. /// /// This can concatenate different iterators, even with different types, into diff --git a/llvm/unittests/ADT/IteratorTest.cpp b/llvm/unittests/ADT/IteratorTest.cpp index de0a6710789..f15ba8eac83 100644 --- a/llvm/unittests/ADT/IteratorTest.cpp +++ b/llvm/unittests/ADT/IteratorTest.cpp @@ -328,6 +328,36 @@ TEST(ZipIteratorTest, ZipFirstBasic) { EXPECT_EQ(iters, 4u); } +TEST(ZipIteratorTest, ZipLongestBasic) { + using namespace std; + const vector<unsigned> pi{3, 1, 4, 1, 5, 9}; + const vector<StringRef> e{"2", "7", "1", "8"}; + + { + // Check left range longer than right. + const vector<tuple<Optional<unsigned>, Optional<StringRef>>> expected{ + {3, {"2"}}, {1, {"7"}}, {4, {"1"}}, {1, {"8"}}, {5, None}, {9, None}}; + size_t iters = 0; + for (auto tup : zip_longest(pi, e)) { + EXPECT_EQ(tup, expected[iters]); + iters += 1; + } + EXPECT_EQ(iters, expected.size()); + } + + { + // Check right range longer than left. + const vector<tuple<Optional<StringRef>, Optional<unsigned>>> expected{ + {{"2"}, 3}, {{"7"}, 1}, {{"1"}, 4}, {{"8"}, 1}, {None, 5}, {None, 9}}; + size_t iters = 0; + for (auto tup : zip_longest(e, pi)) { + EXPECT_EQ(tup, expected[iters]); + iters += 1; + } + EXPECT_EQ(iters, expected.size()); + } +} + TEST(ZipIteratorTest, Mutability) { using namespace std; const SmallVector<unsigned, 4> pi{3, 1, 4, 1, 5, 9}; |