sdbus++: async: server: generate get-property fn
Add binding generation for get-property functions.
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I60d44b25d718373acfd581faf158f45f5b5430e9
diff --git a/example/calculator-aserver.cpp b/example/calculator-aserver.cpp
index 220cda1..536ee25 100644
--- a/example/calculator-aserver.cpp
+++ b/example/calculator-aserver.cpp
@@ -13,6 +13,16 @@
ctx.spawn(startup());
}
+ auto get_property(owner_t) const
+ {
+ return "asdf";
+ }
+
+ auto get_property(last_result_t) const
+ {
+ return 42;
+ }
+
private:
auto startup() -> sdbusplus::async::task<>
{
diff --git a/include/sdbusplus/async/server.hpp b/include/sdbusplus/async/server.hpp
index b7dfbdc..cfc4d78 100644
--- a/include/sdbusplus/async/server.hpp
+++ b/include/sdbusplus/async/server.hpp
@@ -50,6 +50,32 @@
return static_cast<T*>(this)->ctx;
}
};
+
+/* Determine if a type has a get_property call. */
+template <typename Tag, typename Instance>
+concept has_get_property_nomsg =
+ requires(const Instance& i) { i.get_property(Tag{}); };
+
+/* Determine if a type has a get property call that requries a msg. */
+template <typename Tag, typename Instance>
+concept has_get_property_msg = requires(
+ const Instance& i, sdbusplus::message_t& m) { i.get_property(Tag{}, m); };
+
+/* Determine if a type has any get_property call. */
+template <typename Tag, typename Instance>
+concept has_get_property = has_get_property_nomsg<Tag, Instance> ||
+ has_get_property_msg<Tag, Instance>;
+
+/* Determine if a type is missing the 'const' on get-property calls. */
+template <typename Tag, typename Instance>
+concept has_get_property_missing_const =
+ !has_get_property<Tag, Instance> &&
+ (
+ requires(Instance& i) { i.get_property(Tag{}); } ||
+ requires(Instance& i, sdbusplus::message_t& m) {
+ i.get_property(Tag{}, m);
+ });
+
} // namespace server::details
} // namespace sdbusplus::async
diff --git a/tools/meson.build b/tools/meson.build
index 8e1e631..dfc1f03 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -24,6 +24,12 @@
'sdbusplus/templates/method.prototype.hpp.mako',
'sdbusplus/templates/method.server.vtable.cpp.mako',
'sdbusplus/templates/property.md.mako',
+ 'sdbusplus/templates/property.aserver.callback.hpp.mako',
+ 'sdbusplus/templates/property.aserver.get.hpp.mako',
+ 'sdbusplus/templates/property.aserver.tag.hpp.mako',
+ 'sdbusplus/templates/property.aserver.typeid.hpp.mako',
+ 'sdbusplus/templates/property.aserver.value.hpp.mako',
+ 'sdbusplus/templates/property.aserver.vtable.hpp.mako',
'sdbusplus/templates/property.client.hpp.mako',
'sdbusplus/templates/property.server.cpp.mako',
'sdbusplus/templates/property.server.vtable.cpp.mako',
diff --git a/tools/sdbusplus/templates/interface.aserver.hpp.mako b/tools/sdbusplus/templates/interface.aserver.hpp.mako
index 1ec817a..f6e419b 100644
--- a/tools/sdbusplus/templates/interface.aserver.hpp.mako
+++ b/tools/sdbusplus/templates/interface.aserver.hpp.mako
@@ -1,6 +1,7 @@
#pragma once
#include <sdbusplus/async/server.hpp>
#include <sdbusplus/server/interface.hpp>
+#include <sdbusplus/server/transaction.hpp>
#include <type_traits>
@@ -38,10 +39,12 @@
namespace details
{
+namespace server_details = sdbusplus::async::server::details;
+
template <typename Instance, typename Server>
class ${interface.classname} :
public sdbusplus::common::${interface.cppNamespacedClass()},
- protected sdbusplus::async::server::details::server_context_friend
+ protected server_details::server_context_friend
{
public:
explicit ${interface.classname}(const char* path) :
@@ -65,24 +68,46 @@
_${interface.joinedName("_", "interface")}.emit_removed();
}
+ /* Property access tags. */
+% for p in interface.properties:
+${p.render(loader, "property.aserver.tag.hpp.mako", property=p, interface=interface)}\
+% endfor
+
+% for p in interface.properties:
+${p.render(loader, "property.aserver.get.hpp.mako", property=p, interface=interface)}
+% endfor
+
private:
/** @return the async context */
sdbusplus::async::context& _context()
{
- return sdbusplus::async::server::details::server_context_friend::
- context<Server>();
+ return server_details::server_context_friend::context<Server>();
}
sdbusplus::server::interface_t
_${interface.joinedName("_", "interface")};
+% for p in interface.properties:
+${p.render(loader, "property.aserver.value.hpp.mako", property=p, interface=interface)}\
+% endfor
+
+% for p in interface.properties:
+${p.render(loader, "property.aserver.typeid.hpp.mako", property=p, interface=interface)}\
+% endfor
% for s in interface.signals:
${s.render(loader, "signal.aserver.typeid.hpp.mako", signal=s, interface=interface)}\
% endfor
+% for p in interface.properties:
+${p.render(loader, "property.aserver.callback.hpp.mako", property=p, interface=interface)}
+% endfor
+
static constexpr sdbusplus::vtable_t _vtable[] = {
vtable::start(),
+% for p in interface.properties:
+${p.render(loader, "property.aserver.vtable.hpp.mako", property=p, interface=interface)}\
+% endfor
% for s in interface.signals:
${s.render(loader, "signal.aserver.vtable.hpp.mako", signal=s, interface=interface)}\
% endfor
diff --git a/tools/sdbusplus/templates/property.aserver.callback.hpp.mako b/tools/sdbusplus/templates/property.aserver.callback.hpp.mako
new file mode 100644
index 0000000..aa43b98
--- /dev/null
+++ b/tools/sdbusplus/templates/property.aserver.callback.hpp.mako
@@ -0,0 +1,62 @@
+<%
+p_name = property.snake_case
+p_tag = property.snake_case + "_t"
+p_type = property.cppTypeParam(interface.name)
+i_name = interface.classname
+%>\
+ static int _callback_get_${p_name}(
+ sd_bus*, const char*, const char*, const char*,
+ sd_bus_message* reply, void* context,
+ sd_bus_error* error [[maybe_unused]])
+ {
+ auto self = static_cast<${i_name}*>(context);
+
+ try
+ {
+ auto m = sdbusplus::message_t{reply};
+
+ // Set up the transaction.
+ server::transaction::set_id(m);
+
+ // Get property value and add to message.
+ if constexpr (server_details::has_get_property_msg<${p_tag},
+ Instance>)
+ {
+ auto v = self->${p_name}(m);
+ static_assert(std::is_convertible_v<decltype(v), ${p_type}>,
+ "Property doesn't convert to '${p_type}'.");
+ m.append<${p_type}>(std::move(v));
+ }
+ else
+ {
+ auto v = self->${p_name}();
+ static_assert(std::is_convertible_v<decltype(v), ${p_type}>,
+ "Property doesn't convert to '${p_type}'.");
+ m.append<${p_type}>(std::move(v));
+ }
+ }
+ % for e in property.errors:
+ catch (const ${interface.errorNamespacedClass(e)}& e)
+ {
+ return sd_bus_error_set(error, e.name(), e.description());
+ }
+ % endfor
+ catch (const std::exception&)
+ {
+ self->_context().get_bus().set_current_exception(
+ std::current_exception());
+ return -EINVAL;
+ }
+
+ return 1;
+ }
+
+% if 'const' not in property.flags and 'readonly' not in property.flags:
+ static int _callback_set_${p_name}(
+ sd_bus*, const char*, const char*, const char*,
+ sd_bus_message* value[[maybe_unused]], void* context [[maybe_unused]],
+ sd_bus_error* error [[maybe_unused]])
+ {
+ return -EINVAL;
+ }
+% endif
diff --git a/tools/sdbusplus/templates/property.aserver.get.hpp.mako b/tools/sdbusplus/templates/property.aserver.get.hpp.mako
new file mode 100644
index 0000000..00bb69f
--- /dev/null
+++ b/tools/sdbusplus/templates/property.aserver.get.hpp.mako
@@ -0,0 +1,23 @@
+<%
+p_name = property.snake_case;
+p_tag = property.snake_case + "_t"
+%>\
+ auto ${p_name}() const
+ requires server_details::has_get_property_nomsg<${p_tag}, Instance>
+ {
+ return static_cast<const Instance*>(this)->get_property(${p_tag}{});
+ }
+ auto ${p_name}(sdbusplus::message_t& m) const
+ requires server_details::has_get_property_msg<${p_tag}, Instance>
+ {
+ return static_cast<const Instance*>(this)->get_property(${p_tag}{}, m);
+ }
+ auto ${p_name}() const noexcept
+ requires (!server_details::has_get_property<${p_tag}, Instance>)
+ {
+ static_assert(
+ !server_details::has_get_property_missing_const<${p_tag},
+ Instance>,
+ "Missing const on get_property(${p_tag})?");
+ return _${p_name};
+ }
diff --git a/tools/sdbusplus/templates/property.aserver.tag.hpp.mako b/tools/sdbusplus/templates/property.aserver.tag.hpp.mako
new file mode 100644
index 0000000..6ab59db
--- /dev/null
+++ b/tools/sdbusplus/templates/property.aserver.tag.hpp.mako
@@ -0,0 +1,10 @@
+<%
+p_tag = property.snake_case + "_t"
+p_type = property.cppTypeParam(interface.name)
+%>\
+ struct ${p_tag}
+ {
+ using value_type = ${p_type};
+ ${p_tag}() = default;
+ explicit ${p_tag}(value_type) {}
+ };
diff --git a/tools/sdbusplus/templates/property.aserver.typeid.hpp.mako b/tools/sdbusplus/templates/property.aserver.typeid.hpp.mako
new file mode 100644
index 0000000..19663e9
--- /dev/null
+++ b/tools/sdbusplus/templates/property.aserver.typeid.hpp.mako
@@ -0,0 +1,3 @@
+ static constexpr auto _property_typeid_${property.snake_case} =
+ utility::tuple_to_array(message::types::type_id<\
+${property.cppTypeParam(interface.name)}>());
diff --git a/tools/sdbusplus/templates/property.aserver.value.hpp.mako b/tools/sdbusplus/templates/property.aserver.value.hpp.mako
new file mode 100644
index 0000000..452bab3
--- /dev/null
+++ b/tools/sdbusplus/templates/property.aserver.value.hpp.mako
@@ -0,0 +1,17 @@
+<%
+p_name = property.snake_case
+p_type = property.cppTypeParam(interface.name)
+
+def p_value():
+ if property.defaultValue == None:
+ return "{}"
+
+ value = str(property.defaultValue)
+ enum_prefix = ""
+ if property.is_enum():
+ enum_prefix = f"{p_type}::"
+
+ return f" = {enum_prefix}{value}"
+
+%>\
+ ${p_type} _${p_name}${p_value()};
diff --git a/tools/sdbusplus/templates/property.aserver.vtable.hpp.mako b/tools/sdbusplus/templates/property.aserver.vtable.hpp.mako
new file mode 100644
index 0000000..3618049
--- /dev/null
+++ b/tools/sdbusplus/templates/property.aserver.vtable.hpp.mako
@@ -0,0 +1,12 @@
+ vtable::property("${property.name}",
+ _property_typeid_${property.snake_case}.data(),
+ _callback_get_${property.snake_case},
+% if 'const' not in property.flags and 'readonly' not in property.flags:
+ _callback_set_${property.snake_case},
+% endif
+% if not property.cpp_flags:
+ vtable::property_::emits_change\
+% else:
+ ${property.cpp_flags}\
+% endif
+),