Add enum iteration to HIDL.
e.x.:
for (const auto v : hidl_enum_iterator<Foo>) {
...
}
Going with exposing an iterator interface completely
in the header because:
- this should only increase code size if it is used
- we want to be able to change this interface/implementation
to be more efficient or to add new features without having
to update all prebuilts
Bug: 68715899
Test: hidl_test
Change-Id: I613d5c71ebf3f58c5c92338888ad90172896178d
diff --git a/EnumType.cpp b/EnumType.cpp
index 0095165..98aa7c1 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -283,6 +283,35 @@
out << "enum class " << localName() << " : " << storageType << ";\n";
}
+void EnumType::emitIteratorDeclaration(Formatter& out) const {
+ size_t elementCount = 0;
+ for (const auto* type : typeChain()) {
+ elementCount += type->mValues.size();
+ }
+
+ out << "template<> struct hidl_enum_iterator<" << getCppStackType() << ">\n";
+ out.block([&] {
+ out << "const " << getCppStackType() << "* begin() { return static_begin(); }\n";
+ out << "const " << getCppStackType() << "* end() { return begin() + " << elementCount
+ << "; }\n";
+ out << "private:\n";
+ out << "static const " << getCppStackType() << "* static_begin() ";
+ out.block([&] {
+ out << "static const " << getCppStackType() << " kVals[" << elementCount << "] ";
+ out.block([&] {
+ auto enumerators = typeChain();
+ std::reverse(enumerators.begin(), enumerators.end());
+ for (const auto* type : enumerators) {
+ for (const auto* enumValue : type->mValues) {
+ out << fullName() << "::" << enumValue->name() << ",\n";
+ }
+ }
+ }) << ";\n";
+ out << "return &kVals[0];\n";
+ });
+ }) << ";\n\n";
+}
+
void EnumType::emitEnumBitwiseOperator(
Formatter &out,
bool lhsIsEnum,
@@ -348,6 +377,16 @@
out << "}\n\n";
}
+void EnumType::emitGlobalTypeDeclarations(Formatter& out) const {
+ out << "namespace android {\n";
+ out << "namespace hardware {\n";
+
+ emitIteratorDeclaration(out);
+
+ out << "} // namespace hardware\n";
+ out << "} // namespace android\n";
+}
+
status_t EnumType::emitPackageTypeDeclarations(Formatter& out) const {
emitEnumBitwiseOperator(out, true /* lhsIsEnum */, true /* rhsIsEnum */, "|");
emitEnumBitwiseOperator(out, false /* lhsIsEnum */, true /* rhsIsEnum */, "|");
diff --git a/EnumType.h b/EnumType.h
index c6cbbde..7e7a6dc 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -92,6 +92,7 @@
status_t emitTypeDeclarations(Formatter &out) const override;
void emitTypeForwardDeclaration(Formatter& out) const override;
+ void emitGlobalTypeDeclarations(Formatter& out) const override;
status_t emitPackageTypeDeclarations(Formatter& out) const override;
status_t emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
@@ -119,6 +120,9 @@
const Annotation *findExportAnnotation() const;
+ void emitIteratorDeclaration(Formatter& out) const;
+ void emitIteratorDefinitions(Formatter& out) const;
+
void emitEnumBitwiseOperator(
Formatter &out,
bool lhsIsEnum,
diff --git a/test/hidl_test/hidl_test_client.cpp b/test/hidl_test/hidl_test_client.cpp
index 797d914..fbb70fa 100644
--- a/test/hidl_test/hidl_test_client.cpp
+++ b/test/hidl_test/hidl_test_client.cpp
@@ -134,6 +134,9 @@
using std::to_string;
template <typename T>
+using hidl_enum_iterator = ::android::hardware::hidl_enum_iterator<T>;
+
+template <typename T>
static inline ::testing::AssertionResult isOk(const ::android::hardware::Return<T> &ret) {
return ret.isOk()
? (::testing::AssertionSuccess() << ret.description())
@@ -459,6 +462,38 @@
EXPECT_NE(nullptr, IFoo::getService("\n", true /* getStub */).get());
}
+TEST_F(HidlTest, EnumIteratorTest) {
+ using Empty = ::android::hardware::tests::foo::V1_0::EnumIterators::Empty;
+ using Grandchild = ::android::hardware::tests::foo::V1_0::EnumIterators::Grandchild;
+ using SkipsValues = ::android::hardware::tests::foo::V1_0::EnumIterators::SkipsValues;
+ using MultipleValues = ::android::hardware::tests::foo::V1_0::EnumIterators::MultipleValues;
+
+ for (const auto value : hidl_enum_iterator<Empty>()) {
+ (void)value;
+ EXPECT_TRUE(false) << "Empty iterator should not iterate";
+ }
+
+ auto it1 = hidl_enum_iterator<Grandchild>().begin();
+ EXPECT_EQ(Grandchild::A, *it1++);
+ EXPECT_EQ(Grandchild::B, *it1++);
+ EXPECT_EQ(hidl_enum_iterator<Grandchild>().end(), it1);
+
+ auto it2 = hidl_enum_iterator<SkipsValues>().begin();
+ EXPECT_EQ(SkipsValues::A, *it2++);
+ EXPECT_EQ(SkipsValues::B, *it2++);
+ EXPECT_EQ(SkipsValues::C, *it2++);
+ EXPECT_EQ(SkipsValues::D, *it2++);
+ EXPECT_EQ(SkipsValues::E, *it2++);
+ EXPECT_EQ(hidl_enum_iterator<SkipsValues>().end(), it2);
+
+ auto it3 = hidl_enum_iterator<MultipleValues>().begin();
+ EXPECT_EQ(MultipleValues::A, *it3++);
+ EXPECT_EQ(MultipleValues::B, *it3++);
+ EXPECT_EQ(MultipleValues::C, *it3++);
+ EXPECT_EQ(MultipleValues::D, *it3++);
+ EXPECT_EQ(hidl_enum_iterator<MultipleValues>().end(), it3);
+}
+
TEST_F(HidlTest, EnumToStringTest) {
using namespace std::string_literals;
using ::android::hardware::tests::foo::V1_0::toString;