sdbus++: add support for 'set'

Add 'set' as a supported type to `sdbus++` and set as `std::set`.
The use of ordered-set is to match 'dict' as `std::map` and because
'struct' is `std::tuple`, which has `operator<=>` but not `std::hash`.
This ensures better compatiblilty with possible property type choices
by users.

Also, add a few test cases to ensure `std::set` and `std::unordered_set`
are well-covered.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I59605db1b22d54f783d807eda1e1ec1f9eb6792f
diff --git a/docs/interface.md b/docs/interface.md
index 08db52a..01e7f8a 100644
--- a/docs/interface.md
+++ b/docs/interface.md
@@ -89,12 +89,14 @@
 
 * `array[type]`
     - C++ type is `std::vector`
+* `dict[keytype, valuetype]`
+    - C++ type is `std::map`
+* `set[type]`
+    - C++ type is `std::set`
 * `struct[type0, type1, ...]`
     - C++ type is `std::tuple`
 * `variant[type0, type1, ...]`
     - C++ type is `std::variant`
-* `dict[keytype, valuetype]`
-    - C++ type is `std::map`
 
 It may seem odd that variants are required to list the types they may contain,
 but this is due to C++ being a strongly-typed language.  In order to generate
diff --git a/include/sdbusplus/message/types.hpp b/include/sdbusplus/message/types.hpp
index 6b4ddc8..f45e1cd 100644
--- a/include/sdbusplus/message/types.hpp
+++ b/include/sdbusplus/message/types.hpp
@@ -7,6 +7,7 @@
 #include <sdbusplus/utility/type_traits.hpp>
 
 #include <map>
+#include <set>
 #include <string>
 #include <tuple>
 #include <variant>
diff --git a/test/message/append.cpp b/test/message/append.cpp
index c6eb152..288199b 100644
--- a/test/message/append.cpp
+++ b/test/message/append.cpp
@@ -9,6 +9,7 @@
 #include <string>
 #include <tuple>
 #include <unordered_map>
+#include <unordered_set>
 #include <variant>
 #include <vector>
 
@@ -257,6 +258,22 @@
     new_message().append(s);
 }
 
+TEST_F(AppendTest, UnorderedSet)
+{
+    const std::unordered_set<std::string> s{"one", "two", "eight"};
+
+    {
+        testing::InSequence seq;
+        expect_open_container(SD_BUS_TYPE_ARRAY, "s");
+        for (const auto& i : s)
+        {
+            expect_basic_string(SD_BUS_TYPE_STRING, i.c_str());
+        }
+        expect_close_container();
+    }
+    new_message().append(s);
+}
+
 TEST_F(AppendTest, Map)
 {
     const std::map<int, std::string> m{
diff --git a/test/message/read.cpp b/test/message/read.cpp
index 1d99940..d1d78e7 100644
--- a/test/message/read.cpp
+++ b/test/message/read.cpp
@@ -10,6 +10,7 @@
 #include <string>
 #include <tuple>
 #include <unordered_map>
+#include <unordered_set>
 #include <variant>
 #include <vector>
 
@@ -296,6 +297,27 @@
     EXPECT_EQ(ss, ret_ss);
 }
 
+TEST_F(ReadTest, UnorderedSet)
+{
+    const std::unordered_set<std::string> ss{"one", "two", "eight"};
+
+    {
+        testing::InSequence seq;
+        expect_enter_container(SD_BUS_TYPE_ARRAY, "s");
+        for (const auto& s : ss)
+        {
+            expect_at_end(false, 0);
+            expect_basic<const char*>(SD_BUS_TYPE_STRING, s.c_str());
+        }
+        expect_at_end(false, 1);
+        expect_exit_container();
+    }
+
+    std::unordered_set<std::string> ret_ss;
+    new_message().read(ret_ss);
+    EXPECT_EQ(ss, ret_ss);
+}
+
 TEST_F(ReadTest, Map)
 {
     const std::map<int, std::string> mis{
diff --git a/test/message/types.cpp b/test/message/types.cpp
index 4020559..89cab14 100644
--- a/test/message/types.cpp
+++ b/test/message/types.cpp
@@ -45,3 +45,17 @@
 {
     ASSERT_EQ(dbus_string(sdbusplus::message::signature("sss")), "g");
 }
+
+TEST(MessageTypes, VectorOfString)
+{
+    std::vector<std::string> s = {"a", "b"};
+
+    ASSERT_EQ(dbus_string(s), "as");
+}
+
+TEST(MessageTypes, SetOfString)
+{
+    std::set<std::string> s = {"a", "b"};
+
+    ASSERT_EQ(dbus_string(s), "as");
+}
diff --git a/tools/sdbusplus/property.py b/tools/sdbusplus/property.py
index afea22b..75fcff9 100644
--- a/tools/sdbusplus/property.py
+++ b/tools/sdbusplus/property.py
@@ -131,6 +131,7 @@
             'signature': {'cppName': 'sdbusplus::message::signature',
                           'params': 0},
             'array': {'cppName': 'std::vector', 'params': 1},
+            'set': {'cppName': 'std::set', 'params': 1},
             'struct': {'cppName': 'std::tuple', 'params': -1},
             'variant': {'cppName': 'std::variant', 'params': -1},
             'dict': {'cppName': 'std::map', 'params': 2},