Added flags parameter to register_property method

- flags parameter is set by default to vtable::property_::emits_change
- with this change user can set parameter to vtable::property_::none
  to avoid sending property change signal for greater performance
- added example/register-property.cpp with example application
  which uses new functions

Tested:
- run openbmc tests with this change, no regression detected
- tested that default behaviour (property_::emits_change) is working as intended
- tested that properties still signal change when emit_change flag is used
- tested that properties doesn't signal change when no emit_change flag is used
- tested that const flag is passed and handled corretly
- verified that performance of application increases when emit_change flag turned off

Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
Change-Id: If6187cf076b8e05a5211c83797374a3be94da0c9
diff --git a/example/register-property.cpp b/example/register-property.cpp
new file mode 100644
index 0000000..651e974
--- /dev/null
+++ b/example/register-property.cpp
@@ -0,0 +1,189 @@
+#include <boost/asio.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <iostream>
+
+namespace xyz
+{
+namespace demo
+{
+
+const std::string path = "/xyz/demo";
+const std::string name = "xyz.demo";
+
+} // namespace demo
+} // namespace xyz
+
+namespace name
+{
+
+const std::string greetings = "Greetings";
+const std::string goodbyes = "Goodbyes";
+
+} // namespace name
+
+namespace utils
+{
+
+template <class T>
+class Property
+{
+  public:
+    Property(sdbusplus::asio::connection& bus, std::string_view service,
+             std::string_view path, std::string_view interface,
+             std::string_view name) :
+        bus_(bus),
+        service_(service), path_(path), interface_(interface), name_(name)
+    {}
+
+    template <class F>
+    void async_get(F&& callback)
+    {
+        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_);
+    }
+
+    template <class F>
+    void async_set(const T& value, F&& callback)
+    {
+        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);
+    }
+
+  private:
+    sdbusplus::asio::connection& bus_;
+    std::string service_;
+    std::string path_;
+    std::string interface_;
+    std::string name_;
+};
+
+} // namespace utils
+
+class Application
+{
+  public:
+    Application(boost::asio::io_context& ioc, sdbusplus::asio::connection& bus,
+                sdbusplus::asio::object_server& objServer) :
+        ioc_(ioc),
+        bus_(bus), objServer_(objServer)
+    {
+        demo_ = objServer_.add_interface(xyz::demo::path, xyz::demo::name);
+
+        demo_->register_property_r(name::greetings, std::string(),
+                                   sdbusplus::vtable::property_::const_,
+                                   [this](const auto&) { return greetings_; });
+
+        demo_->register_property_rw(
+            name::goodbyes, std::string(),
+            sdbusplus::vtable::property_::emits_change,
+            [this](const auto& newPropertyValue, const auto&) {
+                goodbyes_ = newPropertyValue;
+                return 1;
+            },
+            [this](const auto&) { return goodbyes_; });
+
+        demo_->initialize();
+    }
+
+    ~Application()
+    {
+        objServer_.remove_interface(demo_);
+    }
+
+    void asyncReadProperties()
+    {
+        propertyGreetings.async_get([](std::optional<std::string> value) {
+            std::cout << "Greetings value is: "
+                      << value.value_or("std::nullopt") << "\n";
+        });
+
+        propertyGoodbyes.async_get([](std::optional<std::string> value) {
+            std::cout << "Goodbyes value is: " << value.value_or("std::nullopt")
+                      << "\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";
+                }
+            });
+
+        propertyGoodbyes.async_set(
+            "Bye bye", [this](const boost::system::error_code& error) {
+                if (!error)
+                {
+                    std::cout << "Changed goodbyes property as expected\n";
+                }
+                boost::asio::post(ioc_, [this] { asyncReadProperties(); });
+            });
+    }
+
+  private:
+    boost::asio::io_context& ioc_;
+    sdbusplus::asio::connection& bus_;
+    sdbusplus::asio::object_server& objServer_;
+
+    std::shared_ptr<sdbusplus::asio::dbus_interface> demo_;
+    std::string greetings_ = "Hello";
+    std::string goodbyes_ = "Bye";
+
+    utils::Property<std::string> propertyGreetings{
+        bus_, xyz::demo::name, xyz::demo::path, xyz::demo::name,
+        name::greetings};
+    utils::Property<std::string> propertyGoodbyes{
+        bus_, xyz::demo::name, xyz::demo::path, xyz::demo::name,
+        name::goodbyes};
+};
+
+int main(int, char**)
+{
+    boost::asio::io_context ioc;
+    boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
+
+    signals.async_wait(
+        [&ioc](const boost::system::error_code&, const int&) { ioc.stop(); });
+
+    auto bus = std::make_shared<sdbusplus::asio::connection>(ioc);
+    auto objServer = std::make_unique<sdbusplus::asio::object_server>(bus);
+
+    bus->request_name(xyz::demo::name.c_str());
+
+    Application app(ioc, *bus, *objServer);
+
+    boost::asio::post(ioc, [&app] { app.asyncReadProperties(); });
+    boost::asio::post(ioc, [&app] { app.asyncChangeProperty(); });
+
+    ioc.run();
+
+    return 0;
+}