object: add binding for 'sd_bus_emit_object_added/removed

Change-Id: I450715fb47c975737f9326c3c945da762c53accb
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/sdbusplus/bus.hpp b/sdbusplus/bus.hpp
index eae7cd2..fe68690 100644
--- a/sdbusplus/bus.hpp
+++ b/sdbusplus/bus.hpp
@@ -11,6 +11,7 @@
 // Forward declare.
 namespace server { namespace interface { struct interface; } }
 namespace server { namespace manager { struct manager; } }
+namespace server { namespace object { template<class...> struct object; } }
 
 namespace bus
 {
@@ -144,6 +145,7 @@
 
     friend struct server::interface::interface;
     friend struct server::manager::manager;
+    template<class... Args> friend struct server::object::object;
 
     private:
         busp_t get() { return _bus.get(); }
diff --git a/sdbusplus/object.hpp b/sdbusplus/object.hpp
new file mode 100644
index 0000000..421dc53
--- /dev/null
+++ b/sdbusplus/object.hpp
@@ -0,0 +1,115 @@
+#pragma once
+#include <sdbusplus/bus.hpp>
+
+namespace sdbusplus
+{
+
+namespace server
+{
+
+namespace object
+{
+
+namespace details
+{
+
+/** Templates to allow multiple inheritence via template parameters.
+ *
+ *  These allow an object to group multiple dbus interface bindings into a
+ *  single class.
+ */
+template <class T, class... Rest> struct compose_impl :
+        T, compose_impl<Rest...>
+{
+    compose_impl(bus::bus& bus, const char* path) :
+        T(bus, path), compose_impl<Rest...>(bus, path) {}
+};
+
+/** Specialization for single element. */
+template <class T> struct compose_impl<T> : T
+{
+    compose_impl(bus::bus& bus, const char* path) :
+        T(bus, path) {}
+};
+
+/** Default compose operation for variadic arguments. */
+template <class... Args> struct compose : compose_impl<Args...>
+{
+    compose(bus::bus& bus, const char* path) :
+        compose_impl<Args...>(bus, path) {}
+};
+
+/** Specialization for zero variadic arguments. */
+template <> struct compose<>
+{
+    compose(bus::bus& bus, const char* path) {}
+};
+
+} // namespace details
+
+/** Class to compose multiple dbus interfaces and object signals.
+ *
+ *  Any number of classes representing a dbus interface may be composed into
+ *  a single dbus object.  The interfaces will be created first and hooked
+ *  into the object space via the 'add_object_vtable' calls.  Afterwards,
+ *  a signal will be emitted for the whole object to indicate all new
+ *  interfaces via 'sd_bus_emit_object_added'.
+ *
+ *  Similar, on destruction, the interfaces are removed (unref'd) and the
+ *  'sd_bus_emit_object_removed' signals are emitted.
+ *
+ */
+template<class... Args>
+struct object : details::compose<Args...>
+{
+        /* Define all of the basic class operations:
+         *     Not allowed:
+         *         - Default constructor to avoid nullptrs.
+         *         - Copy operations due to internal unique_ptr.
+         *     Allowed:
+         *         - Move operations.
+         *         - Destructor.
+         */
+    object() = delete;
+    object(const object&) = delete;
+    object& operator=(const object&) = delete;
+    object(object&&) = default;
+    object& operator=(object&&) = default;
+
+    /** Construct an 'object' on a bus with a path.
+     *
+     *  @param[in] bus - The bus to place the object on.
+     *  @param[in] path - The path the object resides at.
+     */
+    object(bus::bus& bus,
+           const char* path)
+        : details::compose<Args...>(bus, path),
+          __sdbusplus_server_object_bus(sd_bus_ref(bus.get())),
+          __sdbusplus_server_object_path(path)
+    {
+        sd_bus_emit_object_added(__sdbusplus_server_object_bus.get(),
+                                 __sdbusplus_server_object_path.c_str());
+    }
+
+    ~object()
+    {
+        sd_bus_emit_object_removed(__sdbusplus_server_object_bus.get(),
+                                   __sdbusplus_server_object_path.c_str());
+    }
+
+    private:
+        // These member names are purposefully chosen as long and, hopefully,
+        // unique.  Since an object is 'composed' via multiple-inheritence,
+        // all members need to have unique names to ensure there is no
+        // ambiguity.
+        bus::bus __sdbusplus_server_object_bus;
+        std::string __sdbusplus_server_object_path;
+
+};
+
+} // namespace object
+
+template <class... Args> using object_t = object::object<Args...>;
+
+} // namespace server
+} // namespace sdbusplus