asio: Added utility functions getProperty and setProperty

Tested:
  - Modified one of the examples to use this utility instead
  - All other tests are still passing after this change

Change-Id: I3237e281915c7edf931e3326b88bd24714ee8ecb
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
diff --git a/example/get-all-properties.cpp b/example/get-all-properties.cpp
index 8e7ee54..7a2d037 100644
--- a/example/get-all-properties.cpp
+++ b/example/get-all-properties.cpp
@@ -1,7 +1,7 @@
 #include <boost/asio.hpp>
 #include <sdbusplus/asio/connection.hpp>
-#include <sdbusplus/asio/get_all_properties.hpp>
 #include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/asio/property.hpp>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/unpack_properties.hpp>
 
diff --git a/example/register-property.cpp b/example/register-property.cpp
index 651e974..530a004 100644
--- a/example/register-property.cpp
+++ b/example/register-property.cpp
@@ -1,6 +1,7 @@
 #include <boost/asio.hpp>
 #include <sdbusplus/asio/connection.hpp>
 #include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/asio/property.hpp>
 #include <sdbusplus/bus.hpp>
 
 #include <iostream>
@@ -38,39 +39,20 @@
         service_(service), path_(path), interface_(interface), name_(name)
     {}
 
-    template <class F>
-    void async_get(F&& callback)
+    template <class OnError, class OnSuccess>
+    void async_get(OnError&& onError, OnSuccess&& onSuccess)
     {
-        bus_.async_method_call(
-            [callback =
-                 std::move(callback)](const boost::system::error_code& error,
-                                      const std::variant<T>& valueVariant) {
-                if (error)
-                {
-                    callback(std::nullopt);
-                    return;
-                }
-
-                if (auto value = std::get_if<T>(&valueVariant))
-                {
-                    callback(*value);
-                    return;
-                }
-
-                callback(std::nullopt);
-            },
-            service_, path_, "org.freedesktop.DBus.Properties", "Get",
-            interface_, name_);
+        sdbusplus::asio::getProperty<T>(bus_, service_, path_, interface_,
+                                        name_, std::forward<OnError>(onError),
+                                        std::forward<OnSuccess>(onSuccess));
     }
 
-    template <class F>
-    void async_set(const T& value, F&& callback)
+    template <class OnError, class OnSuccess>
+    void async_set(T&& value, OnError&& onError, OnSuccess&& onSuccess)
     {
-        bus_.async_method_call(
-            [callback = std::move(callback)](
-                const boost::system::error_code& error) { callback(error); },
-            service_, path_, "org.freedesktop.DBus.Properties", "Set",
-            interface_, name_, value);
+        sdbusplus::asio::setProperty(
+            bus_, service_, path_, interface_, name_, std::forward<T>(value),
+            std::forward<OnError>(onError), std::forward<OnSuccess>(onSuccess));
     }
 
   private:
@@ -114,36 +96,73 @@
         objServer_.remove_interface(demo_);
     }
 
+    uint32_t fatalErrors() const
+    {
+        return fatalErrors_;
+    }
+
+    auto getFailed()
+    {
+        return [this](boost::system::error_code error) {
+            std::cerr << "Error: getProperty failed " << error << "\n";
+            ++fatalErrors_;
+        };
+    }
+
+    void asyncReadPropertyWithIncorrectType()
+    {
+        utils::Property<uint32_t> propertyWithWrongType{
+            bus_, xyz::demo::name, xyz::demo::path, xyz::demo::name,
+            name::greetings};
+
+        propertyWithWrongType.async_get(
+            [](boost::system::error_code error) {
+                std::cout
+                    << "As expected failed to getProperty with wrong type: "
+                    << error << "\n";
+            },
+            [this](uint32_t) {
+                std::cerr << "Error: it was expected to fail getProperty due "
+                             "to wrong type\n";
+                ++fatalErrors_;
+            });
+    }
+
     void asyncReadProperties()
     {
-        propertyGreetings.async_get([](std::optional<std::string> value) {
-            std::cout << "Greetings value is: "
-                      << value.value_or("std::nullopt") << "\n";
+        propertyGreetings.async_get(getFailed(), [](std::string value) {
+            std::cout << "Greetings value is: " << value << "\n";
         });
 
-        propertyGoodbyes.async_get([](std::optional<std::string> value) {
-            std::cout << "Goodbyes value is: " << value.value_or("std::nullopt")
-                      << "\n";
+        propertyGoodbyes.async_get(getFailed(), [](std::string value) {
+            std::cout << "Goodbyes value is: " << value << "\n";
         });
     }
 
     void asyncChangeProperty()
     {
         propertyGreetings.async_set(
-            "Hi, hey, hello", [](const boost::system::error_code& error) {
-                if (error)
-                {
-                    std::cout
-                        << "As expected, failed to set greetings property\n";
-                }
+            "Hi, hey, hello",
+            [](const boost::system::error_code& error) {
+                std::cout << "As expected, failed to set greetings property: "
+                          << error << "\n";
+            },
+            [this]() {
+                std::cout
+                    << "Error: it was expected to fail to change greetings\n";
+                ++fatalErrors_;
             });
 
         propertyGoodbyes.async_set(
-            "Bye bye", [this](const boost::system::error_code& error) {
-                if (!error)
-                {
-                    std::cout << "Changed goodbyes property as expected\n";
-                }
+            "Bye bye",
+            [this](const boost::system::error_code& error) {
+                std::cout << "Error: it supposed to be ok to change goodbyes "
+                             "property: "
+                          << error << "\n";
+                ++fatalErrors_;
+            },
+            [this]() {
+                std::cout << "Changed goodbyes property as expected\n";
                 boost::asio::post(ioc_, [this] { asyncReadProperties(); });
             });
     }
@@ -157,6 +176,8 @@
     std::string greetings_ = "Hello";
     std::string goodbyes_ = "Bye";
 
+    uint32_t fatalErrors_ = 0u;
+
     utils::Property<std::string> propertyGreetings{
         bus_, xyz::demo::name, xyz::demo::path, xyz::demo::name,
         name::greetings};
@@ -180,10 +201,14 @@
 
     Application app(ioc, *bus, *objServer);
 
+    boost::asio::post(ioc,
+                      [&app] { app.asyncReadPropertyWithIncorrectType(); });
     boost::asio::post(ioc, [&app] { app.asyncReadProperties(); });
     boost::asio::post(ioc, [&app] { app.asyncChangeProperty(); });
 
     ioc.run();
 
-    return 0;
+    std::cout << "Fatal errors count: " << app.fatalErrors() << "\n";
+
+    return app.fatalErrors();
 }
diff --git a/include/sdbusplus/asio/get_all_properties.hpp b/include/sdbusplus/asio/get_all_properties.hpp
deleted file mode 100644
index 6bc41fe..0000000
--- a/include/sdbusplus/asio/get_all_properties.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <sdbusplus/asio/connection.hpp>
-
-namespace sdbusplus::asio
-{
-
-template <typename OnError, typename OnSuccess>
-inline void getAllProperties(sdbusplus::asio::connection& bus,
-                             const std::string& service,
-                             const std::string& path,
-                             const std::string& interface, OnError&& onError,
-                             OnSuccess&& onSuccess)
-{
-    using FunctionTuple = boost::callable_traits::args_t<OnSuccess>;
-    using FunctionTupleType =
-        typename sdbusplus::utility::decay_tuple<FunctionTuple>::type;
-
-    bus.async_method_call(
-        [onError = std::move(onError), onSuccess = std::move(onSuccess)](
-            boost::system::error_code ec,
-            std::tuple_element_t<0, FunctionTupleType>& ret) {
-            if (ec)
-            {
-                onError(ec);
-                return;
-            }
-
-            onSuccess(ret);
-        },
-        service, path, "org.freedesktop.DBus.Properties", "GetAll", interface);
-}
-
-} // namespace sdbusplus::asio
diff --git a/include/sdbusplus/asio/property.hpp b/include/sdbusplus/asio/property.hpp
new file mode 100644
index 0000000..94a5820
--- /dev/null
+++ b/include/sdbusplus/asio/property.hpp
@@ -0,0 +1,86 @@
+#pragma once
+
+#include <sdbusplus/asio/connection.hpp>
+
+namespace sdbusplus::asio
+{
+
+template <typename OnError, typename OnSuccess>
+inline void getAllProperties(sdbusplus::asio::connection& bus,
+                             const std::string& service,
+                             const std::string& path,
+                             const std::string& interface, OnError&& onError,
+                             OnSuccess&& onSuccess)
+{
+    using FunctionTuple = boost::callable_traits::args_t<OnSuccess>;
+    using FunctionTupleType =
+        typename sdbusplus::utility::decay_tuple<FunctionTuple>::type;
+
+    bus.async_method_call(
+        [onError = std::move(onError), onSuccess = std::move(onSuccess)](
+            boost::system::error_code ec,
+            std::tuple_element_t<0, FunctionTupleType>& ret) {
+            if (ec)
+            {
+                onError(ec);
+                return;
+            }
+
+            onSuccess(ret);
+        },
+        service, path, "org.freedesktop.DBus.Properties", "GetAll", interface);
+}
+
+template <typename T, typename OnError, typename OnSuccess>
+inline void getProperty(sdbusplus::asio::connection& bus,
+                        const std::string& service, const std::string& path,
+                        const std::string& interface,
+                        const std::string& propertyName, OnError&& onError,
+                        OnSuccess&& onSuccess)
+{
+    bus.async_method_call(
+        [onError = std::move(onError), onSuccess = std::move(onSuccess)](
+            boost::system::error_code ec,
+            std::variant<std::monostate, T>& ret) {
+            if (ec)
+            {
+                onError(ec);
+                return;
+            }
+
+            if (T* value = std::get_if<T>(&ret))
+            {
+                onSuccess(*value);
+                return;
+            }
+
+            onError(boost::system::errc::make_error_code(
+                boost::system::errc::invalid_argument));
+        },
+        service, path, "org.freedesktop.DBus.Properties", "Get", interface,
+        propertyName);
+}
+
+template <typename T, typename OnError, typename OnSuccess>
+inline void setProperty(sdbusplus::asio::connection& bus,
+                        const std::string& service, const std::string& path,
+                        const std::string& interface,
+                        const std::string& propertyName, T&& propertyValue,
+                        OnError&& onError, OnSuccess&& onSuccess)
+{
+    bus.async_method_call(
+        [onError = std::move(onError),
+         onSuccess = std::move(onSuccess)](boost::system::error_code ec) {
+            if (ec)
+            {
+                onError(ec);
+                return;
+            }
+
+            onSuccess();
+        },
+        service, path, "org.freedesktop.DBus.Properties", "Set", interface,
+        propertyName, std::variant<T>(std::forward<T>(propertyValue)));
+}
+
+} // namespace sdbusplus::asio