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/meson.build b/example/meson.build
index 957c01d..aed2a33 100644
--- a/example/meson.build
+++ b/example/meson.build
@@ -22,6 +22,19 @@
     dependencies: [ boost_dep, sdbusplus_dep ],
 )
 
+executable(
+    'register-property',
+    'register-property.cpp',
+    cpp_args: [
+        '-DBOOST_ASIO_DISABLE_THREADS',
+        '-DBOOST_ALL_NO_LIB',
+        '-DBOOST_SYSTEM_NO_DEPRECATED',
+        '-DBOOST_ERROR_CODE_HEADER_ONLY',
+        '-DBOOST_COROUTINES_NO_DEPRECATION_WARNING',
+    ],
+    dependencies: [ boost_dep, sdbusplus_dep ],
+)
+
 calc_buildroot = meson.current_build_dir()
 calc_files = files(
     run_command(
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;
+}
diff --git a/include/sdbusplus/asio/object_server.hpp b/include/sdbusplus/asio/object_server.hpp
index b7e161e..f163208 100644
--- a/include/sdbusplus/asio/object_server.hpp
+++ b/include/sdbusplus/asio/object_server.hpp
@@ -375,96 +375,11 @@
                                        std::vector<std::string>{name_});
     }
 
-    // default getter and setter
-    template <typename PropertyType>
-    bool register_property(
-        const std::string& name, const PropertyType& property,
-        PropertyPermission access = PropertyPermission::readOnly)
-    {
-        // can only register once
-        if (initialized_)
-        {
-            return false;
-        }
-        if (!std::regex_match(name, std::regex(PropertyNamePattern)))
-        {
-            return false;
-        }
-        static const auto type =
-            utility::tuple_to_array(message::types::type_id<PropertyType>());
-
-        auto nameItr = propertyNames_.emplace(propertyNames_.end(), name);
-        auto propertyPtr = std::make_shared<PropertyType>(property);
-
-        callbacksGet_[name] = std::make_unique<callback_get_instance<
-            PropertyType, std::function<PropertyType(const PropertyType&)>>>(
-            propertyPtr, [](const PropertyType& value) { return value; });
-        callbacksSet_[name] = std::make_unique<callback_set_instance<
-            PropertyType,
-            std::function<int(const PropertyType&, PropertyType&)>>>(
-            propertyPtr, [](const PropertyType& req, PropertyType& old) {
-                old = req;
-                return 1;
-            });
-
-        if (access == PropertyPermission::readOnly)
-        {
-            vtable_.emplace_back(
-                vtable::property(nameItr->c_str(), type.data(), get_handler,
-                                 vtable::property_::emits_change));
-        }
-        else
-        {
-            vtable_.emplace_back(
-                vtable::property(nameItr->c_str(), type.data(), get_handler,
-                                 set_handler, vtable::property_::emits_change));
-        }
-        return true;
-    }
-
-    // custom setter, sets take an input property and respond with an int status
-    template <typename PropertyType, typename CallbackType>
-    bool register_property(const std::string& name,
-                           const PropertyType& property,
-                           CallbackType&& setFunction)
-    {
-        // can only register once
-        if (initialized_)
-        {
-            return false;
-        }
-        if (!std::regex_match(name, std::regex(PropertyNamePattern)))
-        {
-            return false;
-        }
-        static const auto type =
-            utility::tuple_to_array(message::types::type_id<PropertyType>());
-
-        auto nameItr = propertyNames_.emplace(propertyNames_.end(), name);
-        auto propertyPtr = std::make_shared<PropertyType>(property);
-
-        callbacksGet_[name] = std::make_unique<callback_get_instance<
-            PropertyType, std::function<PropertyType(const PropertyType&)>>>(
-            propertyPtr, [](const PropertyType& value) { return value; });
-
-        callbacksSet_[name] =
-            std::make_unique<callback_set_instance<PropertyType, CallbackType>>(
-                propertyPtr, std::move(setFunction));
-        vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(),
-                                              get_handler, set_handler,
-                                              vtable::property_::emits_change));
-
-        return true;
-    }
-
-    // custom getter and setter, gets take an input of void and respond with a
-    // property. property is only passed for type deduction
-    template <typename PropertyType, typename CallbackType,
-              typename CallbackTypeGet>
-    bool register_property(const std::string& name,
-                           const PropertyType& property,
-                           CallbackType&& setFunction,
-                           CallbackTypeGet&& getFunction)
+    template <typename PropertyType, typename CallbackTypeGet>
+    bool register_property_r(const std::string& name,
+                             const PropertyType& property,
+                             decltype(vtable::vtable_t::flags) flags,
+                             CallbackTypeGet&& getFunction)
     {
         // can only register once
         if (initialized_)
@@ -484,16 +399,107 @@
         callbacksGet_[name] = std::make_unique<
             callback_get_instance<PropertyType, CallbackTypeGet>>(
             propertyPtr, std::move(getFunction));
-        callbacksSet_[name] =
-            std::make_unique<callback_set_instance<PropertyType, CallbackType>>(
-                propertyPtr, std::move(setFunction));
+        callbacksSet_[name] = std::make_unique<callback_set_instance<
+            PropertyType,
+            std::function<int(const PropertyType&, PropertyType&)>>>(
+            propertyPtr, [](const PropertyType& req, PropertyType& old) {
+                old = req;
+                return 1;
+            });
 
         vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(),
-                                              get_handler, set_handler,
-                                              vtable::property_::emits_change));
+                                              get_handler, flags));
 
         return true;
     }
+
+    template <typename PropertyType, typename CallbackTypeSet,
+              typename CallbackTypeGet>
+    bool register_property_rw(const std::string& name,
+                              const PropertyType& property,
+                              decltype(vtable::vtable_t::flags) flags,
+                              CallbackTypeSet&& setFunction,
+                              CallbackTypeGet&& getFunction)
+    {
+        // can only register once
+        if (initialized_)
+        {
+            return false;
+        }
+        if (!std::regex_match(name, std::regex(PropertyNamePattern)))
+        {
+            return false;
+        }
+        static const auto type =
+            utility::tuple_to_array(message::types::type_id<PropertyType>());
+
+        auto nameItr = propertyNames_.emplace(propertyNames_.end(), name);
+        auto propertyPtr = std::make_shared<PropertyType>(property);
+
+        callbacksGet_[name] = std::make_unique<
+            callback_get_instance<PropertyType, CallbackTypeGet>>(
+            propertyPtr, std::move(getFunction));
+        callbacksSet_[name] = std::make_unique<
+            callback_set_instance<PropertyType, CallbackTypeSet>>(
+            propertyPtr, std::move(setFunction));
+
+        vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(),
+                                              get_handler, set_handler, flags));
+
+        return true;
+    }
+
+    // default getter and setter
+    template <typename PropertyType>
+    bool register_property(
+        const std::string& name, const PropertyType& property,
+        PropertyPermission access = PropertyPermission::readOnly)
+    {
+        if (access == PropertyPermission::readOnly)
+        {
+            return register_property_r(
+                name, property, vtable::property_::emits_change,
+                [](const PropertyType& value) { return value; });
+        }
+        else
+        {
+            return register_property_rw(
+                name, property, vtable::property_::emits_change,
+                [](const PropertyType& req, PropertyType& old) {
+                    old = req;
+                    return 1;
+                },
+                [](const PropertyType& value) { return value; });
+        }
+    }
+
+    // custom setter, sets take an input property and respond with an int status
+    template <typename PropertyType, typename CallbackTypeSet>
+    bool register_property(const std::string& name,
+                           const PropertyType& property,
+                           CallbackTypeSet&& setFunction)
+    {
+        return register_property_rw(
+            name, property, vtable::property_::emits_change,
+            std::forward<CallbackTypeSet>(setFunction),
+            [](const PropertyType& value) { return value; });
+    }
+
+    // custom getter and setter, gets take an input of void and respond with a
+    // property. property is only passed for type deduction
+    template <typename PropertyType, typename CallbackTypeSet,
+              typename CallbackTypeGet>
+    bool register_property(const std::string& name,
+                           const PropertyType& property,
+                           CallbackTypeSet&& setFunction,
+                           CallbackTypeGet&& getFunction)
+    {
+        return register_property_rw(name, property,
+                                    vtable::property_::emits_change,
+                                    std::forward<CallbackTypeSet>(setFunction),
+                                    std::forward<CallbackTypeGet>(getFunction));
+    }
+
     template <typename PropertyType, bool changesOnly = false>
     bool set_property(const std::string& name, const PropertyType& value)
     {
diff --git a/include/sdbusplus/vtable.hpp b/include/sdbusplus/vtable.hpp
index 3a0bdb7..09dbfc6 100644
--- a/include/sdbusplus/vtable.hpp
+++ b/include/sdbusplus/vtable.hpp
@@ -116,6 +116,7 @@
 constexpr auto emits_change = SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
 constexpr auto emits_invalidation = SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION;
 constexpr auto explicit_ = SD_BUS_VTABLE_PROPERTY_EXPLICIT;
+constexpr auto none = decltype(vtable_t::flags){};
 } // namespace property_
 
 constexpr vtable_t start(decltype(vtable_t::flags) flags)