llvm.org GIT mirror llvm / a348028
[ADT] Add zip_longest iterators Like the already existing zip_shortest/zip_first iterators, zip_longest iterates over multiple iterators at once, but has as many iterations as the longest sequence. This means some iterators may reach the end before others do. zip_longest uses llvm::Optional's None value to mark a past-the-end value. zip_longest is not reverse-iteratable because the tuples iterated over would be different for different length sequences (IMHO for the same reason neither zip_shortest nor zip_first should be reverse-iteratable; one can still reverse the ranges individually if that's the expected behavior). In contrast to zip_shortest/zip_first, zip_longest tuples contain rvalues instead of references. This is because llvm::Optional cannot contain reference types and the value-initialized default does not have a memory location a reference could point to. The motivation for these iterators is to use C++ foreach to compare two lists of ordered attributes in D48100 (SemaOverload.cpp and ASTReaderDecl.cpp). Idea by @hfinkel. Differential Revision: https://reviews.llvm.org/D48348 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@348301 91177308-0d34-0410-b5e6-96231b3b80d8 Michael Kruse 1 year, 9 months ago
2 changed file(s) with 155 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
507507 EarlyIncIteratorT(std::end(std::forward(Range))));
508508 }
509509
510 // forward declarations required by zip_shortest/zip_first
510 // forward declarations required by zip_shortest/zip_first/zip_longest
511511 template
512512 bool all_of(R &&range, UnaryPredicate P);
513 template
514 bool any_of(R &&range, UnaryPredicate P);
513515
514516 template struct index_sequence;
515517
657659 detail::zippy zip_first(T &&t, U &&u,
658660 Args &&... args) {
659661 return detail::zippy(
662 std::forward(t), std::forward(u), std::forward(args)...);
663 }
664
665 namespace detail {
666 template
667 static Iter next_or_end(const Iter &I, const Iter &End) {
668 if (I == End)
669 return End;
670 return std::next(I);
671 }
672
673 template
674 static auto deref_or_none(const Iter &I, const Iter &End)
675 -> llvm::Optional
676 typename std::remove_reference::type>::type> {
677 if (I == End)
678 return None;
679 return *I;
680 }
681
682 template struct ZipLongestValueType {
683 using type = std::tuple<
684 llvm::Optional
685 decltype(*std::declval())>::type>::type>...>;
686 };
687
688 template
689 class zip_longest_iterator
690 : public iterator_facade_base<
691 zip_longest_iterator,
692 typename std::common_type<
693 std::forward_iterator_tag,
694 typename std::iterator_traits::iterator_category...>::type,
695 typename ZipLongestValueType::type,
696 typename std::iterator_traits
697 0, std::tuple>::type>::difference_type,
698 typename ZipLongestValueType::type *,
699 typename ZipLongestValueType::type> {
700 public:
701 using value_type = typename ZipLongestValueType::type;
702
703 private:
704 std::tuple iterators;
705 std::tuple end_iterators;
706
707 template
708 bool test(const zip_longest_iterator &other,
709 index_sequence) const {
710 return llvm::any_of(
711 std::initializer_list{std::get(this->iterators) !=
712 std::get(other.iterators)...},
713 identity{});
714 }
715
716 template value_type deref(index_sequence) const {
717 return value_type(
718 deref_or_none(std::get(iterators), std::get(end_iterators))...);
719 }
720
721 template
722 decltype(iterators) tup_inc(index_sequence) const {
723 return std::tuple(
724 next_or_end(std::get(iterators), std::get(end_iterators))...);
725 }
726
727 public:
728 zip_longest_iterator(std::pair... ts)
729 : iterators(std::forward(ts.first)...),
730 end_iterators(std::forward(ts.second)...) {}
731
732 value_type operator*() { return deref(index_sequence_for{}); }
733
734 value_type operator*() const { return deref(index_sequence_for{}); }
735
736 zip_longest_iterator &operator++() {
737 iterators = tup_inc(index_sequence_for{});
738 return *this;
739 }
740
741 bool operator==(const zip_longest_iterator &other) const {
742 return !test(other, index_sequence_for{});
743 }
744 };
745
746 template class zip_longest_range {
747 public:
748 using iterator =
749 zip_longest_iterator()))...>;
750 using iterator_category = typename iterator::iterator_category;
751 using value_type = typename iterator::value_type;
752 using difference_type = typename iterator::difference_type;
753 using pointer = typename iterator::pointer;
754 using reference = typename iterator::reference;
755
756 private:
757 std::tuple ts;
758
759 template iterator begin_impl(index_sequence) const {
760 return iterator(std::make_pair(adl_begin(std::get(ts)),
761 adl_end(std::get(ts)))...);
762 }
763
764 template iterator end_impl(index_sequence) const {
765 return iterator(std::make_pair(adl_end(std::get(ts)),
766 adl_end(std::get(ts)))...);
767 }
768
769 public:
770 zip_longest_range(Args &&... ts_) : ts(std::forward(ts_)...) {}
771
772 iterator begin() const { return begin_impl(index_sequence_for{}); }
773 iterator end() const { return end_impl(index_sequence_for{}); }
774 };
775 } // namespace detail
776
777 /// Iterate over two or more iterators at the same time. Iteration continues
778 /// until all iterators reach the end. The llvm::Optional only contains a value
779 /// if the iterator has not reached the end.
780 template
781 detail::zip_longest_range zip_longest(T &&t, U &&u,
782 Args &&... args) {
783 return detail::zip_longest_range(
660784 std::forward(t), std::forward(u), std::forward(args)...);
661785 }
662786
327327 EXPECT_EQ(iters, 4u);
328328 }
329329
330 TEST(ZipIteratorTest, ZipLongestBasic) {
331 using namespace std;
332 const vector pi{3, 1, 4, 1, 5, 9};
333 const vector e{"2", "7", "1", "8"};
334
335 {
336 // Check left range longer than right.
337 const vector, Optional>> expected{
338 {3, {"2"}}, {1, {"7"}}, {4, {"1"}}, {1, {"8"}}, {5, None}, {9, None}};
339 size_t iters = 0;
340 for (auto tup : zip_longest(pi, e)) {
341 EXPECT_EQ(tup, expected[iters]);
342 iters += 1;
343 }
344 EXPECT_EQ(iters, expected.size());
345 }
346
347 {
348 // Check right range longer than left.
349 const vector, Optional>> expected{
350 {{"2"}, 3}, {{"7"}, 1}, {{"1"}, 4}, {{"8"}, 1}, {None, 5}, {None, 9}};
351 size_t iters = 0;
352 for (auto tup : zip_longest(e, pi)) {
353 EXPECT_EQ(tup, expected[iters]);
354 iters += 1;
355 }
356 EXPECT_EQ(iters, expected.size());
357 }
358 }
359
330360 TEST(ZipIteratorTest, Mutability) {
331361 using namespace std;
332362 const SmallVector pi{3, 1, 4, 1, 5, 9};