Add support for type-only interfaces

Modify Maker template for type-only interfaces that do not
have properties.

Resolves openbmc/openbmc#1786

Change-Id: I2c48b37cf273943a0c696f6b92db0bc901a1c9b4
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/example/extra_interfaces.d/xyz/openbmc_project/Example/Iface3.interface.yaml b/example/extra_interfaces.d/xyz/openbmc_project/Example/Iface3.interface.yaml
new file mode 100644
index 0000000..6a76b8a
--- /dev/null
+++ b/example/extra_interfaces.d/xyz/openbmc_project/Example/Iface3.interface.yaml
@@ -0,0 +1,4 @@
+description: >
+    Example empty interface for PIM.
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/manager.hpp b/manager.hpp
index e6c60a3..624eb60 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -45,6 +45,92 @@
 template <typename T>
 using PropertiesVariantType = typename PropertiesVariant<T>::Type;
 
+template <typename T, typename U = int>
+struct HasProperties : std::false_type
+{
+};
+
+template <typename T>
+struct HasProperties <T,
+        decltype((void) std::declval<typename T::PropertiesVariant>(), 0)> :
+            std::true_type
+{
+};
+
+template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
+any_ns::any propMake(
+        sdbusplus::bus::bus& bus, const char* path, const Interface& props)
+{
+    using InterfaceVariant =
+        std::map<std::string, PropertiesVariantType<T>>;
+
+    InterfaceVariant v;
+    for (const auto& p : props)
+    {
+        v.emplace(
+            p.first,
+            convertVariant<PropertiesVariantType<T>>(p.second));
+    }
+
+    return any_ns::any(std::make_shared<T>(bus, path, v));
+}
+
+template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
+any_ns::any propMake(
+        sdbusplus::bus::bus& bus, const char* path, const Interface& props)
+{
+    return any_ns::any(std::make_shared<T>(bus, path));
+}
+
+template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
+void propAssign(const Interface& props, any_ns::any& holder)
+{
+    auto& iface = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
+    for (const auto& p : props)
+    {
+        iface.setPropertyByName(
+            p.first, convertVariant<PropertiesVariantType<T>>(p.second));
+    }
+}
+
+template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
+void propAssign(const Interface& props, any_ns::any& holder)
+{
+}
+
+template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
+void propSerialize(
+        const std::string& path, const std::string& iface,
+        const any_ns::any& holder)
+{
+    const auto& object =
+        *any_ns::any_cast<const std::shared_ptr<T> &>(holder);
+    cereal::serialize(path, iface, object);
+}
+
+template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
+void propSerialize(
+        const std::string& path, const std::string& iface,
+        const any_ns::any& holder)
+{
+    cereal::serialize(path, iface);
+}
+
+template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
+void propDeSerialize(
+        const std::string& path, const std::string& iface, any_ns::any& holder)
+{
+    auto& object = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
+    cereal::deserialize(path, iface, object);
+}
+
+template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
+void propDeSerialize(
+        const std::string& path, const std::string& iface, any_ns::any& holder)
+{
+}
+
+
 /** @struct MakeInterface
  *  @brief Adapt an sdbusplus interface proxy.
  *
@@ -58,49 +144,29 @@
 struct MakeInterface
 {
     static any_ns::any make(
-        sdbusplus::bus::bus& bus,
-        const char* path,
-        const Interface& props)
+        sdbusplus::bus::bus& bus, const char* path, const Interface& props)
     {
-        using InterfaceVariant =
-            std::map<std::string, PropertiesVariantType<T>>;
-
-        InterfaceVariant v;
-
-        for (const auto& p : props)
-        {
-            v.emplace(
-                p.first,
-                convertVariant<PropertiesVariantType<T>>(p.second));
-        }
-
-        return any_ns::any(std::make_shared<T>(bus, path, v));
+        return propMake<T>(bus, path, props);
     }
 
     static void assign(const Interface& props, any_ns::any& holder)
     {
-        auto& iface = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
-        for (const auto& p : props)
-        {
-            iface.setPropertyByName(
-                p.first, convertVariant<PropertiesVariantType<T>>(p.second));
-        }
+        propAssign<T>(props, holder);
     }
 
-    static void serialize(const std::string& path, const std::string& iface,
-                          const any_ns::any& holder)
+    static void serialize(
+        const std::string& path, const std::string& iface,
+        const any_ns::any& holder)
     {
-        const auto& object =
-            *any_ns::any_cast<const std::shared_ptr<T> &>(holder);
-        cereal::serialize(path, iface, object);
+        propSerialize<T>(path, iface, holder);
     }
 
-    static void deserialize(const std::string& path, const std::string& iface,
-                            any_ns::any& holder)
+    static void deserialize(
+        const std::string& path, const std::string& iface, any_ns::any& holder)
     {
-        auto& object = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
-        cereal::deserialize(path, iface, object);
+        propDeSerialize<T>(path, iface, holder);
     }
+
 };
 
 /** @class Manager
diff --git a/pimgen.py b/pimgen.py
index a31267f..de5419d 100755
--- a/pimgen.py
+++ b/pimgen.py
@@ -44,7 +44,9 @@
         return self.dict.keys()
 
     def names(self, interface):
-        names = [x["name"] for x in self.dict[interface]]
+        names = []
+        if self.dict[interface]:
+            names = [x["name"] for x in self.dict[interface]]
         return names
 
 
@@ -283,13 +285,13 @@
             for interface, properties, in interfaces.iteritems():
                 key_i = TrivialArgument(value=interface, type='string')
                 value_p = []
-
-                for prop, value in properties.iteritems():
-                    key_p = TrivialArgument(value=prop, type='string')
-                    value_v = TrivialArgument(
-                        decorators=[Literal(value.get('type', None))],
-                        **value)
-                    value_p.append(InitializerList(values=[key_p, value_v]))
+                if properties:
+                    for prop, value in properties.iteritems():
+                        key_p = TrivialArgument(value=prop, type='string')
+                        value_v = TrivialArgument(
+                            decorators=[Literal(value.get('type', None))],
+                            **value)
+                        value_p.append(InitializerList(values=[key_p, value_v]))
 
                 value_p = InitializerList(values=value_p)
                 value_i.append(InitializerList(values=[key_i, value_p]))
@@ -536,13 +538,9 @@
                 i = y.replace('.interface.yaml', '').replace(os.sep, '.')
 
                 # PIM can't create interfaces with methods.
-                # PIM can't create interfaces without properties.
                 parsed = yaml.safe_load(fd.read())
                 if parsed.get('methods', None):
                     continue
-                properties = parsed.get('properties', None)
-                if not properties:
-                    continue
                 # Cereal can't understand the type sdbusplus::object_path. This
                 # type is a wrapper around std::string. Ignore interfaces having
                 # a property of this type for now. The only interface that has a
@@ -551,8 +549,10 @@
                 # this interface.
                 # TODO via openbmc/openbmc#2123 : figure out how to make Cereal
                 # understand sdbusplus::object_path.
-                if any('path' in p['type'] for p in properties):
-                    continue
+                properties = parsed.get('properties', None)
+                if properties:
+                    if any('path' in p['type'] for p in properties):
+                        continue
                 interface_composite[i] = properties
                 interfaces.append(i)
 
diff --git a/serialize.hpp b/serialize.hpp
index 7d0b10e..84463b3 100644
--- a/serialize.hpp
+++ b/serialize.hpp
@@ -34,6 +34,21 @@
     oarchive(object);
 }
 
+/** @brief Serialize inventory item path
+ *  Serializing only path for an empty interface to be consistent
+ *  interfaces.
+ *  @param[in] path - DBus object path
+ *  @param[in] iface - Inventory interface name
+ */
+inline void serialize(const Path& path, const Interface& iface)
+{
+    fs::path p(PIM_PERSIST_PATH);
+    p /= path;
+    fs::create_directories(p);
+    p /= iface;
+    std::ofstream os(p, std::ios::binary);
+}
+
 /** @brief Deserialize inventory item
  *
  *  @param[in] path - DBus object path