[llvm][ADT] Allow using structured bindings with `llvm::enumerate`

This patch adds the ability to deconstruct the `value_type` returned by `llvm::enumarate` into index and value of the wrapping range. Main use case is the common occurence of using it during loop iteration. After this patch it'd then be possible to write code such as:
```
for (auto [index, value] : enumerate(container)) {
   ...
}
```
where `index` is the current index and `value` a reference to elements in the given container.

Differential Revision: https://reviews.llvm.org/D131486
This commit is contained in:
Markus Böck 2022-08-09 18:07:50 +02:00
parent cc71e69459
commit 205701fd47
2 changed files with 79 additions and 25 deletions

View File

@ -1982,6 +1982,16 @@ private:
IterOfRange<R> Iter;
};
template <std::size_t i, typename R>
decltype(auto) get(const result_pair<R> &Pair) {
static_assert(i < 2);
if constexpr (i == 0) {
return Pair.index();
} else {
return Pair.value();
}
}
template <typename R>
class enumerator_iter
: public iterator_facade_base<enumerator_iter<R>, std::forward_iterator_tag,
@ -2054,6 +2064,12 @@ private:
/// printf("Item %d - %c\n", X.index(), X.value());
/// }
///
/// or using structured bindings:
///
/// for (auto [Index, Value] : enumerate(Items)) {
/// printf("Item %d - %c\n", Index, Value);
/// }
///
/// Output:
/// Item 0 - A
/// Item 1 - B
@ -2192,4 +2208,17 @@ template <class T> constexpr T *to_address(T *P) { return P; }
} // end namespace llvm
namespace std {
template <typename R>
struct tuple_size<llvm::detail::result_pair<R>>
: std::integral_constant<std::size_t, 2> {};
template <std::size_t i, typename R>
struct tuple_element<i, llvm::detail::result_pair<R>>
: std::conditional<i == 0, std::size_t,
typename llvm::detail::result_pair<R>::value_reference> {
};
} // namespace std
#endif // LLVM_ADT_STLEXTRAS_H

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <climits>
@ -15,6 +16,8 @@
using namespace llvm;
using testing::ElementsAre;
namespace {
int f(rank<0>) { return 0; }
@ -47,31 +50,29 @@ TEST(STLExtrasTest, EnumerateLValue) {
typedef std::pair<std::size_t, char> CharPairType;
std::vector<CharPairType> CharResults;
for (auto X : llvm::enumerate(foo)) {
CharResults.emplace_back(X.index(), X.value());
for (auto [index, value] : llvm::enumerate(foo)) {
CharResults.emplace_back(index, value);
}
ASSERT_EQ(3u, CharResults.size());
EXPECT_EQ(CharPairType(0u, 'a'), CharResults[0]);
EXPECT_EQ(CharPairType(1u, 'b'), CharResults[1]);
EXPECT_EQ(CharPairType(2u, 'c'), CharResults[2]);
EXPECT_THAT(CharResults,
ElementsAre(CharPairType(0u, 'a'), CharPairType(1u, 'b'),
CharPairType(2u, 'c')));
// Test a const range of a different type.
typedef std::pair<std::size_t, int> IntPairType;
std::vector<IntPairType> IntResults;
const std::vector<int> bar = {1, 2, 3};
for (auto X : llvm::enumerate(bar)) {
IntResults.emplace_back(X.index(), X.value());
for (auto [index, value] : llvm::enumerate(bar)) {
IntResults.emplace_back(index, value);
}
ASSERT_EQ(3u, IntResults.size());
EXPECT_EQ(IntPairType(0u, 1), IntResults[0]);
EXPECT_EQ(IntPairType(1u, 2), IntResults[1]);
EXPECT_EQ(IntPairType(2u, 3), IntResults[2]);
EXPECT_THAT(IntResults, ElementsAre(IntPairType(0u, 1), IntPairType(1u, 2),
IntPairType(2u, 3)));
// Test an empty range.
IntResults.clear();
const std::vector<int> baz{};
for (auto X : llvm::enumerate(baz)) {
IntResults.emplace_back(X.index(), X.value());
for (auto [index, value] : llvm::enumerate(baz)) {
IntResults.emplace_back(index, value);
}
EXPECT_TRUE(IntResults.empty());
}
@ -84,9 +85,15 @@ TEST(STLExtrasTest, EnumerateModifyLValue) {
for (auto X : llvm::enumerate(foo)) {
++X.value();
}
EXPECT_EQ('b', foo[0]);
EXPECT_EQ('c', foo[1]);
EXPECT_EQ('d', foo[2]);
EXPECT_THAT(foo, ElementsAre('b', 'c', 'd'));
// Also test if this works with structured bindings.
foo = {'a', 'b', 'c'};
for (auto [index, value] : llvm::enumerate(foo)) {
++value;
}
EXPECT_THAT(foo, ElementsAre('b', 'c', 'd'));
}
TEST(STLExtrasTest, EnumerateRValueRef) {
@ -100,10 +107,18 @@ TEST(STLExtrasTest, EnumerateRValueRef) {
Results.emplace_back(X.index(), X.value());
}
ASSERT_EQ(3u, Results.size());
EXPECT_EQ(PairType(0u, 1), Results[0]);
EXPECT_EQ(PairType(1u, 2), Results[1]);
EXPECT_EQ(PairType(2u, 3), Results[2]);
EXPECT_THAT(Results,
ElementsAre(PairType(0u, 1), PairType(1u, 2), PairType(2u, 3)));
// Also test if this works with structured bindings.
Results.clear();
for (auto [index, value] : llvm::enumerate(std::vector<int>{1, 2, 3})) {
Results.emplace_back(index, value);
}
EXPECT_THAT(Results,
ElementsAre(PairType(0u, 1), PairType(1u, 2), PairType(2u, 3)));
}
TEST(STLExtrasTest, EnumerateModifyRValue) {
@ -118,10 +133,20 @@ TEST(STLExtrasTest, EnumerateModifyRValue) {
Results.emplace_back(X.index(), X.value());
}
ASSERT_EQ(3u, Results.size());
EXPECT_EQ(PairType(0u, '2'), Results[0]);
EXPECT_EQ(PairType(1u, '3'), Results[1]);
EXPECT_EQ(PairType(2u, '4'), Results[2]);
EXPECT_THAT(Results, ElementsAre(PairType(0u, '2'), PairType(1u, '3'),
PairType(2u, '4')));
// Also test if this works with structured bindings.
Results.clear();
for (auto [index, value] :
llvm::enumerate(std::vector<char>{'1', '2', '3'})) {
++value;
Results.emplace_back(index, value);
}
EXPECT_THAT(Results, ElementsAre(PairType(0u, '2'), PairType(1u, '3'),
PairType(2u, '4')));
}
template <bool B> struct CanMove {};