sdbus++: generate server bindings for properties

Change-Id: Iabfefd94d1a261355d547c8c1cfbcf27cce825b7
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 63374db..b606033 100644
--- a/tools/templates/interface.mako.server.cpp
+++ b/tools/templates/interface.mako.server.cpp
@@ -27,6 +27,59 @@
 ${ s.cpp_prototype(loader, interface=interface, ptype='callback-cpp') }
     % endfor
 
+    % for p in interface.properties:
+${p.typeName} ${classname}::${p.camelCase}() const
+{
+    return _${p.camelCase};
+}
+
+int ${classname}::_callback_get_${p.name}(
+        sd_bus* bus, const char* path, const char* interface,
+        const char* property, sd_bus_message* reply, void* context,
+        sd_bus_error* error)
+{
+    auto m = message::message(sd_bus_message_ref(reply));
+
+    auto o = static_cast<${classname}*>(context);
+    m.append(o->${p.camelCase}());
+
+    return 0;
+}
+
+${p.typeName} ${classname}::${p.camelCase}(${p.typeName} value)
+{
+    _${p.camelCase} = value;
+
+    return _${p.camelCase};
+}
+
+int ${classname}::_callback_set_${p.name}(
+        sd_bus* bus, const char* path, const char* interface,
+        const char* property, sd_bus_message* value, void* context,
+        sd_bus_error* error)
+{
+    auto m = message::message(sd_bus_message_ref(value));
+
+    auto o = static_cast<${classname}*>(context);
+
+    decltype(_${p.camelCase}) v{};
+    m.read(v);
+    o->${p.camelCase}(v);
+
+    return 0;
+}
+
+namespace details
+{
+namespace ${classname}
+{
+static const auto _property_${p.name} =
+    utility::tuple_to_array(message::types::type_id<
+            ${p.typeName}>());
+}
+}
+    % endfor
+
 const vtable::vtable_t ${classname}::_vtable[] = {
     vtable::start(),
     % for m in interface.methods:
@@ -35,6 +88,13 @@
     % for s in interface.signals:
 ${ s.cpp_prototype(loader, interface=interface, ptype='vtable') }
     % endfor
+    % for p in interface.properties:
+    vtable::property("${p.name}",
+                     details::${classname}::_property_${p.name}
+                        .data(),
+                     _callback_get_${p.name},
+                     _callback_set_${p.name}),
+    % endfor
     vtable::end()
 };
 
diff --git a/tools/templates/interface.mako.server.hpp b/tools/templates/interface.mako.server.hpp
index 73aff6b..ca78cec 100644
--- a/tools/templates/interface.mako.server.hpp
+++ b/tools/templates/interface.mako.server.hpp
@@ -47,15 +47,42 @@
 ${ s.cpp_prototype(loader, interface=interface, ptype='header') }
     % endfor
 
+    % for p in interface.properties:
+        /** Get value of ${p.name} */
+        virtual ${p.typeName} ${p.camelCase}() const;
+        /** Set value of ${p.name} */
+        virtual ${p.typeName} ${p.camelCase}(${p.typeName} value);
+    % endfor
+
     private:
     % for m in interface.methods:
 ${ m.cpp_prototype(loader, interface=interface, ptype='callback-header') }
     % endfor
 
+    % for p in interface.properties:
+        /** @brief sd-bus callback for get-property '${p.name}' */
+        static int _callback_get_${p.name}(
+            sd_bus*, const char*, const char*, const char*,
+            sd_bus_message*, void*, sd_bus_error*);
+        /** @brief sd-bus callback for set-property '${p.name}' */
+        static int _callback_set_${p.name}(
+            sd_bus*, const char*, const char*, const char*,
+            sd_bus_message*, void*, sd_bus_error*);
+
+    % endfor
+
         static constexpr auto _interface = "${interface.name}";
         static const vtable::vtable_t _vtable[];
         interface::interface _${"_".join(interface.name.split('.'))}_interface;
 
+    % for p in interface.properties:
+        % if p.defaultValue:
+        ${p.typeName} _${p.camelCase} = ${p.defaultValue};
+        % else:
+        ${p.typeName} _${p.camelCase}{};
+        % endif
+    % endfor
+
 };
 
     % for s in namespaces: