llvm.org GIT mirror llvm / 5844b06
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 Zachary Turner 3 years ago
2 changed file(s) with 148 addition(s) and 55 deletion(s). Raw diff Collapse all Expand all
3434 namespace detail {
3535
3636 template
37 using IterOfRange = decltype(std::begin(std::declval>()));
37 using IterOfRange = decltype(std::begin(std::declval &>()));
3838
3939 } // End detail namespace
4040
626626 };
627627
628628 namespace detail {
629 template I, typename V> class enumerator_impl {
629 template R> class enumerator_impl {
630630 public:
631631 template struct result_pair {
632632 result_pair(std::size_t Index, X Value) : Index(Index), Value(Value) {}
635635 X Value;
636636 };
637637
638 struct iterator {
639 iterator(I Iter, std::size_t Index) : Iter(Iter), Index(Index) {}
640
641 result_pair operator*() const {
642 return result_pair(Index, *Iter);
643 }
644 result_pair operator*() { return result_pair(Index, *Iter); }
638 class iterator {
639 typedef
640 typename std::iterator_traits>::reference iter_reference;
641 typedef result_pair result_type;
642
643 public:
644 iterator(IterOfRange &&Iter, std::size_t Index)
645 : Iter(Iter), Index(Index) {}
646
647 result_type operator*() const { return result_type(Index, *Iter); }
645648
646649 iterator &operator++() {
647650 ++Iter;
652655 bool operator!=(const iterator &RHS) const { return Iter != RHS.Iter; }
653656
654657 private:
655 I Iter;
658 IterOfRange Iter;
656659 std::size_t Index;
657660 };
658661
659 enumerator_impl(I Begin, I End)
660 : Begin(std::move(Begin)), End(std::move(End)) {}
661
662 iterator begin() { return iterator(Begin, 0); }
663 iterator end() { return iterator(End, std::size_t(-1)); }
664
665 iterator begin() const { return iterator(Begin, 0); }
666 iterator end() const { return iterator(End, std::size_t(-1)); }
662 public:
663 explicit enumerator_impl(R &&Range) : Range(std::forward(Range)) {}
664
665 iterator begin() { return iterator(std::begin(Range), 0); }
666 iterator end() { return iterator(std::end(Range), std::size_t(-1)); }
667667
668668 private:
669 I Begin;
670 I End;
671 };
672
673 template
674 auto make_enumerator(I Begin, I End) -> enumerator_impl {
675 return enumerator_impl(std::move(Begin), std::move(End));
676 }
669 R Range;
670 };
677671 }
678672
679673 /// Given an input range, returns a new range whose values are are pair (A,B)
691685 /// Item 2 - C
692686 /// Item 3 - D
693687 ///
694 template
695 auto enumerate(R &&Range)
696 -> decltype(detail::make_enumerator(std::begin(Range), std::end(Range))) {
697 return detail::make_enumerator(std::begin(Range), std::end(Range));
688 template detail::enumerator_impl enumerate(R &&Range) {
689 return detail::enumerator_impl(std::forward(Range));
698690 }
699691
700692 } // End llvm namespace
3838 EXPECT_EQ(4, f(rank<6>()));
3939 }
4040
41 TEST(STLExtrasTest, Enumerate) {
41 TEST(STLExtrasTest, EnumerateLValue) {
42 // Test that a simple LValue can be enumerated and gives correct results with
43 // multiple types, including the empty container.
4244 std::vector foo = {'a', 'b', 'c'};
43
44 std::vector> results;
45 std::vector> CharResults;
4546
4647 for (auto X : llvm::enumerate(foo)) {
47 results.push_back(std::make_pair(X.Index, X.Value));
48 CharResults.emplace_back(X.Index, X.Value);
4849 }
49 ASSERT_EQ(3u, results.size());
50 EXPECT_EQ(0u, results[0].first);
51 EXPECT_EQ('a', results[0].second);
52 EXPECT_EQ(1u, results[1].first);
53 EXPECT_EQ('b', results[1].second);
54 EXPECT_EQ(2u, results[2].first);
55 EXPECT_EQ('c', results[2].second);
50 ASSERT_EQ(3u, CharResults.size());
51 EXPECT_EQ(std::make_pair(0u, 'a'), CharResults[0]);
52 EXPECT_EQ(std::make_pair(1u, 'b'), CharResults[1]);
53 EXPECT_EQ(std::make_pair(2u, 'c'), CharResults[2]);
5654
57 results.clear();
58 const std::vector bar = {'1', '2', '3'};
55 // Test a const range of a different type.
56 std::vector> IntResults;
57 const std::vector bar = {1, 2, 3};
5958 for (auto X : llvm::enumerate(bar)) {
60 results.push_back(std::make_pair(X.Index, X.Value));
59 IntResults.emplace_back(X.Index, X.Value);
6160 }
62 EXPECT_EQ(0u, results[0].first);
63 EXPECT_EQ('1', results[0].second);
64 EXPECT_EQ(1u, results[1].first);
65 EXPECT_EQ('2', results[1].second);
66 EXPECT_EQ(2u, results[2].first);
67 EXPECT_EQ('3', results[2].second);
61 ASSERT_EQ(3u, IntResults.size());
62 EXPECT_EQ(std::make_pair(0u, 1), IntResults[0]);
63 EXPECT_EQ(std::make_pair(1u, 2), IntResults[1]);
64 EXPECT_EQ(std::make_pair(2u, 3), IntResults[2]);
6865
69 results.clear();
66 // Test an empty range.
67 IntResults.clear();
7068 const std::vector baz;
7169 for (auto X : llvm::enumerate(baz)) {
72 results.push_back(std::make_pair(X.Index, X.Value));
70 IntResults.emplace_back(X.Index, X.Value);
7371 }
74 EXPECT_TRUE(baz.empty());
72 EXPECT_TRUE(IntResults.empty());
7573 }
7674
77 TEST(STLExtrasTest, EnumerateModify) {
75 TEST(STLExtrasTest, EnumerateModifyLValue) {
76 // Test that you can modify the underlying entries of an lvalue range through
77 // the enumeration iterator.
7878 std::vector foo = {'a', 'b', 'c'};
7979
8080 for (auto X : llvm::enumerate(foo)) {
8181 ++X.Value;
8282 }
83
8483 EXPECT_EQ('b', foo[0]);
8584 EXPECT_EQ('c', foo[1]);
8685 EXPECT_EQ('d', foo[2]);
8786 }
87
88 TEST(STLExtrasTest, EnumerateRValueRef) {
89 // Test that an rvalue can be enumerated.
90 std::vector> Results;
91
92 auto Enumerator = llvm::enumerate(std::vector{1, 2, 3});
93
94 for (auto X : llvm::enumerate(std::vector{1, 2, 3})) {
95 Results.emplace_back(X.Index, X.Value);
96 }
97
98 ASSERT_EQ(3u, Results.size());
99 EXPECT_EQ(std::make_pair(0u, 1), Results[0]);
100 EXPECT_EQ(std::make_pair(1u, 2), Results[1]);
101 EXPECT_EQ(std::make_pair(2u, 3), Results[2]);
88102 }
103
104 TEST(STLExtrasTest, EnumerateModifyRValue) {
105 // Test that when enumerating an rvalue, modification still works (even if
106 // this isn't terribly useful, it at least shows that we haven't snuck an
107 // extra const in there somewhere.
108 std::vector> Results;
109
110 for (auto X : llvm::enumerate(std::vector{'1', '2', '3'})) {
111 ++X.Value;
112 Results.emplace_back(X.Index, X.Value);
113 }
114
115 ASSERT_EQ(3u, Results.size());
116 EXPECT_EQ(std::make_pair(0u, '2'), Results[0]);
117 EXPECT_EQ(std::make_pair(1u, '3'), Results[1]);
118 EXPECT_EQ(std::make_pair(2u, '4'), Results[2]);
119 }
120
121 template struct CanMove {};
122 template <> struct CanMove {
123 CanMove(CanMove &&) = delete;
124
125 CanMove() = default;
126 CanMove(const CanMove &) = default;
127 };
128
129 template struct CanCopy {};
130 template <> struct CanCopy {
131 CanCopy(const CanCopy &) = delete;
132
133 CanCopy() = default;
134 CanCopy(CanCopy &&) = default;
135 };
136
137 template
138 struct Range : CanMove, CanCopy {
139 explicit Range(int &C, int &M, int &D) : C(C), M(M), D(D) {}
140 Range(const Range &R) : CanCopy(R), C(R.C), M(R.M), D(R.D) { ++C; }
141 Range(Range &&R) : CanMove(std::move(R)), C(R.C), M(R.M), D(R.D) {
142 ++M;
143 }
144 ~Range() { ++D; }
145
146 int &C;
147 int &M;
148 int &D;
149
150 int *begin() { return nullptr; }
151 int *end() { return nullptr; }
152 };
153
154 TEST(STLExtrasTest, EnumerateLifetimeSemantics) {
155 // Test that when enumerating lvalues and rvalues, there are no surprise
156 // copies or moves.
157
158 // With an rvalue, it should not be destroyed until the end of the scope.
159 int Copies = 0;
160 int Moves = 0;
161 int Destructors = 0;
162 {
163 auto E1 = enumerate(Range(Copies, Moves, Destructors));
164 // Doesn't compile. rvalue ranges must be moveable.
165 // auto E2 = enumerate(Range(Copies, Moves, Destructors));
166 EXPECT_EQ(0, Copies);
167 EXPECT_EQ(1, Moves);
168 EXPECT_EQ(1, Destructors);
169 }
170 EXPECT_EQ(0, Copies);
171 EXPECT_EQ(1, Moves);
172 EXPECT_EQ(2, Destructors);
173
174 Copies = Moves = Destructors = 0;
175 // With an lvalue, it should not be destroyed even after the end of the scope.
176 // lvalue ranges need be neither copyable nor moveable.
177 Range R(Copies, Moves, Destructors);
178 {
179 auto Enumerator = enumerate(R);
180 (void)Enumerator;
181 EXPECT_EQ(0, Copies);
182 EXPECT_EQ(0, Moves);
183 EXPECT_EQ(0, Destructors);
184 }
185 EXPECT_EQ(0, Copies);
186 EXPECT_EQ(0, Moves);
187 EXPECT_EQ(0, Destructors);
188 }
189 }