sdbus++: Generate initial client header

By "client-header" option, sdbus++ could generate a client header file
that provides the code used by the client.

Currently it only includes the interface name's string in the header
file, it could be extended in the future.

The client header could be merged together for all the interfaces, so
that an interface library could provide a single header to represent all
the interface strings.

Usage:

 sdbus++ -r $(srcdir) -t $(top_builddir)/tools/sdbusplus/templates \
    interface client-header <interface>

Tested: Generate the header in example and use static_assert to verify
        it is the same as the interface defined in the interface calss.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: Idafc4724efa88a2dfd37e3f7735732c45171fd88
diff --git a/example/Makefile.am b/example/Makefile.am
index 6d43c4a..294ee00 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -33,7 +33,8 @@
 	net/poettering/Calculator/server.hpp \
 	net/poettering/Calculator/server.cpp \
 	net/poettering/Calculator/error.hpp \
-	net/poettering/Calculator/error.cpp
+	net/poettering/Calculator/error.cpp \
+	net/poettering/Calculator/client.hpp
 
 calculator_markdown_generated_files = \
 	calculator.md
@@ -82,4 +83,10 @@
 	@top_srcdir@/tools/sdbus++ \
 	    -r $(srcdir) -t $(top_builddir)/tools/sdbusplus/templates \
 	    error markdown net.poettering.Calculator >> $@
+
+net/poettering/Calculator/client.hpp:
+	@mkdir -p $(@D)
+	@top_srcdir@/tools/sdbus++ \
+	    -r $(srcdir) -t $(top_builddir)/tools/sdbusplus/templates \
+	    interface client-header net.poettering.Calculator > $@
 endif
diff --git a/example/calculator-server.cpp b/example/calculator-server.cpp
index f128bf1..2c7f3e4 100644
--- a/example/calculator-server.cpp
+++ b/example/calculator-server.cpp
@@ -1,7 +1,9 @@
 #include <iostream>
+#include <net/poettering/Calculator/client.hpp>
 #include <net/poettering/Calculator/error.hpp>
 #include <net/poettering/Calculator/server.hpp>
 #include <sdbusplus/server.hpp>
+#include <string_view>
 
 using Calculator_inherit =
     sdbusplus::server::object_t<sdbusplus::net::poettering::server::Calculator>;
@@ -52,6 +54,11 @@
     // Define a dbus path location to place the object.
     constexpr auto path = "/net/poettering/calculator";
 
+    static_assert(
+        std::string_view(
+            sdbusplus::net::poettering::client::Calculator::interface) ==
+        std::string_view(Calculator::interface));
+
     // Create a new bus and affix an object manager for the subtree path we
     // intend to place objects at..
     auto b = sdbusplus::bus::new_default();
diff --git a/tools/sdbus++ b/tools/sdbus++
index 26969f3..6cfcb9e 100755
--- a/tools/sdbus++
+++ b/tools/sdbus++
@@ -15,7 +15,8 @@
                        'server-header': "server_header",
                        'server-cpp': "server_cpp",
                        'exception-header': "exception_header",
-                       'exception-cpp': "exception_cpp"}
+                       'exception-cpp': "exception_cpp",
+                       'client-header': "client_header"}
 
     parser = argparse.ArgumentParser(description='Process sdbus++ YAML files.')
 
diff --git a/tools/sdbusplus/interface.py b/tools/sdbusplus/interface.py
index 29f41e4..e6251b3 100644
--- a/tools/sdbusplus/interface.py
+++ b/tools/sdbusplus/interface.py
@@ -60,3 +60,6 @@
 
     def server_cpp(self, loader):
         return self.render(loader, "interface.mako.server.cpp", interface=self)
+
+    def client_header(self, loader):
+        return self.render(loader, "interface.mako.client.hpp", interface=self)
diff --git a/tools/sdbusplus/templates/interface.mako.client.hpp b/tools/sdbusplus/templates/interface.mako.client.hpp
new file mode 100644
index 0000000..c615ced
--- /dev/null
+++ b/tools/sdbusplus/templates/interface.mako.client.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+<%
+    namespaces = interface.name.split('.')
+    classname = namespaces.pop()
+%>
+namespace sdbusplus
+{
+% for s in namespaces:
+namespace ${s}
+{
+% endfor
+namespace client
+{
+namespace ${classname}
+{
+
+static constexpr auto interface = "${interface.name}";
+
+} // namespace ${classname}
+} // namespace client
+% for s in reversed(namespaces):
+} // namespace ${s}
+% endfor
+} // namespace sdbusplus