Commit 891965f5 authored by Rodrigo Valle's avatar Rodrigo Valle Committed by Facebook GitHub Bot

Add support for structured binding to folly::enumerate

Summary:
Adding support for de-structuring the Proxy object inside a for loop, this will let users write code that looks like the following while still supporting the existing interface.

```
for (const auto& [index, element] : folly::enumerate(collection)) {
    std::cout << index << ": " << element;
}
```

Also: update an existing unittest for c++17

Please let me know if there's anything I'm failing to consider/you disagree with in this change and I'll do my best to make it right.

Reviewed By: yfeldblum, ot

Differential Revision: D23948526

fbshipit-source-id: 6da83c9e9e0e911e33c6febc08ad2fd5b06498eb
parent 4d450003
...@@ -29,6 +29,13 @@ ...@@ -29,6 +29,13 @@
* *
* For example: * For example:
* *
* for (const auto& [index, element] : folly::enumerate(vec)) {
* // index is a reference to a size_t
* // element is a reference to the type contained within vec
* }
*
* It can also be used as follows:
*
* for (auto&& it : folly::enumerate(vec)) { * for (auto&& it : folly::enumerate(vec)) {
* // *it is a reference to the current element. Const if vec is const. * // *it is a reference to the current element. Const if vec is const.
* // it->member can be used as well. * // it->member can be used as well.
...@@ -61,20 +68,6 @@ struct MakeConst<T*> { ...@@ -61,20 +68,6 @@ struct MakeConst<T*> {
using type = const T*; using type = const T*;
}; };
// Raw pointers don't have an operator->() member function, so the
// second overload will be SFINAEd out in that case. Otherwise, the
// second is preferred in the partial order for getPointer(_, 0).
template <class Iterator>
FOLLY_ALWAYS_INLINE auto getPointer(const Iterator& it, long)
-> decltype(std::addressof(*it)) {
return std::addressof(*it);
}
template <class Iterator>
FOLLY_ALWAYS_INLINE auto getPointer(const Iterator& it, int)
-> decltype(it.operator->()) {
return it.operator->();
}
template <class Iterator> template <class Iterator>
class Enumerator { class Enumerator {
public: public:
...@@ -89,29 +82,27 @@ class Enumerator { ...@@ -89,29 +82,27 @@ class Enumerator {
using iterator_category = std::input_iterator_tag; using iterator_category = std::input_iterator_tag;
FOLLY_ALWAYS_INLINE explicit Proxy(const Enumerator& e) FOLLY_ALWAYS_INLINE explicit Proxy(const Enumerator& e)
: it_(e.it_), index(e.idx_) {} : index(e.idx_), element(*e.it_) {}
// Non-const Proxy: Forward constness from Iterator. // Non-const Proxy: Forward constness from Iterator.
FOLLY_ALWAYS_INLINE reference operator*() { FOLLY_ALWAYS_INLINE reference operator*() {
return *it_; return element;
} }
FOLLY_ALWAYS_INLINE pointer operator->() { FOLLY_ALWAYS_INLINE pointer operator->() {
return getPointer(it_, 0); return std::addressof(element);
} }
// Const Proxy: Force const references. // Const Proxy: Force const references.
FOLLY_ALWAYS_INLINE typename MakeConst<reference>::type operator*() const { FOLLY_ALWAYS_INLINE typename MakeConst<reference>::type operator*() const {
return *it_; return element;
} }
FOLLY_ALWAYS_INLINE typename MakeConst<pointer>::type operator->() const { FOLLY_ALWAYS_INLINE typename MakeConst<pointer>::type operator->() const {
return getPointer(it_, 0); return std::addressof(element);
} }
private:
const Iterator& it_;
public: public:
const size_t index; const size_t index;
reference element;
}; };
FOLLY_ALWAYS_INLINE Proxy operator*() const { FOLLY_ALWAYS_INLINE Proxy operator*() const {
......
...@@ -82,6 +82,24 @@ ENUMERATE_TEST_BASIC_CONST(const auto&&, BasicConstRRef) ...@@ -82,6 +82,24 @@ ENUMERATE_TEST_BASIC_CONST(const auto&&, BasicConstRRef)
#undef ENUMERATE_TEST_BASIC_CONST #undef ENUMERATE_TEST_BASIC_CONST
#define ENUMERATE_TEST_BASIC_VECTOR_BOOL(DECL, NAME) \
TEST(Enumerate, NAME) { \
std::vector<bool> v = {true, false, false, true}; \
size_t i = 0; \
for (DECL it : folly::enumerate(v)) { \
EXPECT_EQ(it.index, i); \
EXPECT_EQ(*it, v[i]); \
++i; \
} \
\
EXPECT_EQ(i, v.size()); \
}
ENUMERATE_TEST_BASIC_VECTOR_BOOL(auto, BasicVecBool)
ENUMERATE_TEST_BASIC_VECTOR_BOOL(auto&&, BasicVecBoolRRef)
#undef ENUMERATE_TEST_BASIC_VECTOR_BOOL
TEST(Enumerate, Temporary) { TEST(Enumerate, Temporary) {
std::vector<std::string> v = {"abc", "a", "ab"}; std::vector<std::string> v = {"abc", "a", "ab"};
size_t i = 0; size_t i = 0;
...@@ -167,18 +185,37 @@ bool operator==(const char* c, CStringRange::Sentinel) { ...@@ -167,18 +185,37 @@ bool operator==(const char* c, CStringRange::Sentinel) {
TEST(Enumerate, Cpp17Support) { TEST(Enumerate, Cpp17Support) {
std::array<char, 5> test = {"test"}; std::array<char, 5> test = {"test"};
// Can't use range based for loop until C++17, so test manually for (const auto&& it : folly::enumerate(CStringRange{test.data()})) {
// Equivalent to: ASSERT_LT(it.index, test.size());
// for (const auto&& it : folly::enumerate(CStringRange{test.data()})) { ... } EXPECT_EQ(*it, test[it.index]);
{ }
auto&& enumerate = folly::enumerate(CStringRange{test.data()}); }
auto begin = enumerate.begin();
auto end = enumerate.end(); TEST(Enumerate, Cpp17StructuredBinding) {
for (; begin != end; ++begin) { std::vector<std::string> test = {"abc", "a", "ab"};
const auto&& it = *begin; for (const auto& [index, str] : folly::enumerate(test)) {
ASSERT_LT(index, test.size());
ASSERT_LT(it.index, test.size()); EXPECT_EQ(str, test[index]);
EXPECT_EQ(*it, test[it.index]); }
} }
TEST(Enumerate, Cpp17StructuredBindingConstVector) {
const std::vector<std::string> test = {"abc", "a", "ab"};
for (auto&& [index, str] : folly::enumerate(test)) {
static_assert(
IsConstReference<decltype(str)>::value, "Enumerating const vector");
ASSERT_LT(index, test.size());
EXPECT_EQ(str, test[index]);
}
}
TEST(Enumerate, Cpp17StructuredBindingModify) {
std::vector<int> test = {1, 2, 3, 4, 5};
for (auto&& [index, integer] : folly::enumerate(test)) {
integer = 0;
}
for (const auto& integer : test) {
EXPECT_EQ(integer, 0);
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment