sdbus++: generate signal broadcast bindings

Change-Id: If1b04fedc95614c95a23768f95801e2bb9b4c0e9
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/tools/templates/interface.mako.server.cpp b/tools/templates/interface.mako.server.cpp
index 63bc788..63374db 100644
--- a/tools/templates/interface.mako.server.cpp
+++ b/tools/templates/interface.mako.server.cpp
@@ -23,11 +23,18 @@
 ${ m.cpp_prototype(loader, interface=interface, ptype='callback-cpp') }
     % endfor
 
+    % for s in interface.signals:
+${ s.cpp_prototype(loader, interface=interface, ptype='callback-cpp') }
+    % endfor
+
 const vtable::vtable_t ${classname}::_vtable[] = {
     vtable::start(),
     % for m in interface.methods:
 ${ m.cpp_prototype(loader, interface=interface, ptype='vtable') }
     % endfor
+    % for s in interface.signals:
+${ s.cpp_prototype(loader, interface=interface, ptype='vtable') }
+    % endfor
     vtable::end()
 };
 
diff --git a/tools/templates/interface.mako.server.hpp b/tools/templates/interface.mako.server.hpp
index 1d23646..73aff6b 100644
--- a/tools/templates/interface.mako.server.hpp
+++ b/tools/templates/interface.mako.server.hpp
@@ -43,6 +43,10 @@
 ${ m.cpp_prototype(loader, interface=interface, ptype='header') }
     % endfor
 
+    % for s in interface.signals:
+${ s.cpp_prototype(loader, interface=interface, ptype='header') }
+    % endfor
+
     private:
     % for m in interface.methods:
 ${ m.cpp_prototype(loader, interface=interface, ptype='callback-header') }
diff --git a/tools/templates/signal.mako.prototype.hpp b/tools/templates/signal.mako.prototype.hpp
new file mode 100644
index 0000000..8dfaf81
--- /dev/null
+++ b/tools/templates/signal.mako.prototype.hpp
@@ -0,0 +1,77 @@
+<%
+    def parameters(defaultValue=False):
+        return ",\n            ".\
+            join([ parameter(p, defaultValue) for p in signal.properties ])
+
+    def parameter(p, defaultValue=False):
+        r = "%s %s" % (p.typeName, p.camelCase)
+        if defaultValue:
+            r += default_value(p)
+        return r
+
+    def parameters_as_list():
+        return ", ".join([ p.camelCase for p in signal.properties ])
+
+    def parameters_types_as_list():
+        return ", ".join([ p.typeName for p in signal.properties ])
+
+    def default_value(p):
+        if p.defaultValue != None:
+            return " = " + str(p.defaultValue)
+        else:
+            return ""
+
+    def interface_name():
+        return interface.name.split('.').pop()
+%>
+###
+### Emit 'header'
+###
+    % if ptype == 'header':
+        /** @brief Send signal '${signal.name}'
+         *
+         *  ${ signal.description.strip() }
+    % if len(signal.properties) != 0:
+         *
+        % for p in signal.properties:
+         *  @param[in] ${p.camelCase} - ${p.description.strip()}
+        % endfor
+    % endif
+         */
+        void ${ signal.camelCase }(
+            ${ parameters(True) });
+###
+### Emit 'vtable'
+###
+    % elif ptype == 'vtable':
+    vtable::signal("${signal.name}",
+                   details::${interface_name()}::_signal_${signal.CamelCase }
+                        .data()),
+###
+### Emit 'callback-cpp'
+###
+    % elif ptype == 'callback-cpp':
+void ${interface_name()}::${ signal.camelCase }(
+            ${ parameters() })
+{
+    auto& i = _${"_".join(interface.name.split('.'))}_interface;
+    auto m = i.new_signal("${ signal.name }");
+
+    m.append(${ parameters_as_list() });
+    m.signal_send();
+}
+
+namespace details
+{
+namespace ${interface_name()}
+{
+static const auto _signal_${ signal.CamelCase } =
+    % if len(signal.properties) == 0:
+        utility::tuple_to_array(std::make_tuple('\0'));
+    % else:
+        utility::tuple_to_array(message::types::type_id<
+                ${ parameters_types_as_list() }>());
+    % endif
+}
+}
+    % endif