snoop_listen: Add constructors for SnoopListen

Provide two additional constructors for SnoopListen, one that takes
any std::function that operates on sdbusplus message, and another one
that takes std::function that operates on the uint64_t postcode. This
allows user to easily register a POST code handler without writing much
of the boiler plate code.

example.cpp is also converted to use the new constructor.

Tested:
'snooper' program compiled from example.cpp behaves correctly.

Change-Id: I20dc4fa5067e836d3cfa8ebb616a131f38a93a30
Signed-off-by: Kun Yi <kunyi731@gmail.com>
diff --git a/example.cpp b/example.cpp
index 55bd4ed..6754b0e 100644
--- a/example.cpp
+++ b/example.cpp
@@ -19,35 +19,39 @@
 #include <iostream>
 #include <memory>
 
-#include <sdbusplus/bus.hpp>
-#include <sdbusplus/message.hpp>
-#include <sdbusplus/server.hpp>
+#include "lpcsnoop/snoop_listen.hpp"
 
-#include "lpcsnoop/snoop.hpp"
-
-/*
- * Handle incoming dbus signal we care about.
- */
-static int DbusHandleSignal(sd_bus_message* msg, void* data, sd_bus_error* err);
-
-/*
- * Get the match signal for dbus.
- */
-static std::string GetMatch(void);
-
-// Example object that listens for dbus updates.
-class SnoopListen
+/* Example PostCode handler which simply prints them */
+static void printPostcode(uint64_t postcode)
 {
-  public:
-    SnoopListen(sdbusplus::bus::bus& bus) :
-        _bus(bus), _signal(bus, GetMatch().c_str(), DbusHandleSignal, this)
-    {
-    }
+    /* Print output to verify the example program is receiving values. */
+    std::printf("recv: 0x%" PRIx64 "\n", postcode);
+}
 
-  private:
-    sdbusplus::bus::bus& _bus;
-    sdbusplus::server::match::match _signal;
-};
+/*
+ * One can also specify custom handler that operates on
+ * sdbusplus::message::message type and pass them to constructor.
+ * e.g.
+ *
+ * static void PrintMessageMap(sdbusplus::message::message& m)
+ * {
+ *     using sdbusplus::message::variant_ns::get;
+ *     std::string messageBusName;
+ *     std::map<std::string, sdbusplus::message::variant<uint64_t>>
+ *         messageData;
+ *
+ *     m.read(messageBusName, messageData);
+ *
+ *     std::cout << "Got message from " << messageBusName << std::endl;
+ *     for (const auto& kv : messageData)
+ *     {
+ *         std::cout << "Key: " << kv.first << std::endl;
+ *         std::cout << "Value: " << get<uint64_t>(kv.second) << std::endl;
+ *     }
+ * }
+ *
+ * lpcsnoop::SnoopListen snoop(ListenBus, PrintMessageMap);
+ */
 
 /*
  * This is the entry point for the application.
@@ -58,7 +62,7 @@
 int main(int argc, char* argv[])
 {
     auto ListenBus = sdbusplus::bus::new_default();
-    SnoopListen snoop(ListenBus);
+    lpcsnoop::SnoopListen snoop(ListenBus, printPostcode);
 
     while (true)
     {
@@ -68,34 +72,3 @@
 
     return 0;
 }
-
-static int DbusHandleSignal(sd_bus_message* msg, void* data, sd_bus_error* err)
-{
-    auto sdbpMsg = sdbusplus::message::message(msg);
-
-    std::string msgSensor, busName{SNOOP_BUSNAME};
-    std::map<std::string, sdbusplus::message::variant<uint64_t>> msgData;
-    sdbpMsg.read(msgSensor, msgData);
-
-    if (msgSensor == busName)
-    {
-        auto valPropMap = msgData.find("Value");
-        if (valPropMap != msgData.end())
-        {
-            uint64_t rawValue = sdbusplus::message::variant_ns::get<uint64_t>(
-                valPropMap->second);
-
-            /* Print output to verify the example program is receiving values.
-             */
-            std::printf("recv: 0x%" PRIx64 "\n", rawValue);
-        }
-    }
-
-    return 0;
-}
-
-static std::string GetMatch(void)
-{
-    return "type='signal',interface='org.freedesktop.DBus.Properties',"
-           "member='PropertiesChanged',path='" SNOOP_OBJECTPATH "'";
-}
diff --git a/lpcsnoop/snoop_listen.hpp b/lpcsnoop/snoop_listen.hpp
index 15b8c0f..4d0ef36 100644
--- a/lpcsnoop/snoop_listen.hpp
+++ b/lpcsnoop/snoop_listen.hpp
@@ -22,26 +22,39 @@
 
 namespace lpcsnoop
 {
-
-using DbusSignalHandler = int (*)(sd_bus_message*, void*, sd_bus_error*);
+using sdbusplus::message::variant_ns::get;
 
 /* Returns matching string for what signal to listen on Dbus */
 static const std::string GetMatchRule()
 {
-    return "type='signal',"
-           "interface='org.freedesktop.DBus.Properties',"
-           "member='PropertiesChanged',"
-           "path='" SNOOP_OBJECTPATH "'";
+    using namespace sdbusplus::bus::match::rules;
+
+    return type::signal() + interface("org.freedesktop.DBus.Properties") +
+           member("PropertiesChanged") + path(SNOOP_OBJECTPATH);
 }
 
 class SnoopListen
 {
+    using message_handler_t = std::function<void(sdbusplus::message::message&)>;
+    using postcode_handler_t = std::function<void(uint64_t)>;
+
   public:
-    SnoopListen(sdbusplus::bus::bus& busIn, DbusSignalHandler handler) :
+    SnoopListen(sdbusplus::bus::bus& busIn, sd_bus_message_handler_t handler) :
         bus(busIn), signal(busIn, GetMatchRule().c_str(), handler, this)
     {
     }
 
+    SnoopListen(sdbusplus::bus::bus& busIn, message_handler_t handler) :
+        bus(busIn), signal(busIn, GetMatchRule(), handler)
+    {
+    }
+
+    SnoopListen(sdbusplus::bus::bus& busIn, postcode_handler_t handler) :
+        SnoopListen(busIn, std::bind(defaultMessageHandler, handler,
+                                     std::placeholders::_1))
+    {
+    }
+
     SnoopListen() = delete; // no default constructor
     ~SnoopListen() = default;
     SnoopListen(const SnoopListen&) = delete;
@@ -52,6 +65,27 @@
   private:
     sdbusplus::bus::bus& bus;
     sdbusplus::server::match::match signal;
+
+    /*
+     * Default message handler which listens to published messages on snoop
+     * DBus path, and calls the given postcode_handler on each value received.
+     */
+    static void defaultMessageHandler(postcode_handler_t& handler,
+                                      sdbusplus::message::message& m)
+    {
+        std::string messageBusName;
+        std::map<std::string, sdbusplus::message::variant<uint64_t>>
+            messageData;
+        constexpr char propertyKey[] = "Value";
+
+        m.read(messageBusName, messageData);
+
+        if (messageBusName == SNOOP_BUSNAME &&
+            messageData.find(propertyKey) != messageData.end())
+        {
+            handler(get<uint64_t>(messageData[propertyKey]));
+        }
+    }
 };
 
 } // namespace lpcsnoop