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/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