Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit 5844b06

Browse files
author
Zachary Turner
committed
Add llvm::enumerate() range adapter.
This allows you to enumerate over a range using a range-based for while the return type contains the index of the enumeration. Differential revision: https://reviews.llvm.org/D25124 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283337 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent b53fb3c commit 5844b06

File tree

2 files changed

+147
-54
lines changed

2 files changed

+147
-54
lines changed

include/llvm/ADT/STLExtras.h

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace llvm {
3535
namespace detail {
3636

3737
template <typename RangeT>
38-
using IterOfRange = decltype(std::begin(std::declval<RangeT>()));
38+
using IterOfRange = decltype(std::begin(std::declval<RangeT &>()));
3939

4040
} // End detail namespace
4141

@@ -627,7 +627,7 @@ template <typename T> struct deref {
627627
};
628628

629629
namespace detail {
630-
template <typename I, typename V> class enumerator_impl {
630+
template <typename R> class enumerator_impl {
631631
public:
632632
template <typename X> struct result_pair {
633633
result_pair(std::size_t Index, X Value) : Index(Index), Value(Value) {}
@@ -636,13 +636,16 @@ template <typename I, typename V> class enumerator_impl {
636636
X Value;
637637
};
638638

639-
struct iterator {
640-
iterator(I Iter, std::size_t Index) : Iter(Iter), Index(Index) {}
639+
class iterator {
640+
typedef
641+
typename std::iterator_traits<IterOfRange<R>>::reference iter_reference;
642+
typedef result_pair<iter_reference> result_type;
641643

642-
result_pair<const V> operator*() const {
643-
return result_pair<const V>(Index, *Iter);
644-
}
645-
result_pair<V> operator*() { return result_pair<V>(Index, *Iter); }
644+
public:
645+
iterator(IterOfRange<R> &&Iter, std::size_t Index)
646+
: Iter(Iter), Index(Index) {}
647+
648+
result_type operator*() const { return result_type(Index, *Iter); }
646649

647650
iterator &operator++() {
648651
++Iter;
@@ -653,28 +656,19 @@ template <typename I, typename V> class enumerator_impl {
653656
bool operator!=(const iterator &RHS) const { return Iter != RHS.Iter; }
654657

655658
private:
656-
I Iter;
659+
IterOfRange<R> Iter;
657660
std::size_t Index;
658661
};
659662

660-
enumerator_impl(I Begin, I End)
661-
: Begin(std::move(Begin)), End(std::move(End)) {}
662-
663-
iterator begin() { return iterator(Begin, 0); }
664-
iterator end() { return iterator(End, std::size_t(-1)); }
663+
public:
664+
explicit enumerator_impl(R &&Range) : Range(std::forward<R>(Range)) {}
665665

666-
iterator begin() const { return iterator(Begin, 0); }
667-
iterator end() const { return iterator(End, std::size_t(-1)); }
666+
iterator begin() { return iterator(std::begin(Range), 0); }
667+
iterator end() { return iterator(std::end(Range), std::size_t(-1)); }
668668

669669
private:
670-
I Begin;
671-
I End;
670+
R Range;
672671
};
673-
674-
template <typename I>
675-
auto make_enumerator(I Begin, I End) -> enumerator_impl<I, decltype(*Begin)> {
676-
return enumerator_impl<I, decltype(*Begin)>(std::move(Begin), std::move(End));
677-
}
678672
}
679673

680674
/// Given an input range, returns a new range whose values are are pair (A,B)
@@ -692,10 +686,8 @@ auto make_enumerator(I Begin, I End) -> enumerator_impl<I, decltype(*Begin)> {
692686
/// Item 2 - C
693687
/// Item 3 - D
694688
///
695-
template <typename R>
696-
auto enumerate(R &&Range)
697-
-> decltype(detail::make_enumerator(std::begin(Range), std::end(Range))) {
698-
return detail::make_enumerator(std::begin(Range), std::end(Range));
689+
template <typename R> detail::enumerator_impl<R> enumerate(R &&Range) {
690+
return detail::enumerator_impl<R>(std::forward<R>(Range));
699691
}
700692

701693
} // End llvm namespace

unittests/ADT/STLExtrasTest.cpp

Lines changed: 128 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,51 +39,152 @@ TEST(STLExtrasTest, Rank) {
3939
EXPECT_EQ(4, f(rank<6>()));
4040
}
4141

42-
TEST(STLExtrasTest, Enumerate) {
42+
TEST(STLExtrasTest, EnumerateLValue) {
43+
// Test that a simple LValue can be enumerated and gives correct results with
44+
// multiple types, including the empty container.
4345
std::vector<char> foo = {'a', 'b', 'c'};
44-
45-
std::vector<std::pair<std::size_t, char>> results;
46+
std::vector<std::pair<std::size_t, char>> CharResults;
4647

4748
for (auto X : llvm::enumerate(foo)) {
48-
results.push_back(std::make_pair(X.Index, X.Value));
49+
CharResults.emplace_back(X.Index, X.Value);
4950
}
50-
ASSERT_EQ(3u, results.size());
51-
EXPECT_EQ(0u, results[0].first);
52-
EXPECT_EQ('a', results[0].second);
53-
EXPECT_EQ(1u, results[1].first);
54-
EXPECT_EQ('b', results[1].second);
55-
EXPECT_EQ(2u, results[2].first);
56-
EXPECT_EQ('c', results[2].second);
57-
58-
results.clear();
59-
const std::vector<int> bar = {'1', '2', '3'};
51+
ASSERT_EQ(3u, CharResults.size());
52+
EXPECT_EQ(std::make_pair(0u, 'a'), CharResults[0]);
53+
EXPECT_EQ(std::make_pair(1u, 'b'), CharResults[1]);
54+
EXPECT_EQ(std::make_pair(2u, 'c'), CharResults[2]);
55+
56+
// Test a const range of a different type.
57+
std::vector<std::pair<std::size_t, int>> IntResults;
58+
const std::vector<int> bar = {1, 2, 3};
6059
for (auto X : llvm::enumerate(bar)) {
61-
results.push_back(std::make_pair(X.Index, X.Value));
60+
IntResults.emplace_back(X.Index, X.Value);
6261
}
63-
EXPECT_EQ(0u, results[0].first);
64-
EXPECT_EQ('1', results[0].second);
65-
EXPECT_EQ(1u, results[1].first);
66-
EXPECT_EQ('2', results[1].second);
67-
EXPECT_EQ(2u, results[2].first);
68-
EXPECT_EQ('3', results[2].second);
69-
70-
results.clear();
62+
ASSERT_EQ(3u, IntResults.size());
63+
EXPECT_EQ(std::make_pair(0u, 1), IntResults[0]);
64+
EXPECT_EQ(std::make_pair(1u, 2), IntResults[1]);
65+
EXPECT_EQ(std::make_pair(2u, 3), IntResults[2]);
66+
67+
// Test an empty range.
68+
IntResults.clear();
7169
const std::vector<int> baz;
7270
for (auto X : llvm::enumerate(baz)) {
73-
results.push_back(std::make_pair(X.Index, X.Value));
71+
IntResults.emplace_back(X.Index, X.Value);
7472
}
75-
EXPECT_TRUE(baz.empty());
73+
EXPECT_TRUE(IntResults.empty());
7674
}
7775

78-
TEST(STLExtrasTest, EnumerateModify) {
76+
TEST(STLExtrasTest, EnumerateModifyLValue) {
77+
// Test that you can modify the underlying entries of an lvalue range through
78+
// the enumeration iterator.
7979
std::vector<char> foo = {'a', 'b', 'c'};
8080

8181
for (auto X : llvm::enumerate(foo)) {
8282
++X.Value;
8383
}
84-
8584
EXPECT_EQ('b', foo[0]);
8685
EXPECT_EQ('c', foo[1]);
8786
EXPECT_EQ('d', foo[2]);
8887
}
88+
89+
TEST(STLExtrasTest, EnumerateRValueRef) {
90+
// Test that an rvalue can be enumerated.
91+
std::vector<std::pair<std::size_t, int>> Results;
92+
93+
auto Enumerator = llvm::enumerate(std::vector<int>{1, 2, 3});
94+
95+
for (auto X : llvm::enumerate(std::vector<int>{1, 2, 3})) {
96+
Results.emplace_back(X.Index, X.Value);
97+
}
98+
99+
ASSERT_EQ(3u, Results.size());
100+
EXPECT_EQ(std::make_pair(0u, 1), Results[0]);
101+
EXPECT_EQ(std::make_pair(1u, 2), Results[1]);
102+
EXPECT_EQ(std::make_pair(2u, 3), Results[2]);
103+
}
104+
105+
TEST(STLExtrasTest, EnumerateModifyRValue) {
106+
// Test that when enumerating an rvalue, modification still works (even if
107+
// this isn't terribly useful, it at least shows that we haven't snuck an
108+
// extra const in there somewhere.
109+
std::vector<std::pair<std::size_t, char>> Results;
110+
111+
for (auto X : llvm::enumerate(std::vector<char>{'1', '2', '3'})) {
112+
++X.Value;
113+
Results.emplace_back(X.Index, X.Value);
114+
}
115+
116+
ASSERT_EQ(3u, Results.size());
117+
EXPECT_EQ(std::make_pair(0u, '2'), Results[0]);
118+
EXPECT_EQ(std::make_pair(1u, '3'), Results[1]);
119+
EXPECT_EQ(std::make_pair(2u, '4'), Results[2]);
120+
}
121+
122+
template <bool B> struct CanMove {};
123+
template <> struct CanMove<false> {
124+
CanMove(CanMove &&) = delete;
125+
126+
CanMove() = default;
127+
CanMove(const CanMove &) = default;
128+
};
129+
130+
template <bool B> struct CanCopy {};
131+
template <> struct CanCopy<false> {
132+
CanCopy(const CanCopy &) = delete;
133+
134+
CanCopy() = default;
135+
CanCopy(CanCopy &&) = default;
136+
};
137+
138+
template <bool Moveable, bool Copyable>
139+
struct Range : CanMove<Moveable>, CanCopy<Copyable> {
140+
explicit Range(int &C, int &M, int &D) : C(C), M(M), D(D) {}
141+
Range(const Range &R) : CanCopy<Copyable>(R), C(R.C), M(R.M), D(R.D) { ++C; }
142+
Range(Range &&R) : CanMove<Moveable>(std::move(R)), C(R.C), M(R.M), D(R.D) {
143+
++M;
144+
}
145+
~Range() { ++D; }
146+
147+
int &C;
148+
int &M;
149+
int &D;
150+
151+
int *begin() { return nullptr; }
152+
int *end() { return nullptr; }
153+
};
154+
155+
TEST(STLExtrasTest, EnumerateLifetimeSemantics) {
156+
// Test that when enumerating lvalues and rvalues, there are no surprise
157+
// copies or moves.
158+
159+
// With an rvalue, it should not be destroyed until the end of the scope.
160+
int Copies = 0;
161+
int Moves = 0;
162+
int Destructors = 0;
163+
{
164+
auto E1 = enumerate(Range<true, false>(Copies, Moves, Destructors));
165+
// Doesn't compile. rvalue ranges must be moveable.
166+
// auto E2 = enumerate(Range<false, true>(Copies, Moves, Destructors));
167+
EXPECT_EQ(0, Copies);
168+
EXPECT_EQ(1, Moves);
169+
EXPECT_EQ(1, Destructors);
170+
}
171+
EXPECT_EQ(0, Copies);
172+
EXPECT_EQ(1, Moves);
173+
EXPECT_EQ(2, Destructors);
174+
175+
Copies = Moves = Destructors = 0;
176+
// With an lvalue, it should not be destroyed even after the end of the scope.
177+
// lvalue ranges need be neither copyable nor moveable.
178+
Range<false, false> R(Copies, Moves, Destructors);
179+
{
180+
auto Enumerator = enumerate(R);
181+
(void)Enumerator;
182+
EXPECT_EQ(0, Copies);
183+
EXPECT_EQ(0, Moves);
184+
EXPECT_EQ(0, Destructors);
185+
}
186+
EXPECT_EQ(0, Copies);
187+
EXPECT_EQ(0, Moves);
188+
EXPECT_EQ(0, Destructors);
189+
}
89190
}

0 commit comments

Comments
 (0)