Sync list props behavior between Java and C++
C++ list props will have type std::vector<std::optional<element_type>>
which can have std::nullopt as an element, just like null on Java. And
empty list/vector(not null/std::nullopt) will be returned when sysprop
value is empty.
Bug: 80125326
Test: mma -j && sysprop_test
Change-Id: Ia638636a7dbf4c96443ee36c746408f0ba8e34e4
diff --git a/CppGen.cpp b/CppGen.cpp
index 7217041..c2fbe9e 100644
--- a/CppGen.cpp
+++ b/CppGen.cpp
@@ -110,19 +110,15 @@
return *str == '\0' ? std::nullopt : std::make_optional(str);
}
-template <typename Vec> [[maybe_unused]] std::optional<Vec> DoParseList(const char* str) {
+template <typename Vec> [[maybe_unused]] Vec DoParseList(const char* str) {
Vec ret;
for (auto&& element : android::base::Split(str, ",")) {
- auto parsed = DoParse<typename Vec::value_type>(element.c_str());
- if (!parsed) {
- return std::nullopt;
- }
- ret.emplace_back(std::move(*parsed));
+ ret.emplace_back(DoParse<typename Vec::value_type>(element.c_str());
}
- return std::make_optional(std::move(ret));
+ return ret;
}
-template <typename T> inline std::optional<T> TryParse(const char* str) {
+template <typename T> inline T TryParse(const char* str) {
if constexpr(is_vector<T>) {
return DoParseList<T>(str);
} else {
@@ -130,20 +126,22 @@
}
}
-[[maybe_unused]] std::string FormatValue(std::int32_t value) {
- return std::to_string(value);
+[[maybe_unused]] std::string FormatValue(const std::optional<std::int32_t>& value) {
+ return value ? std::to_string(*value) : "";
}
-[[maybe_unused]] std::string FormatValue(std::int64_t value) {
- return std::to_string(value);
+[[maybe_unused]] std::string FormatValue(const std::optional<std::int64_t>& value) {
+ return value ? std::to_string(*value) : "";
}
-[[maybe_unused]] std::string FormatValue(double value) {
- return android::base::StringPrintf("%.*g", std::numeric_limits<double>::max_digits10, value);
+[[maybe_unused]] std::string FormatValue(const std::optional<double>& value) {
+ return value
+ ? android::base::StringPrintf("%.*g", std::numeric_limits<double>::max_digits10, *value)
+ : "";
}
-[[maybe_unused]] std::string FormatValue(bool value) {
- return value ? "true" : "false";
+[[maybe_unused]] std::string FormatValue(const std::optional<bool>& value) {
+ return value ? (*value ? "true" : "false") : "";
}
template <typename T>
@@ -153,9 +151,9 @@
std::string ret;
for (auto&& element : value) {
- if (ret.empty()) ret.push_back(',');
- if constexpr(std::is_same_v<T, std::string>) {
- ret += element;
+ if (ret.empty()) ret += ',';
+ if constexpr(std::is_same_v<T, std::optional<std::string>>) {
+ if (element) ret += *element;
} else {
ret += FormatValue(element);
}
@@ -165,13 +163,14 @@
}
template <typename T>
-std::optional<T> GetProp(const char* key) {
+T GetProp(const char* key) {
+ T ret;
auto pi = __system_property_find(key);
- if (pi == nullptr) return std::nullopt;
- std::optional<T> ret;
- __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
- *static_cast<std::optional<T>*>(cookie) = TryParse<T>(value);
- }, &ret);
+ if (pi != nullptr) {
+ __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
+ *static_cast<T*>(cookie) = TryParse<T>(value);
+ }, &ret);
+ }
return ret;
}
@@ -203,29 +202,29 @@
std::string GetCppPropTypeName(const sysprop::Property& prop) {
switch (prop.type()) {
case sysprop::Boolean:
- return "bool";
+ return "std::optional<bool>";
case sysprop::Integer:
- return "std::int32_t";
+ return "std::optional<std::int32_t>";
case sysprop::Long:
- return "std::int64_t";
+ return "std::optional<std::int64_t>";
case sysprop::Double:
- return "double";
+ return "std::optional<double>";
case sysprop::String:
- return "std::string";
+ return "std::optional<std::string>";
case sysprop::Enum:
- return GetCppEnumName(prop);
+ return "std::optional<" + GetCppEnumName(prop) + ">";
case sysprop::BooleanList:
- return "std::vector<bool>";
+ return "std::vector<std::optional<bool>>";
case sysprop::IntegerList:
- return "std::vector<std::int32_t>";
+ return "std::vector<std::optional<std::int32_t>>";
case sysprop::LongList:
- return "std::vector<std::int64_t>";
+ return "std::vector<std::optional<std::int64_t>>";
case sysprop::DoubleList:
- return "std::vector<double>";
+ return "std::vector<std::optional<double>>";
case sysprop::StringList:
- return "std::vector<std::string>";
+ return "std::vector<std::optional<std::string>>";
case sysprop::EnumList:
- return "std::vector<" + GetCppEnumName(prop) + ">";
+ return "std::vector<std::optional<" + GetCppEnumName(prop) + ">>";
default:
__builtin_unreachable();
}
@@ -267,8 +266,7 @@
writer.Write("};\n\n");
}
- writer.Write("std::optional<%s> %s();\n", prop_type.c_str(),
- prop_id.c_str());
+ writer.Write("%s %s();\n", prop_type.c_str(), prop_id.c_str());
if (prop.access() != sysprop::Readonly) {
writer.Write("bool %s(const %s& value);\n", prop_id.c_str(),
prop_type.c_str());
@@ -295,8 +293,7 @@
writer.Write("namespace {\n\n");
writer.Write("using namespace %s;\n\n", cpp_namespace.c_str());
- writer.Write(
- "template <typename T> std::optional<T> DoParse(const char* str);\n\n");
+ writer.Write("template <typename T> T DoParse(const char* str);\n\n");
for (int i = 0; i < props.prop_size(); ++i) {
const sysprop::Property& prop = props.prop(i);
@@ -336,11 +333,13 @@
writer.Write("}\n\n");
if (prop.access() != sysprop::Readonly) {
- writer.Write("std::string FormatValue(%s value) {\n", enum_name.c_str());
+ writer.Write("std::string FormatValue(std::optional<%s> value) {\n",
+ enum_name.c_str());
writer.Indent();
+ writer.Write("if (!value) return \"\";\n");
writer.Write("for (auto [name, val] : %s_list) {\n", prop_id.c_str());
writer.Indent();
- writer.Write("if (val == value) {\n");
+ writer.Write("if (val == *value) {\n");
writer.Indent();
writer.Write("return name;\n");
writer.Dedent();
@@ -350,7 +349,7 @@
writer.Write(
"LOG(FATAL) << \"Invalid value \" << "
- "static_cast<std::int32_t>(value) << "
+ "static_cast<std::int32_t>(*value) << "
"\" for property \" << \"%s\";\n",
prop.prop_name().c_str());
@@ -371,12 +370,7 @@
std::string prop_id = ApiNameToIdentifier(prop.api_name());
std::string prop_type = GetCppPropTypeName(prop);
- if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
- std::string enum_name = GetCppEnumName(prop);
- }
-
- writer.Write("std::optional<%s> %s() {\n", prop_type.c_str(),
- prop_id.c_str());
+ writer.Write("%s %s() {\n", prop_type.c_str(), prop_id.c_str());
writer.Indent();
writer.Write("return GetProp<%s>(\"%s\");\n", prop_type.c_str(),
prop.prop_name().c_str());
diff --git a/JavaGen.cpp b/JavaGen.cpp
index 0e4a570..c3a7154 100644
--- a/JavaGen.cpp
+++ b/JavaGen.cpp
@@ -97,7 +97,7 @@
}
private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) {
- if (str == null || str.equals("")) return null;
+ if (str == null || str.equals("")) return new ArrayList<>();
List<T> ret = new ArrayList<>();
@@ -109,7 +109,7 @@
}
private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) {
- if (str == null || str.equals("")) return null;
+ if (str == null || str.equals("")) return new ArrayList<>();
List<T> ret = new ArrayList<>();
diff --git a/tests/CppGenTest.cpp b/tests/CppGenTest.cpp
index 595c5b5..e610a23 100644
--- a/tests/CppGenTest.cpp
+++ b/tests/CppGenTest.cpp
@@ -28,7 +28,7 @@
constexpr const char* kTestSyspropFile =
R"(owner: Platform
-module: "android.PlatformProperties"
+module: "android.sysprop.PlatformProperties"
prop {
api_name: "test_double"
@@ -105,24 +105,24 @@
constexpr const char* kExpectedHeaderOutput =
R"(// Generated by the sysprop generator. DO NOT EDIT!
-#ifndef SYSPROPGEN_android_PlatformProperties_H_
-#define SYSPROPGEN_android_PlatformProperties_H_
+#ifndef SYSPROPGEN_android_sysprop_PlatformProperties_H_
+#define SYSPROPGEN_android_sysprop_PlatformProperties_H_
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
-namespace android::PlatformProperties {
+namespace android::sysprop::PlatformProperties {
std::optional<double> test_double();
-bool test_double(const double& value);
+bool test_double(const std::optional<double>& value);
std::optional<std::int32_t> test_int();
-bool test_int(const std::int32_t& value);
+bool test_int(const std::optional<std::int32_t>& value);
std::optional<std::string> test_string();
-bool test_string(const std::string& value);
+bool test_string(const std::optional<std::string>& value);
enum class test_enum_values {
a,
@@ -135,22 +135,22 @@
};
std::optional<test_enum_values> test_enum();
-bool test_enum(const test_enum_values& value);
+bool test_enum(const std::optional<test_enum_values>& value);
std::optional<bool> test_BOOLeaN();
-bool test_BOOLeaN(const bool& value);
+bool test_BOOLeaN(const std::optional<bool>& value);
std::optional<std::int64_t> android_os_test_long();
-bool android_os_test_long(const std::int64_t& value);
+bool android_os_test_long(const std::optional<std::int64_t>& value);
-std::optional<std::vector<double>> test_double_list();
-bool test_double_list(const std::vector<double>& value);
+std::vector<std::optional<double>> test_double_list();
+bool test_double_list(const std::vector<std::optional<double>>& value);
-std::optional<std::vector<std::int32_t>> test_list_int();
-bool test_list_int(const std::vector<std::int32_t>& value);
+std::vector<std::optional<std::int32_t>> test_list_int();
+bool test_list_int(const std::vector<std::optional<std::int32_t>>& value);
-std::optional<std::vector<std::string>> test_strlist();
-bool test_strlist(const std::vector<std::string>& value);
+std::vector<std::optional<std::string>> test_strlist();
+bool test_strlist(const std::vector<std::optional<std::string>>& value);
enum class el_values {
enu,
@@ -158,12 +158,12 @@
lue,
};
-std::optional<std::vector<el_values>> el();
-bool el(const std::vector<el_values>& value);
+std::vector<std::optional<el_values>> el();
+bool el(const std::vector<std::optional<el_values>>& value);
-} // namespace android::PlatformProperties
+} // namespace android::sysprop::PlatformProperties
-#endif // SYSPROPGEN_android_PlatformProperties_H_
+#endif // SYSPROPGEN_android_sysprop_PlatformProperties_H_
)";
constexpr const char* kExpectedSourceOutput =
@@ -186,9 +186,9 @@
namespace {
-using namespace android::PlatformProperties;
+using namespace android::sysprop::PlatformProperties;
-template <typename T> std::optional<T> DoParse(const char* str);
+template <typename T> T DoParse(const char* str);
constexpr const std::pair<const char*, test_enum_values> test_enum_list[] = {
{"a", test_enum_values::a},
@@ -210,13 +210,14 @@
return std::nullopt;
}
-std::string FormatValue(test_enum_values value) {
+std::string FormatValue(std::optional<test_enum_values> value) {
+ if (!value) return "";
for (auto [name, val] : test_enum_list) {
- if (val == value) {
+ if (val == *value) {
return name;
}
}
- LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(value) << " for property " << "android.test.enum";
+ LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(*value) << " for property " << "android.test.enum";
__builtin_unreachable();
}
@@ -236,13 +237,14 @@
return std::nullopt;
}
-std::string FormatValue(el_values value) {
+std::string FormatValue(std::optional<el_values> value) {
+ if (!value) return "";
for (auto [name, val] : el_list) {
- if (val == value) {
+ if (val == *value) {
return name;
}
}
- LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(value) << " for property " << "el";
+ LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(*value) << " for property " << "el";
__builtin_unreachable();
}
@@ -297,19 +299,15 @@
return *str == '\0' ? std::nullopt : std::make_optional(str);
}
-template <typename Vec> [[maybe_unused]] std::optional<Vec> DoParseList(const char* str) {
+template <typename Vec> [[maybe_unused]] Vec DoParseList(const char* str) {
Vec ret;
for (auto&& element : android::base::Split(str, ",")) {
- auto parsed = DoParse<typename Vec::value_type>(element.c_str());
- if (!parsed) {
- return std::nullopt;
- }
- ret.emplace_back(std::move(*parsed));
+ ret.emplace_back(DoParse<typename Vec::value_type>(element.c_str());
}
- return std::make_optional(std::move(ret));
+ return ret;
}
-template <typename T> inline std::optional<T> TryParse(const char* str) {
+template <typename T> inline T TryParse(const char* str) {
if constexpr(is_vector<T>) {
return DoParseList<T>(str);
} else {
@@ -317,20 +315,22 @@
}
}
-[[maybe_unused]] std::string FormatValue(std::int32_t value) {
- return std::to_string(value);
+[[maybe_unused]] std::string FormatValue(const std::optional<std::int32_t>& value) {
+ return value ? std::to_string(*value) : "";
}
-[[maybe_unused]] std::string FormatValue(std::int64_t value) {
- return std::to_string(value);
+[[maybe_unused]] std::string FormatValue(const std::optional<std::int64_t>& value) {
+ return value ? std::to_string(*value) : "";
}
-[[maybe_unused]] std::string FormatValue(double value) {
- return android::base::StringPrintf("%.*g", std::numeric_limits<double>::max_digits10, value);
+[[maybe_unused]] std::string FormatValue(const std::optional<double>& value) {
+ return value
+ ? android::base::StringPrintf("%.*g", std::numeric_limits<double>::max_digits10, *value)
+ : "";
}
-[[maybe_unused]] std::string FormatValue(bool value) {
- return value ? "true" : "false";
+[[maybe_unused]] std::string FormatValue(const std::optional<bool>& value) {
+ return value ? (*value ? "true" : "false") : "";
}
template <typename T>
@@ -340,9 +340,9 @@
std::string ret;
for (auto&& element : value) {
- if (ret.empty()) ret.push_back(',');
- if constexpr(std::is_same_v<T, std::string>) {
- ret += element;
+ if (ret.empty()) ret += ',';
+ if constexpr(std::is_same_v<T, std::optional<std::string>>) {
+ if (element) ret += *element;
} else {
ret += FormatValue(element);
}
@@ -352,101 +352,102 @@
}
template <typename T>
-std::optional<T> GetProp(const char* key) {
+T GetProp(const char* key) {
+ T ret;
auto pi = __system_property_find(key);
- if (pi == nullptr) return std::nullopt;
- std::optional<T> ret;
- __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
- *static_cast<std::optional<T>*>(cookie) = TryParse<T>(value);
- }, &ret);
+ if (pi != nullptr) {
+ __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
+ *static_cast<T*>(cookie) = TryParse<T>(value);
+ }, &ret);
+ }
return ret;
}
} // namespace
-namespace android::PlatformProperties {
+namespace android::sysprop::PlatformProperties {
std::optional<double> test_double() {
- return GetProp<double>("android.test_double");
+ return GetProp<std::optional<double>>("android.test_double");
}
-bool test_double(const double& value) {
+bool test_double(const std::optional<double>& value) {
return __system_property_set("android.test_double", FormatValue(value).c_str()) == 0;
}
std::optional<std::int32_t> test_int() {
- return GetProp<std::int32_t>("android.test_int");
+ return GetProp<std::optional<std::int32_t>>("android.test_int");
}
-bool test_int(const std::int32_t& value) {
+bool test_int(const std::optional<std::int32_t>& value) {
return __system_property_set("android.test_int", FormatValue(value).c_str()) == 0;
}
std::optional<std::string> test_string() {
- return GetProp<std::string>("android.test.string");
+ return GetProp<std::optional<std::string>>("android.test.string");
}
-bool test_string(const std::string& value) {
+bool test_string(const std::optional<std::string>& value) {
return __system_property_set("android.test.string", value.c_str()) == 0;
}
std::optional<test_enum_values> test_enum() {
- return GetProp<test_enum_values>("android.test.enum");
+ return GetProp<std::optional<test_enum_values>>("android.test.enum");
}
-bool test_enum(const test_enum_values& value) {
+bool test_enum(const std::optional<test_enum_values>& value) {
return __system_property_set("android.test.enum", FormatValue(value).c_str()) == 0;
}
std::optional<bool> test_BOOLeaN() {
- return GetProp<bool>("ro.android.test.b");
+ return GetProp<std::optional<bool>>("ro.android.test.b");
}
-bool test_BOOLeaN(const bool& value) {
+bool test_BOOLeaN(const std::optional<bool>& value) {
return __system_property_set("ro.android.test.b", FormatValue(value).c_str()) == 0;
}
std::optional<std::int64_t> android_os_test_long() {
- return GetProp<std::int64_t>("android.os_test-long");
+ return GetProp<std::optional<std::int64_t>>("android.os_test-long");
}
-bool android_os_test_long(const std::int64_t& value) {
+bool android_os_test_long(const std::optional<std::int64_t>& value) {
return __system_property_set("android.os_test-long", FormatValue(value).c_str()) == 0;
}
-std::optional<std::vector<double>> test_double_list() {
- return GetProp<std::vector<double>>("test_double_list");
+std::vector<std::optional<double>> test_double_list() {
+ return GetProp<std::vector<std::optional<double>>>("test_double_list");
}
-bool test_double_list(const std::vector<double>& value) {
+bool test_double_list(const std::vector<std::optional<double>>& value) {
return __system_property_set("test_double_list", FormatValue(value).c_str()) == 0;
}
-std::optional<std::vector<std::int32_t>> test_list_int() {
- return GetProp<std::vector<std::int32_t>>("test_list_int");
+std::vector<std::optional<std::int32_t>> test_list_int() {
+ return GetProp<std::vector<std::optional<std::int32_t>>>("test_list_int");
}
-bool test_list_int(const std::vector<std::int32_t>& value) {
+bool test_list_int(const std::vector<std::optional<std::int32_t>>& value) {
return __system_property_set("test_list_int", FormatValue(value).c_str()) == 0;
}
-std::optional<std::vector<std::string>> test_strlist() {
- return GetProp<std::vector<std::string>>("test.strlist");
+std::vector<std::optional<std::string>> test_strlist() {
+ return GetProp<std::vector<std::optional<std::string>>>("test.strlist");
}
-bool test_strlist(const std::vector<std::string>& value) {
+bool test_strlist(const std::vector<std::optional<std::string>>& value) {
return __system_property_set("test.strlist", FormatValue(value).c_str()) == 0;
}
-std::optional<std::vector<el_values>> el() {
- return GetProp<std::vector<el_values>>("el");
+std::vector<std::optional<el_values>> el() {
+ return GetProp<std::vector<std::optional<el_values>>>("el");
}
-bool el(const std::vector<el_values>& value) {
+bool el(const std::vector<std::optional<el_values>>& value) {
return __system_property_set("el", FormatValue(value).c_str()) == 0;
}
-} // namespace android::PlatformProperties
+} // namespace android::sysprop::PlatformProperties
)";
} // namespace
diff --git a/tests/JavaGenTest.cpp b/tests/JavaGenTest.cpp
index ccc2049..dbdc6ec 100644
--- a/tests/JavaGenTest.cpp
+++ b/tests/JavaGenTest.cpp
@@ -168,7 +168,7 @@
}
private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) {
- if (str == null || str.equals("")) return null;
+ if (str == null || str.equals("")) return new ArrayList<>();
List<T> ret = new ArrayList<>();
@@ -180,7 +180,7 @@
}
private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) {
- if (str == null || str.equals("")) return null;
+ if (str == null || str.equals("")) return new ArrayList<>();
List<T> ret = new ArrayList<>();