message: add unpack method
Add an unpack method that allows reading from the message as
r-values. This simplifies the pattern:
```
foo f{};
bar b{};
msg.read(f,b);
// Can now be written as...
auto [f, b] = msg.unpack<foo, bar>();
```
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: Ic2ae7f2c52d41702b8c7c3af6a2efb21558a7579
diff --git a/README.md b/README.md
index 64e5984..3bf3704 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,9 @@
std::vector<std::tuple<uint32_t, std::string, message::object_path>> users;
reply.read(users);
+ // or
+auto users = reply.unpack<
+ std::vector<std::tuple<uint32_t, std::string, message::object_path>>>();
```
In a few, relatively succinct, C++ lines this snippet will create a D-Bus
diff --git a/example/list-users.cpp b/example/list-users.cpp
index 23913b0..f7ab475 100644
--- a/example/list-users.cpp
+++ b/example/list-users.cpp
@@ -18,8 +18,9 @@
"org.freedesktop.login1.Manager", "ListUsers");
auto reply = b.call(m);
- std::vector<std::tuple<uint32_t, std::string, message::object_path>> users;
- reply.read(users);
+ using return_type =
+ std::vector<std::tuple<uint32_t, std::string, message::object_path>>;
+ auto users = reply.unpack<return_type>();
for (auto& user : users)
{
diff --git a/include/sdbusplus/message.hpp b/include/sdbusplus/message.hpp
index 749522e..4824997 100644
--- a/include/sdbusplus/message.hpp
+++ b/include/sdbusplus/message.hpp
@@ -12,6 +12,7 @@
#include <exception>
#include <memory>
#include <optional>
+#include <tuple>
#include <type_traits>
#include <utility>
@@ -156,6 +157,32 @@
std::forward<Args>(args)...);
}
+ /** @brief Perform sd_bus_message_read with results returned.
+ *
+ * @tparam ...Args - Type of items to read from the message.
+ * @return One of { void, Args, std::tuple<Args...> }.
+ */
+ template <typename... Args>
+ auto unpack()
+ {
+ if constexpr (sizeof...(Args) == 0)
+ {
+ return;
+ }
+ else if constexpr (sizeof...(Args) == 1)
+ {
+ std::tuple_element_t<0, std::tuple<Args...>> r{};
+ read(r);
+ return r;
+ }
+ else
+ {
+ std::tuple<Args...> r{};
+ std::apply([this](auto&&... v) { this->read(v...); }, r);
+ return r;
+ }
+ }
+
/** @brief Get the dbus bus from the message. */
// Forward declare.
auto get_bus() const;
diff --git a/test/message/read.cpp b/test/message/read.cpp
index 6131c24..44253c6 100644
--- a/test/message/read.cpp
+++ b/test/message/read.cpp
@@ -649,4 +649,44 @@
EXPECT_EQ(msv, ret_msv);
}
+// Unpack tests.
+// Since unpack uses read, we're mostly just testing the compilation.
+// Duplicate a few tests from Read using 'unpack'.
+
+TEST_F(ReadTest, UnpackSingleVector)
+{
+ const std::vector<int> vi{1, 2, 3, 4};
+
+ {
+ testing::InSequence seq;
+ expect_enter_container(SD_BUS_TYPE_ARRAY, "i");
+ for (const auto& i : vi)
+ {
+ expect_at_end(false, 0);
+ expect_basic<int>(SD_BUS_TYPE_INT32, i);
+ }
+ expect_at_end(false, 1);
+ expect_exit_container();
+ }
+
+ auto ret_vi = new_message().unpack<std::vector<int>>();
+ EXPECT_EQ(vi, ret_vi);
+}
+
+TEST_F(ReadTest, UnpackMultiple)
+{
+ const std::tuple<int, std::string, bool> tisb{3, "hi", false};
+
+ {
+ testing::InSequence seq;
+ expect_basic<int>(SD_BUS_TYPE_INT32, std::get<0>(tisb));
+ expect_basic<const char*>(SD_BUS_TYPE_STRING,
+ std::get<1>(tisb).c_str());
+ expect_basic<int>(SD_BUS_TYPE_BOOLEAN, std::get<2>(tisb));
+ }
+
+ auto ret_tisb = new_message().unpack<int, std::string, bool>();
+ EXPECT_EQ(tisb, ret_tisb);
+}
+
} // namespace