Skip to content

Commit f57bfd3

Browse files
committed
[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. This re-commits r348301 which was reverted by r348303. The compilation error by gcc 5.4 was resolved using make_tuple in the in the initializer_list. The compileration error by msvc14 was resolved by splitting ZipLongestValueType (which already was a workaround for msvc15) into ZipLongestItemType and ZipLongestTupleType. Differential Revision: https://reviews.llvm.org/D48348 llvm-svn: 348323
1 parent ff9aaa2 commit f57bfd3

File tree

2 files changed

+163
-1
lines changed

2 files changed

+163
-1
lines changed

llvm/include/llvm/ADT/STLExtras.h

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,9 +508,11 @@ make_early_inc_range(RangeT &&Range) {
508508
EarlyIncIteratorT(std::end(std::forward<RangeT>(Range))));
509509
}
510510

511-
// forward declarations required by zip_shortest/zip_first
511+
// forward declarations required by zip_shortest/zip_first/zip_longest
512512
template <typename R, typename UnaryPredicate>
513513
bool all_of(R &&range, UnaryPredicate P);
514+
template <typename R, typename UnaryPredicate>
515+
bool any_of(R &&range, UnaryPredicate P);
514516

515517
template <size_t... I> struct index_sequence;
516518

@@ -661,6 +663,132 @@ detail::zippy<detail::zip_first, T, U, Args...> zip_first(T &&t, U &&u,
661663
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
662664
}
663665

666+
namespace detail {
667+
template <typename Iter>
668+
static Iter next_or_end(const Iter &I, const Iter &End) {
669+
if (I == End)
670+
return End;
671+
return std::next(I);
672+
}
673+
674+
template <typename Iter>
675+
static auto deref_or_none(const Iter &I, const Iter &End)
676+
-> llvm::Optional<typename std::remove_const<
677+
typename std::remove_reference<decltype(*I)>::type>::type> {
678+
if (I == End)
679+
return None;
680+
return *I;
681+
}
682+
683+
template <typename Iter> struct ZipLongestItemType {
684+
using type =
685+
llvm::Optional<typename std::remove_const<typename std::remove_reference<
686+
decltype(*std::declval<Iter>())>::type>::type>;
687+
};
688+
689+
template <typename... Iters> struct ZipLongestTupleType {
690+
using type = std::tuple<typename ZipLongestItemType<Iters>::type...>;
691+
};
692+
693+
template <typename... Iters>
694+
class zip_longest_iterator
695+
: public iterator_facade_base<
696+
zip_longest_iterator<Iters...>,
697+
typename std::common_type<
698+
std::forward_iterator_tag,
699+
typename std::iterator_traits<Iters>::iterator_category...>::type,
700+
typename ZipLongestTupleType<Iters...>::type,
701+
typename std::iterator_traits<typename std::tuple_element<
702+
0, std::tuple<Iters...>>::type>::difference_type,
703+
typename ZipLongestTupleType<Iters...>::type *,
704+
typename ZipLongestTupleType<Iters...>::type> {
705+
public:
706+
using value_type = typename ZipLongestTupleType<Iters...>::type;
707+
708+
private:
709+
std::tuple<Iters...> iterators;
710+
std::tuple<Iters...> end_iterators;
711+
712+
template <size_t... Ns>
713+
bool test(const zip_longest_iterator<Iters...> &other,
714+
index_sequence<Ns...>) const {
715+
return llvm::any_of(
716+
std::initializer_list<bool>{std::get<Ns>(this->iterators) !=
717+
std::get<Ns>(other.iterators)...},
718+
identity<bool>{});
719+
}
720+
721+
template <size_t... Ns> value_type deref(index_sequence<Ns...>) const {
722+
return value_type(
723+
deref_or_none(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
724+
}
725+
726+
template <size_t... Ns>
727+
decltype(iterators) tup_inc(index_sequence<Ns...>) const {
728+
return std::tuple<Iters...>(
729+
next_or_end(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
730+
}
731+
732+
public:
733+
zip_longest_iterator(std::pair<Iters &&, Iters &&>... ts)
734+
: iterators(std::forward<Iters>(ts.first)...),
735+
end_iterators(std::forward<Iters>(ts.second)...) {}
736+
737+
value_type operator*() { return deref(index_sequence_for<Iters...>{}); }
738+
739+
value_type operator*() const { return deref(index_sequence_for<Iters...>{}); }
740+
741+
zip_longest_iterator<Iters...> &operator++() {
742+
iterators = tup_inc(index_sequence_for<Iters...>{});
743+
return *this;
744+
}
745+
746+
bool operator==(const zip_longest_iterator<Iters...> &other) const {
747+
return !test(other, index_sequence_for<Iters...>{});
748+
}
749+
};
750+
751+
template <typename... Args> class zip_longest_range {
752+
public:
753+
using iterator =
754+
zip_longest_iterator<decltype(adl_begin(std::declval<Args>()))...>;
755+
using iterator_category = typename iterator::iterator_category;
756+
using value_type = typename iterator::value_type;
757+
using difference_type = typename iterator::difference_type;
758+
using pointer = typename iterator::pointer;
759+
using reference = typename iterator::reference;
760+
761+
private:
762+
std::tuple<Args...> ts;
763+
764+
template <size_t... Ns> iterator begin_impl(index_sequence<Ns...>) const {
765+
return iterator(std::make_pair(adl_begin(std::get<Ns>(ts)),
766+
adl_end(std::get<Ns>(ts)))...);
767+
}
768+
769+
template <size_t... Ns> iterator end_impl(index_sequence<Ns...>) const {
770+
return iterator(std::make_pair(adl_end(std::get<Ns>(ts)),
771+
adl_end(std::get<Ns>(ts)))...);
772+
}
773+
774+
public:
775+
zip_longest_range(Args &&... ts_) : ts(std::forward<Args>(ts_)...) {}
776+
777+
iterator begin() const { return begin_impl(index_sequence_for<Args...>{}); }
778+
iterator end() const { return end_impl(index_sequence_for<Args...>{}); }
779+
};
780+
} // namespace detail
781+
782+
/// Iterate over two or more iterators at the same time. Iteration continues
783+
/// until all iterators reach the end. The llvm::Optional only contains a value
784+
/// if the iterator has not reached the end.
785+
template <typename T, typename U, typename... Args>
786+
detail::zip_longest_range<T, U, Args...> zip_longest(T &&t, U &&u,
787+
Args &&... args) {
788+
return detail::zip_longest_range<T, U, Args...>(
789+
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
790+
}
791+
664792
/// Iterator wrapper that concatenates sequences together.
665793
///
666794
/// This can concatenate different iterators, even with different types, into

llvm/unittests/ADT/IteratorTest.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,40 @@ TEST(ZipIteratorTest, ZipFirstBasic) {
328328
EXPECT_EQ(iters, 4u);
329329
}
330330

331+
TEST(ZipIteratorTest, ZipLongestBasic) {
332+
using namespace std;
333+
const vector<unsigned> pi{3, 1, 4, 1, 5, 9};
334+
const vector<StringRef> e{"2", "7", "1", "8"};
335+
336+
{
337+
// Check left range longer than right.
338+
const vector<tuple<Optional<unsigned>, Optional<StringRef>>> expected{
339+
make_tuple(3, StringRef("2")), make_tuple(1, StringRef("7")),
340+
make_tuple(4, StringRef("1")), make_tuple(1, StringRef("8")),
341+
make_tuple(5, None), make_tuple(9, None)};
342+
size_t iters = 0;
343+
for (auto tup : zip_longest(pi, e)) {
344+
EXPECT_EQ(tup, expected[iters]);
345+
iters += 1;
346+
}
347+
EXPECT_EQ(iters, expected.size());
348+
}
349+
350+
{
351+
// Check right range longer than left.
352+
const vector<tuple<Optional<StringRef>, Optional<unsigned>>> expected{
353+
make_tuple(StringRef("2"), 3), make_tuple(StringRef("7"), 1),
354+
make_tuple(StringRef("1"), 4), make_tuple(StringRef("8"), 1),
355+
make_tuple(None, 5), make_tuple(None, 9)};
356+
size_t iters = 0;
357+
for (auto tup : zip_longest(e, pi)) {
358+
EXPECT_EQ(tup, expected[iters]);
359+
iters += 1;
360+
}
361+
EXPECT_EQ(iters, expected.size());
362+
}
363+
}
364+
331365
TEST(ZipIteratorTest, Mutability) {
332366
using namespace std;
333367
const SmallVector<unsigned, 4> pi{3, 1, 4, 1, 5, 9};

0 commit comments

Comments
 (0)