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<>();