Add message read functionality

C++ bindings for sd_bus_message_read* functions.  Similar compile-time
type deduction as the message::append interfaces.

Change-Id: I88639bedb9703266f7282642ce261c28b736adfc
diff --git a/test/message/read.cpp b/test/message/read.cpp
new file mode 100644
index 0000000..48c445e
--- /dev/null
+++ b/test/message/read.cpp
@@ -0,0 +1,219 @@
+#include <iostream>
+#include <cassert>
+#include <sdbusplus/message.hpp>
+#include <sdbusplus/bus.hpp>
+
+// Global to share the dbus type string between client and server.
+static std::string verifyTypeString;
+
+using verifyCallback_t = void(*)(sdbusplus::message::message&);
+verifyCallback_t verifyCallback = nullptr;
+
+static constexpr auto SERVICE = "sdbusplus.test";
+static constexpr auto INTERFACE = SERVICE;
+static constexpr auto TEST_METHOD = "test";
+static constexpr auto QUIT_METHOD = "quit";
+
+// Open up the sdbus and claim SERVICE name.
+auto serverInit()
+{
+    auto b = sdbusplus::bus::new_default();
+    b.request_name(SERVICE);
+
+    return std::move(b);
+}
+
+// Thread to run the dbus server.
+void* server(void* b)
+{
+    auto bus = sdbusplus::bus::bus(reinterpret_cast<sdbusplus::bus::busp_t>(b));
+
+    while(1)
+    {
+        // Wait for messages.
+        auto m = bus.process();
+
+        if(!m)
+        {
+            bus.wait();
+            continue;
+        }
+
+        if (m.is_method_call(INTERFACE, TEST_METHOD))
+        {
+            // Verify the message type matches what the test expects.
+            assert(verifyTypeString == m.get_signature());
+
+            if (verifyCallback)
+            {
+
+                verifyCallback(m);
+                verifyCallback = nullptr;
+            }
+            else
+            {
+                std::cout << "Warning: No verification for "
+                          << verifyTypeString << std::endl;
+            }
+            // Reply to client.
+            sd_bus_reply_method_return(m.release(), nullptr);
+        }
+        else if (m.is_method_call(INTERFACE, QUIT_METHOD))
+        {
+            // Reply and exit.
+            sd_bus_reply_method_return(m.release(), nullptr);
+            break;
+        }
+    }
+}
+
+auto newMethodCall__test(sdbusplus::bus::bus& b)
+{
+    // Allocate a method-call message for INTERFACE,TEST_METHOD.
+    return b.new_method_call(SERVICE, "/", INTERFACE, TEST_METHOD);
+}
+
+void runTests()
+{
+    using namespace std::literals;
+
+    auto b = sdbusplus::bus::new_default();
+
+    // Test r-value int.
+    {
+        auto m = newMethodCall__test(b);
+        m.append(1);
+        verifyTypeString = "i";
+
+        struct verify
+        {
+            static void op(sdbusplus::message::message& m)
+            {
+                int32_t i = 0;
+                m.read(i);
+                assert(i == 1);
+            }
+        };
+        verifyCallback = &verify::op;
+
+        b.call_noreply(m);
+    }
+    // Test l-value int.
+    {
+        auto m = newMethodCall__test(b);
+        int a = 1;
+        m.append(a, a);
+        verifyTypeString = "ii";
+
+        struct verify
+        {
+            static void op(sdbusplus::message::message& m)
+            {
+                int32_t a = 0, b = 0;
+                m.read(a, b);
+                assert(a == 1);
+                assert(b == 1);
+            }
+        };
+        verifyCallback = &verify::op;
+
+        b.call_noreply(m);
+    }
+
+    // Test multiple ints.
+    {
+        auto m = newMethodCall__test(b);
+        m.append(1, 2, 3, 4, 5);
+        verifyTypeString = "iiiii";
+
+        struct verify
+        {
+            static void op(sdbusplus::message::message& m)
+            {
+                int32_t a = 0, b = 0, c = 0, d = 0, e = 0;
+                m.read(a,b,c,d,e);
+                assert(a == 1);
+                assert(b == 2);
+                assert(c == 3);
+                assert(d == 4);
+                assert(e == 5);
+            }
+        };
+        verifyCallback = &verify::op;
+
+        b.call_noreply(m);
+    }
+
+    // Test r-value string.
+    {
+        auto m = newMethodCall__test(b);
+        m.append("asdf"s);
+        verifyTypeString = "s";
+
+        struct verify
+        {
+            static void op(sdbusplus::message::message& m)
+            {
+                const char* s = nullptr;
+                m.read(s);
+                assert(0 == strcmp("asdf", s));
+            }
+        };
+        verifyCallback = &verify::op;
+
+        b.call_noreply(m);
+    }
+
+    // Test multiple strings, various forms.
+    {
+        auto m = newMethodCall__test(b);
+        auto str = "jkl;"s;
+        auto str2 = "JKL:"s;
+        m.append(1, "asdf", "ASDF"s, str,
+                 std::move(str2), 5);
+        verifyTypeString = "issssi";
+
+        struct verify
+        {
+            static void op(sdbusplus::message::message& m)
+            {
+                int32_t a = 0, b = 0;
+                std::string s0, s1, s2, s3;
+                m.read(a, s0, s1, s2, s3, b);
+                assert(a == 1);
+                assert(b == 5);
+                assert(s0 == "asdf"s);
+                assert(s1 == "ASDF"s);
+                assert(s2 == "jkl;"s);
+                assert(s3 == "JKL:");
+            }
+        };
+        verifyCallback = &verify::op;
+
+        b.call_noreply(m);
+    }
+
+    // Shutdown server.
+    {
+        auto m = b.new_method_call(SERVICE, "/", INTERFACE, QUIT_METHOD);
+        b.call_noreply(m);
+    }
+}
+
+int main()
+{
+
+    // Initialize and start server thread.
+    pthread_t t;
+    {
+        auto b = serverInit();
+        pthread_create(&t, NULL, server, b.release());
+    }
+
+    runTests();
+
+    // Wait for server thread to exit.
+    pthread_join(t, NULL);
+
+    return 0;
+}