sdbus++: generate callback methods for server bindings

Create sd-bus message handler methods as callbacks for object
method calls.  These generated callbacks will extract the
parameters from the message, call the virtual-method implementation,
and pack the response into a method-return message, which is sent
back to the caller.

Change-Id: Ia3872bbbbfdb57375a1ffdde7e64296271cdd532
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/tools/sdbus++ b/tools/sdbus++
index 6606d07..6051046 100755
--- a/tools/sdbus++
+++ b/tools/sdbus++
@@ -6,7 +6,8 @@
 def main():
     valid_types = { 'interface': sdbusplus.Interface }
     valid_processes = { 'markdown' : "markdown",
-                        'server-header' : "server_header" }
+                        'server-header' : "server_header",
+                        'server-cpp' : "server_cpp" }
 
     parser = argparse.ArgumentParser(description='Process sdbus++ YAML files.')
 
diff --git a/tools/sdbusplus/interface.py b/tools/sdbusplus/interface.py
index fa56eb9..7bbae38 100644
--- a/tools/sdbusplus/interface.py
+++ b/tools/sdbusplus/interface.py
@@ -33,3 +33,6 @@
 
     def server_header(self, loader):
         return self.render(loader, "interface.mako.server.hpp", interface=self)
+
+    def server_cpp(self, loader):
+        return self.render(loader, "interface.mako.server.cpp", interface=self)
diff --git a/tools/templates/interface.mako.server.cpp b/tools/templates/interface.mako.server.cpp
new file mode 100644
index 0000000..16f7f43
--- /dev/null
+++ b/tools/templates/interface.mako.server.cpp
@@ -0,0 +1,24 @@
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/message.hpp>
+#include <${"/".join(interface.name.split('.') + [ 'server.hpp' ])}>
+    <%
+        namespaces = interface.name.split('.')
+        classname = namespaces.pop()
+    %>
+namespace sdbusplus
+{
+namespace server
+{
+    % for s in namespaces:
+namespace ${s}
+{
+    % endfor
+    % for m in interface.methods:
+${ m.cpp_prototype(loader, interface=interface, ptype='callback-cpp') }
+    % endfor
+
+    % for s in namespaces:
+} // namespace ${s}
+    % endfor
+} // namespace server
+} // namespace sdbusplus
diff --git a/tools/templates/method.mako.prototype.hpp b/tools/templates/method.mako.prototype.hpp
index 2718812..e26e726 100644
--- a/tools/templates/method.mako.prototype.hpp
+++ b/tools/templates/method.mako.prototype.hpp
@@ -13,17 +13,30 @@
         return ",\n            ".\
             join([ parameter(p, defaultValue) for p in method.parameters ])
 
+    def parameters_as_local():
+        return "{};\n    ".join([ parameter(p) for p in method.parameters ])
+
+    def parameters_as_list():
+        return ", ".join([ p.camelCase for p in method.parameters ])
+
     def parameter(p, defaultValue=False):
         r = "%s %s" % (p.typeName, p.camelCase)
         if defaultValue:
             r += default_value(p)
         return r
 
+    def returns_as_tuple_index(tuple):
+        return ", ".join([ "std::move(std::get<%d>(%s))" % (i,tuple) \
+                for i in range(len(method.returns))])
+
     def default_value(p):
         if p.defaultValue != None:
             return " = " + str(p.defaultValue)
         else:
             return ""
+
+    def interface_name():
+        return interface.name.split('.').pop()
 %>
 ###
 ### Emit 'header'
@@ -54,4 +67,39 @@
          */
         static int _callback_${ method.CamelCase }(
             sd_bus_message*, void*, sd_bus_error*);
+###
+### Emit 'callback-cpp'
+###
+    % elif ptype == 'callback-cpp':
+int ${interface_name()}::_callback_${ method.CamelCase }(
+        sd_bus_message* msg, void* context, sd_bus_error* error)
+{
+    ### Need to add a ref to msg since we attached it to an sdbusplus::message.
+    auto m = sdbusplus::message::message(sd_bus_message_ref(msg));
+
+    % if len(method.parameters) != 0:
+    ${parameters_as_local()}{};
+
+    m.read(${parameters_as_list()});
+    % endif
+
+    auto o = static_cast<${interface_name()}*>(context);
+    % if len(method.returns) != 0:
+    auto r = \
+    %endif
+    o->${ method.camelCase }(${parameters_as_list()});
+
+    auto reply = m.new_method_return();
+    % if len(method.returns) == 0:
+    // No data to append on reply.
+    % elif len(method.returns) == 1:
+    reply.append(std::move(r));
+    % else:
+    reply.append(${returns_as_tuple_index("r")});
+    % endif
+
+    sdbusplus::bus::method_return(reply);
+
+    return 0;
+}
     % endif