variant support for append/read
Change-Id: I682275405e3c4f7c798317a60ea070b3b1a0846c
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/sdbusplus/message/append.hpp b/sdbusplus/message/append.hpp
index 2db790b..df0b08f 100644
--- a/sdbusplus/message/append.hpp
+++ b/sdbusplus/message/append.hpp
@@ -59,6 +59,9 @@
// std::tuple needs to be broken down into components.
template<typename ...Args>
struct can_append_multiple<std::tuple<Args...>> : std::false_type {};
+ // variant needs to be broken down into components.
+template<typename ...Args>
+struct can_append_multiple<variant<Args...>> : std::false_type {};
/** @struct append_single
* @brief Utility to append a single C++ element into a sd_bus_message.
@@ -188,6 +191,30 @@
}
};
+/** @brief Specialization of append_single for std::variant. */
+template <typename ...Args> struct append_single<variant<Args...>>
+{
+ template<typename S,
+ typename = std::enable_if_t<0 < sizeof...(Args)>>
+ static void op(sd_bus_message* m, S&& s)
+ {
+ auto apply =
+ [m](auto&& arg)
+ {
+ constexpr auto dbusType = utility::tuple_to_array(
+ types::type_id<decltype(arg)>());
+
+ sd_bus_message_open_container(m,
+ SD_BUS_TYPE_VARIANT,
+ dbusType.data());
+ sdbusplus::message::append(m, arg);
+ sd_bus_message_close_container(m);
+ };
+
+ std::remove_reference_t<S>::visit(s, apply);
+ }
+};
+
/** @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 47e2392..275e3a4 100644
--- a/sdbusplus/message/read.hpp
+++ b/sdbusplus/message/read.hpp
@@ -59,6 +59,9 @@
// std::tuple needs to be broken down into components.
template<typename ...Args>
struct can_read_multiple<std::tuple<Args...>> : std::false_type {};
+ // variant needs to be broken down into components.
+template<typename ...Args>
+struct can_read_multiple<variant<Args...>> : std::false_type {};
/** @struct read_single
* @brief Utility to read a single C++ element from a sd_bus_message.
@@ -207,6 +210,46 @@
}
};
+/** @brief Specialization of read_single for std::variant. */
+template <typename ...Args> struct read_single<variant<Args...>>
+{
+ template<typename S, typename S1, typename ...Args1>
+ static void read(sd_bus_message* m, S&& s)
+ {
+ constexpr auto dbusType = utility::tuple_to_array(types::type_id<S1>());
+
+ auto rc = sd_bus_message_verify_type(m,
+ SD_BUS_TYPE_VARIANT,
+ dbusType.data());
+ if (0 >= rc)
+ {
+ read<S, Args1...>(m, s);
+ return;
+ }
+
+ std::remove_reference_t<S1> s1;
+
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, dbusType.data());
+ sdbusplus::message::read(m, s1);
+ sd_bus_message_exit_container(m);
+
+ s = std::move(s1);
+ }
+
+ template<typename S>
+ static void read(sd_bus_message* m, S&& s)
+ {
+ s = std::remove_reference_t<S>{};
+ }
+
+ template<typename S,
+ typename = std::enable_if_t<0 < sizeof...(Args)>>
+ static void op(sd_bus_message* m, S&& s)
+ {
+ read<S, Args...>(m, s);
+ }
+};
+
/** @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 1876fa2..fd4a294 100644
--- a/sdbusplus/message/types.hpp
+++ b/sdbusplus/message/types.hpp
@@ -4,6 +4,7 @@
#include <string>
#include <vector>
#include <map>
+#include <mapbox/variant.hpp>
#include <systemd/sd-bus.h>
#include <sdbusplus/utility/type_traits.hpp>
@@ -14,6 +15,9 @@
namespace message
{
+template <typename ...Args>
+using variant = mapbox::util::variant<Args...>;
+
namespace types
{
@@ -169,6 +173,9 @@
tuple_type_id<SD_BUS_TYPE_STRUCT_END>::value);
};
+template <typename ...Args>
+struct type_id<variant<Args...>> : tuple_type_id<SD_BUS_TYPE_VARIANT> {};
+
template <typename T> constexpr auto& type_id_single()
{
static_assert(!std::is_base_of<undefined_type_id, type_id<T>>::value,
diff --git a/test/message/append.cpp b/test/message/append.cpp
index 03ab781..4f85b74 100644
--- a/test/message/append.cpp
+++ b/test/message/append.cpp
@@ -332,6 +332,89 @@
b.call_noreply(m);
}
+ // Test variant.
+ {
+ auto m = newMethodCall__test(b);
+ sdbusplus::message::variant<int, double> a1{3.1}, a2{4};
+ m.append(1, a1, a2, 2);
+ verifyTypeString = "ivvi";
+
+ struct verify
+ {
+ static void op(sd_bus_message* m)
+ {
+ int32_t a = 0;
+ double b = 0;
+
+ sd_bus_message_read(m, "i", &a);
+ assert(a == 1);
+
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "d");
+ sd_bus_message_read(m, "d", &b);
+ assert(b == 3.1);
+ sd_bus_message_exit_container(m);
+
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "i");
+ sd_bus_message_read(m, "i", &a);
+ assert(a == 4);
+ sd_bus_message_exit_container(m);
+
+ sd_bus_message_read(m, "i", &a);
+ assert(a == 2);
+ }
+ };
+ verifyCallback = &verify::op;
+
+ b.call_noreply(m);
+ }
+
+ // Test map-variant.
+ {
+ auto m = newMethodCall__test(b);
+ std::map<std::string, sdbusplus::message::variant<int, double>> a1 =
+ { { "asdf", 3 }, { "jkl;", 4.1 } };
+ m.append(1, a1, 2);
+ verifyTypeString = "ia{sv}i";
+
+ struct verify
+ {
+ static void op(sd_bus_message* m)
+ {
+ int32_t a = 0;
+ double b = 0;
+ const char* c = nullptr;
+
+ sd_bus_message_read(m, "i", &a);
+ assert(a == 1);
+
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
+ sd_bus_message_read(m, "s", &c);
+ assert(0 == strcmp("asdf", c));
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "i");
+ sd_bus_message_read(m, "i", &a);
+ assert(a == 3);
+ sd_bus_message_exit_container(m);
+ sd_bus_message_exit_container(m);
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
+ sd_bus_message_read(m, "s", &c);
+ assert(0 == strcmp("jkl;", c));
+ sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "d");
+ sd_bus_message_read(m, "d", &b);
+ assert(b == 4.1);
+ sd_bus_message_exit_container(m);
+ sd_bus_message_exit_container(m);
+ 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 25ccdb5..496e6ca 100644
--- a/test/message/read.cpp
+++ b/test/message/read.cpp
@@ -272,6 +272,61 @@
b.call_noreply(m);
}
+ // Test variant.
+ {
+ auto m = newMethodCall__test(b);
+ sdbusplus::message::variant<int, double> a1{3.1}, a2{4};
+ m.append(1, a1, a2, 2);
+ verifyTypeString = "ivvi";
+
+ struct verify
+ {
+ static void op(sdbusplus::message::message& m)
+ {
+ int32_t a, b;
+ sdbusplus::message::variant<int, double> a1{}, a2{};
+
+ m.read(a, a1, a2, b);
+ assert(a == 1);
+ assert(a1 == 3.1);
+ assert(a2 == 4);
+ assert(b == 2);
+ }
+ };
+ verifyCallback = &verify::op;
+
+ b.call_noreply(m);
+ }
+
+ // Test map-variant.
+ {
+ auto m = newMethodCall__test(b);
+ std::map<std::string, sdbusplus::message::variant<int, double>> a1 =
+ { { "asdf", 3 }, { "jkl;", 4.1 } };
+ m.append(1, a1, 2);
+ verifyTypeString = "ia{sv}i";
+
+ struct verify
+ {
+ static void op(sdbusplus::message::message& m)
+ {
+ int32_t a = 0, b = 0;
+ std::map<std::string,
+ sdbusplus::message::variant<int, double>> a1{};
+
+ m.read(a, a1, b);
+ assert(a == 1);
+ assert(a1["asdf"] == 3);
+ assert(a1["jkl;"] == 4.1);
+ assert(b == 2);
+ }
+ };
+ verifyCallback = &verify::op;
+
+ b.call_noreply(m);
+ }
+
+
// Shutdown server.
{
auto m = b.new_method_call(SERVICE, "/", INTERFACE, QUIT_METHOD);