async: add client-object proxy

Add a class which acts as a client-side proxy to a object.  The
proxy holds the object address (service, path, interface) and
creates Senders for standard dbus operations: method-call,
get-property, set-property, get-all-properties.  These Senders
can also be used in co-routine contexts (via co_await).

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I4b2e50a8b55d10975984ac4244bb075db8746fbf
diff --git a/example/coroutine-example.cpp b/example/coroutine-example.cpp
new file mode 100644
index 0000000..8cfa0f4
--- /dev/null
+++ b/example/coroutine-example.cpp
@@ -0,0 +1,81 @@
+#include <sdbusplus/async.hpp>
+
+#include <iostream>
+#include <string>
+#include <variant>
+#include <vector>
+
+auto runner(sdbusplus::async::context& ctx) -> sdbusplus::async::task<>
+{
+    // Create a proxy to the systemd manager object.
+    constexpr auto systemd = sdbusplus::async::proxy()
+                                 .service("org.freedesktop.systemd1")
+                                 .path("/org/freedesktop/systemd1")
+                                 .interface("org.freedesktop.systemd1.Manager");
+
+    // Call ListUnitFiles method.
+    using ret_type = std::vector<std::tuple<std::string, std::string>>;
+    for (auto& [file, status] :
+         co_await systemd.call<ret_type>(ctx, "ListUnitFiles"))
+    {
+        std::cout << file << " " << status << std::endl;
+    }
+
+    // Get the Architecture property.
+    std::cout << co_await systemd.get_property<std::string>(ctx, "Architecture")
+              << std::endl;
+
+    // Get all the properties.
+    using variant_type =
+        std::variant<bool, std::string, std::vector<std::string>, uint64_t,
+                     int32_t, uint32_t, double>;
+    for (auto& [property, value] :
+         co_await systemd.get_all_properties<variant_type>(ctx))
+    {
+        std::cout
+            << property << " is "
+            << std::visit(
+                   // Convert the variant member to a string for printing.
+                   [](auto v) {
+                       if constexpr (std::is_same_v<
+                                         std::remove_cvref_t<decltype(v)>,
+                                         std::vector<std::string>>)
+                       {
+                           return std::string{"Array"};
+                       }
+                       else if constexpr (std::is_same_v<
+                                              std::remove_cvref_t<decltype(v)>,
+                                              std::string>)
+                       {
+                           return v;
+                       }
+                       else
+                       {
+                           return std::to_string(v);
+                       }
+                   },
+                   value)
+            << std::endl;
+    }
+
+    // Try to set the Architecture property (which won't work).
+    try
+    {
+        co_await systemd.set_property(ctx, "Architecture", "arm64");
+    }
+    catch (const std::exception& e)
+    {
+        std::cout << "Caught exception because you cannot set Architecture: "
+                  << e.what() << std::endl;
+    }
+
+    co_return;
+}
+
+int main()
+{
+    sdbusplus::async::context ctx;
+    ctx.run(runner(ctx));
+
+    return 0;
+}
diff --git a/example/meson.build b/example/meson.build
index 17fd569..1850596 100644
--- a/example/meson.build
+++ b/example/meson.build
@@ -33,6 +33,12 @@
 )
 
 executable(
+    'coroutine-example',
+    'coroutine-example.cpp',
+    dependencies: [ sdbusplus_dep ],
+)
+
+executable(
     'register-property',
     'register-property.cpp',
     dependencies: asio_dep,