std::map support for append/read
Change-Id: I3b5510b8ba400cf4d3f936f01708cb17aa009e62
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/sdbusplus/message/append.hpp b/sdbusplus/message/append.hpp
index 2fda860..da8dc1a 100644
--- a/sdbusplus/message/append.hpp
+++ b/sdbusplus/message/append.hpp
@@ -50,6 +50,12 @@
// std::vector needs a loop.
template<typename T>
struct can_append_multiple<std::vector<T>> : std::false_type {};
+ // std::pair needs to be broken down into components.
+template<typename T1, typename T2>
+struct can_append_multiple<std::pair<T1,T2>> : std::false_type {};
+ // std::map needs a loop.
+template<typename T1, typename T2>
+struct can_append_multiple<std::map<T1,T2>> : std::false_type {};
/** @struct append_single
* @brief Utility to append a single C++ element into a sd_bus_message.
@@ -121,6 +127,38 @@
}
};
+/** @brief Specialization of append_single for std::pairs. */
+template <typename T1, typename T2> struct append_single<std::pair<T1, T2>>
+{
+ template <typename S>
+ static void op(sd_bus_message* m, S&& s)
+ {
+ constexpr auto dbusType = utility::tuple_to_array(
+ std::tuple_cat(types::type_id_nonull<T1>(),
+ types::type_id<T2>()));
+
+ sd_bus_message_open_container(
+ m, SD_BUS_TYPE_DICT_ENTRY, dbusType.data());
+ sdbusplus::message::append(m, s.first, s.second);
+ sd_bus_message_close_container(m);
+ }
+};
+
+/** @brief Specialization of append_single for std::maps. */
+template <typename T1, typename T2> struct append_single<std::map<T1, T2>>
+{
+ template<typename S>
+ static void op(sd_bus_message* m, S&& s)
+ {
+ constexpr auto dbusType = utility::tuple_to_array(
+ types::type_id<typename std::map<T1, T2>::value_type>());
+
+ sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, dbusType.data());
+ for(auto& i : s) { sdbusplus::message::append(m, i); }
+ sd_bus_message_close_container(m);
+ }
+};
+
/** @brief Append a tuple of content into the sd_bus_message.
*
* @tparam Tuple - The tuple type to append.
diff --git a/sdbusplus/message/read.hpp b/sdbusplus/message/read.hpp
index 9b28421..6291409 100644
--- a/sdbusplus/message/read.hpp
+++ b/sdbusplus/message/read.hpp
@@ -50,6 +50,12 @@
// std::vector needs a loop.
template<typename T>
struct can_read_multiple<std::vector<T>> : std::false_type {};
+ // std::pair needs to be broken down into components.
+template<typename T1, typename T2>
+struct can_read_multiple<std::pair<T1,T2>> : std::false_type {};
+ // std::map needs a loop.
+template<typename T1, typename T2>
+struct can_read_multiple<std::map<T1,T2>> : std::false_type {};
/** @struct read_single
* @brief Utility to read a single C++ element from a sd_bus_message.
@@ -131,6 +137,48 @@
}
};
+/** @brief Specialization of read_single for std::pairs. */
+template <typename T1, typename T2> struct read_single<std::pair<T1, T2>>
+{
+ template <typename S>
+ static void op(sd_bus_message* m, S&& s)
+ {
+ constexpr auto dbusType = utility::tuple_to_array(
+ std::tuple_cat(types::type_id_nonull<T1>(),
+ types::type_id<T2>()));
+
+ sd_bus_message_enter_container(
+ m, SD_BUS_TYPE_DICT_ENTRY, dbusType.data());
+ sdbusplus::message::read(m, s.first, s.second);
+ sd_bus_message_exit_container(m);
+ }
+};
+
+/** @brief Specialization of read_single for std::maps. */
+template <typename T1, typename T2> struct read_single<std::map<T1, T2>>
+{
+ template<typename S>
+ static void op(sd_bus_message* m, S&& s)
+ {
+ s.clear();
+
+ constexpr auto dbusType = utility::tuple_to_array(
+ types::type_id<typename std::map<T1, T2>::value_type>());
+
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, dbusType.data());
+
+ while(!sd_bus_message_at_end(m, false))
+ {
+ std::pair<std::remove_const_t<T1>, std::remove_const_t<T2>> p{};
+ sdbusplus::message::read(m, p);
+ s.insert(std::move(p));
+ }
+
+ sd_bus_message_exit_container(m);
+ }
+};
+
+
/** @brief Read a tuple of content from the sd_bus_message.
*
* @tparam Tuple - The tuple type to read.
diff --git a/sdbusplus/message/types.hpp b/sdbusplus/message/types.hpp
index 403b4f1..3e39aca 100644
--- a/sdbusplus/message/types.hpp
+++ b/sdbusplus/message/types.hpp
@@ -3,6 +3,7 @@
#include <tuple>
#include <string>
#include <vector>
+#include <map>
#include <systemd/sd-bus.h>
#include <sdbusplus/utility/type_traits.hpp>
@@ -31,6 +32,12 @@
* the cost of hard-coded type string constants.
*/
template <typename ...Args> constexpr auto type_id();
+/** @fn type_id_nonull()
+ * @brief A non-null-terminated version of type_id.
+ *
+ * This is useful when type-ids may need to be concatenated.
+ */
+template <typename ...Args> constexpr auto type_id_nonull();
namespace details
{
@@ -138,6 +145,22 @@
type_id<type_id_downcast_t<T>>::value);
};
+template <typename T1, typename T2> struct type_id<std::pair<T1, T2>>
+{
+ static constexpr auto value = std::tuple_cat(
+ tuple_type_id<SD_BUS_TYPE_DICT_ENTRY_BEGIN>::value,
+ type_id<type_id_downcast_t<T1>>::value,
+ type_id<type_id_downcast_t<T2>>::value,
+ tuple_type_id<SD_BUS_TYPE_DICT_ENTRY_END>::value);
+};
+
+template <typename T1, typename T2> struct type_id<std::map<T1, T2>>
+{
+ static constexpr auto value = std::tuple_cat(
+ tuple_type_id<SD_BUS_TYPE_ARRAY>::value,
+ type_id<typename std::map<T1,T2>::value_type>::value);
+};
+
template <typename T> constexpr auto& type_id_single()
{
static_assert(!std::is_base_of<undefined_type_id, type_id<T>>::value,
@@ -160,6 +183,11 @@
std::make_tuple('\0') /* null terminator for C-string */ );
}
+template <typename ...Args> constexpr auto type_id_nonull()
+{
+ return details::type_id_multiple<details::type_id_downcast_t<Args>...>();
+}
+
} // namespace types
} // namespace message
diff --git a/test/message/append.cpp b/test/message/append.cpp
index 87d8fc5..4734815 100644
--- a/test/message/append.cpp
+++ b/test/message/append.cpp
@@ -234,6 +234,65 @@
b.call_noreply(m);
}
+ // Test map.
+ {
+ auto m = newMethodCall__test(b);
+ std::map<std::string, int> s = { { "asdf", 3 }, { "jkl;", 4 } };
+ m.append(1, s, 2);
+ verifyTypeString = "ia{si}i";
+
+ struct verify
+ {
+ static void op(sd_bus_message* m)
+ {
+ int32_t a = 0;
+ sd_bus_message_read(m, "i", &a);
+ assert(a == 1);
+
+ auto rc = sd_bus_message_enter_container(m,
+ SD_BUS_TYPE_ARRAY,
+ "{si}");
+ assert(0 <= rc);
+
+ rc = sd_bus_message_enter_container(m,
+ SD_BUS_TYPE_DICT_ENTRY,
+ "si");
+ assert(0 <= rc);
+
+ const char* s = nullptr;
+ sd_bus_message_read_basic(m, 's', &s);
+ assert(0 == strcmp("asdf", s));
+ sd_bus_message_read_basic(m, 'i', &a);
+ assert(a == 3);
+
+ assert(1 == sd_bus_message_at_end(m, false));
+ sd_bus_message_exit_container(m);
+
+ rc = sd_bus_message_enter_container(m,
+ SD_BUS_TYPE_DICT_ENTRY,
+ "si");
+ assert(0 <= rc);
+
+ sd_bus_message_read_basic(m, 's', &s);
+ assert(0 == strcmp("jkl;", s));
+ sd_bus_message_read_basic(m, 'i', &a);
+ assert(a == 4);
+
+ assert(1 == sd_bus_message_at_end(m, false));
+ sd_bus_message_exit_container(m);
+
+ assert(1 == sd_bus_message_at_end(m, false));
+ sd_bus_message_exit_container(m);
+
+ sd_bus_message_read(m, "i", &a);
+ assert(a == 2);
+ }
+ };
+ verifyCallback = &verify::op;
+
+ b.call_noreply(m);
+ }
+
// Shutdown server.
{
auto m = b.new_method_call(SERVICE, "/", INTERFACE, QUIT_METHOD);
diff --git a/test/message/read.cpp b/test/message/read.cpp
index 1021175..f1dfc77 100644
--- a/test/message/read.cpp
+++ b/test/message/read.cpp
@@ -220,6 +220,35 @@
b.call_noreply(m);
}
+ // Test map.
+ {
+ auto m = newMethodCall__test(b);
+ std::map<std::string, int> s = { { "asdf", 3 }, { "jkl;", 4 } };
+ m.append(1, s, 2);
+ verifyTypeString = "ia{si}i";
+
+ struct verify
+ {
+ static void op(sdbusplus::message::message& m)
+ {
+ int32_t a = 0, b = 0;
+ std::map<std::string, int> s{};
+
+ m.read(a, s, b);
+ assert(a == 1);
+ assert(s.size() == 2);
+ assert(s["asdf"] == 3);
+ assert(s["jkl;"] == 4);
+ assert(b == 2);
+ }
+ };
+ verifyCallback = &verify::op;
+
+ b.call_noreply(m);
+ }
+
+
+
// Shutdown server.
{
auto m = b.new_method_call(SERVICE, "/", INTERFACE, QUIT_METHOD);