Skip to content

Commit 397d5d9

Browse files
sbenzaquencopybara-github
authored andcommitted
Add EnumerateEnumValues function.
It generates an iterable object that yields all the labels for the enum. PiperOrigin-RevId: 859109360
1 parent 7d6f8ef commit 397d5d9

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

src/google/protobuf/generated_enum_reflection.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
#ifndef GOOGLE_PROTOBUF_GENERATED_ENUM_REFLECTION_H__
1717
#define GOOGLE_PROTOBUF_GENERATED_ENUM_REFLECTION_H__
1818

19+
#include <cstddef>
20+
#include <iterator>
1921
#include <string>
22+
#include <type_traits>
2023

24+
#include "absl/log/absl_check.h"
2125
#include "absl/strings/string_view.h"
2226
#include "google/protobuf/generated_enum_util.h"
2327
#include "google/protobuf/port.h"
@@ -72,7 +76,79 @@ PROTOBUF_FUTURE_ADD_EARLY_NODISCARD
7276
PROTOBUF_EXPORT const std::string& NameOfEnum(
7377
const EnumDescriptor* PROTOBUF_NONNULL descriptor, int value);
7478

79+
template <typename Enum>
80+
class EnumeratedEnumView {
81+
// Make the type dependent to avoid eager instantiations.
82+
// EnumDescriptor is incomplete at this point, but will be complete when this
83+
// template is instantiated.
84+
using Desc = std::enable_if_t<sizeof(Enum) != 0, EnumDescriptor>;
85+
86+
public:
87+
using value_type = Enum;
88+
class iterator {
89+
public:
90+
using difference_type = ptrdiff_t;
91+
using value_type = Enum;
92+
using pointer = value_type*;
93+
using reference = value_type&;
94+
using iterator_category = std::input_iterator_tag;
95+
96+
iterator() : desc_(nullptr), index_(0) {}
97+
iterator(const iterator&) = default;
98+
iterator& operator=(const iterator&) = default;
99+
100+
friend bool operator==(iterator a, iterator b) {
101+
ABSL_DCHECK_EQ(a.desc_, b.desc_);
102+
return a.index_ == b.index_;
103+
}
104+
friend bool operator!=(iterator a, iterator b) { return !(a == b); }
105+
106+
Enum operator*() const {
107+
return static_cast<Enum>(desc_->value(index_)->number());
108+
}
109+
iterator& operator++() {
110+
++index_;
111+
return *this;
112+
}
113+
iterator operator++(int) {
114+
auto copy = *this;
115+
++*this;
116+
return copy;
117+
}
118+
119+
private:
120+
friend EnumeratedEnumView;
121+
iterator(const Desc* PROTOBUF_NONNULL desc, int index)
122+
: desc_(desc), index_(index) {}
123+
const Desc* PROTOBUF_NONNULL desc_;
124+
int index_;
125+
};
126+
using const_iterator = iterator;
127+
128+
// Proto enums are never empty.
129+
bool empty() const { return false; }
130+
131+
size_t size() const { return desc_->value_count(); }
132+
133+
iterator begin() const { return iterator(desc_, 0); }
134+
iterator end() const { return iterator(desc_, size()); }
135+
iterator cbegin() const { return begin(); }
136+
iterator cend() const { return end(); }
137+
138+
private:
139+
const Desc* PROTOBUF_NONNULL desc_ = GetEnumDescriptor<Enum>();
140+
};
141+
75142
} // namespace internal
143+
144+
// Returns an iterable object that will yield all the enum labels for `Enum` in
145+
// the same order as specified in their EnumDescriptor.
146+
// The returned type is unspecified.
147+
template <typename Enum>
148+
auto EnumerateEnumValues() {
149+
return internal::EnumeratedEnumView<Enum>{};
150+
}
151+
76152
} // namespace protobuf
77153
} // namespace google
78154

src/google/protobuf/message_unittest.inc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <cstdint>
2323
#include <limits>
2424
#include <string>
25+
#include <type_traits>
2526

2627
#ifndef _MSC_VER
2728
#include <unistd.h>
@@ -61,6 +62,7 @@
6162
// Must be included last.
6263
#include "google/protobuf/port_def.inc"
6364

65+
using ::testing::ElementsAre;
6466
using ::testing::IsEmpty;
6567
using ::testing::Not;
6668

@@ -2227,6 +2229,48 @@ TEST(MESSAGE_TEST_NAME, AllAddMethodsOnRepeatedStringField) {
22272229
msg.clear_repeated_string();
22282230
}
22292231

2232+
TEST(MESSAGE_TEST_NAME, EnumerateEnumValuesTest) {
2233+
using M = UNITTEST::TestAllTypes;
2234+
using E = M::NestedEnum;
2235+
auto view = google::protobuf::EnumerateEnumValues<E>();
2236+
2237+
EXPECT_FALSE(view.empty());
2238+
EXPECT_EQ(4, view.size());
2239+
2240+
std::vector<E> values;
2241+
for (auto e : view) values.push_back(e);
2242+
EXPECT_THAT(values, ElementsAre(M::FOO, M::BAR, M::BAZ, M::NEG));
2243+
2244+
// Again, but let std::vector do the iteration to make sure they really follow
2245+
// the contract.
2246+
values = std::vector<E>(view.begin(), view.end());
2247+
EXPECT_THAT(values, ElementsAre(M::FOO, M::BAR, M::BAZ, M::NEG));
2248+
}
2249+
2250+
TEST(MESSAGE_TEST_NAME, EnumerateEnumValuesIteratorTest) {
2251+
using M = UNITTEST::TestAllTypes;
2252+
using E = M::NestedEnum;
2253+
auto view = google::protobuf::EnumerateEnumValues<E>();
2254+
// Pre Increment
2255+
auto it = view.begin();
2256+
EXPECT_EQ(M::FOO, *it);
2257+
EXPECT_EQ(M::BAR, *++it);
2258+
2259+
// operator==
2260+
auto it2 = view.begin();
2261+
EXPECT_FALSE(it == it2);
2262+
EXPECT_TRUE(it != it2);
2263+
++it2;
2264+
EXPECT_FALSE(it != it2);
2265+
EXPECT_TRUE(it == it2);
2266+
2267+
// Post increment
2268+
it = view.begin();
2269+
EXPECT_EQ(M::FOO, *it);
2270+
EXPECT_EQ(M::FOO, *it++);
2271+
EXPECT_EQ(M::BAR, *it);
2272+
}
2273+
22302274

22312275
} // namespace protobuf
22322276
} // namespace google

0 commit comments

Comments
 (0)