forked from OSchip/llvm-project
[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 llvm-svn: 348301
This commit is contained in:
parent
24fb296589
commit
e6899bf002
|
@ -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
|
||||
|
|
|
@ -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};
|
||||
|
|
Loading…
Reference in New Issue