sdbus++: generate `properties` method for client harness
This uses the existing proxy `get_all_properties` method and allows for
more efficient fetching of all properties in one shot. A struct is
generated for each type, and each field is initialized with its default
value (but is not guaranteed to be deserialized).
As errors are less detectable (e.g. perhaps an expected property
did not come through?), I considered whether the values in the struct
should all be `std::optional`. However, given how other methods are
used, it feels like this would result in a crash and assertion
failure rather than saving anyone time and effort. The wrong type
is detected a suitable exception is thrown in those cases.
Change-Id: Iff7712bd17db39f1ee5699f867e9f17a54eea21b
Signed-off-by: Adin Scannell <adin@scannell.ca>
diff --git a/example/calculator-client.cpp b/example/calculator-client.cpp
index 0c9b3ad..5e3a40e 100644
--- a/example/calculator-client.cpp
+++ b/example/calculator-client.cpp
@@ -59,6 +59,13 @@
std::cout << "Should be 'client': " << _ << std::endl;
}
+ {
+ // Grab all the properties and print them.
+ auto _ = co_await c.properties();
+ std::cout << "Should be 1234: " << _.last_result << std::endl;
+ std::cout << "Should be 'client': " << _.owner << std::endl;
+ }
+
co_return;
}
diff --git a/tools/meson.build b/tools/meson.build
index 94a9f85..4c33b32 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -43,7 +43,6 @@
'sdbusplus/templates/property.aserver.set.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',
diff --git a/tools/sdbusplus/property.py b/tools/sdbusplus/property.py
index 4d79cb6..a055fe5 100644
--- a/tools/sdbusplus/property.py
+++ b/tools/sdbusplus/property.py
@@ -86,11 +86,15 @@
def cppTypeParam(self, interface, full=False, typename="common"):
return self.__cppTypeParam(interface, self.cppTypeName, full, typename)
- def default_value(self):
- if self.defaultValue is not None:
- return " = " + str(self.defaultValue)
- else:
+ def default_value(self, interface):
+ if self.defaultValue is None:
return ""
+ value = str(self.defaultValue)
+ enum_prefix = ""
+ if self.is_enum():
+ p_type = self.cppTypeParam(interface)
+ enum_prefix = f"{p_type}::"
+ return f" = {enum_prefix}{value}"
def __cppTypeParam(
self, interface, cppTypeName, full=False, typename="common"
diff --git a/tools/sdbusplus/templates/interface.aserver.hpp.mako b/tools/sdbusplus/templates/interface.aserver.hpp.mako
index 7592c82..75df5ad 100644
--- a/tools/sdbusplus/templates/interface.aserver.hpp.mako
+++ b/tools/sdbusplus/templates/interface.aserver.hpp.mako
@@ -87,7 +87,7 @@
protected:
% for p in interface.properties:
-${p.render(loader, "property.aserver.value.hpp.mako", property=p, interface=interface)}\
+ ${p.cppTypeParam(interface.name)} ${p.snake_case}_${p.default_value(interface.name)};
% endfor
private:
diff --git a/tools/sdbusplus/templates/interface.client.hpp.mako b/tools/sdbusplus/templates/interface.client.hpp.mako
index 4ed3375..cbc89d2 100644
--- a/tools/sdbusplus/templates/interface.client.hpp.mako
+++ b/tools/sdbusplus/templates/interface.client.hpp.mako
@@ -1,6 +1,8 @@
#pragma once
#include <sdbusplus/async/client.hpp>
+#include <sdbusplus/async/execution.hpp>
#include <type_traits>
+#include <variant>
% for h in interface.cpp_includes():
#include <${h}>
@@ -47,6 +49,13 @@
private sdbusplus::async::client::details::client_context_friend
{
public:
+ struct properties_t
+ {
+ % for p in interface.properties:
+ ${p.cppTypeParam(interface.name)} ${p.snake_case}${p.default_value(interface.name)};
+ % endfor
+ };
+
friend Client;
template <typename, typename>
friend struct sdbusplus::client::${interface.cppNamespacedClass()};
@@ -61,6 +70,41 @@
% for p in interface.properties:
${p.render(loader, "property.client.hpp.mako", property=p, interface=interface)}
% endfor
+
+ auto properties()
+ {
+ return proxy.template get_all_properties<PropertiesVariant>(context()) |
+ sdbusplus::async::execution::then([](auto&& v) {
+ properties_t result;
+ for (const auto& [property, value] : v)
+ {
+ std::visit(
+ [&](auto v) {
+ % for p in interface.properties:
+ if (property == "${p.name}")
+ {
+ if constexpr (std::is_same_v<
+ std::decay_t<decltype(v)>,
+ ${p.cppTypeParam(interface.name)}>)
+ {
+ result.${p.snake_case} = v;
+ return;
+ }
+ else
+ {
+ throw exception::UnpackPropertyError(
+ property,
+ UnpackErrorReason::wrongType);
+ }
+ }
+ % endfor
+ },
+ value);
+ }
+ return result;
+ });
+ }
+
private:
// Conversion constructor from proxy used by client_t.
explicit constexpr ${interface.classname}(Proxy p) :
diff --git a/tools/sdbusplus/templates/interface.common.hpp.mako b/tools/sdbusplus/templates/interface.common.hpp.mako
index 50893ca..a26f404 100644
--- a/tools/sdbusplus/templates/interface.common.hpp.mako
+++ b/tools/sdbusplus/templates/interface.common.hpp.mako
@@ -34,6 +34,8 @@
% if interface.properties:
using PropertiesVariant = sdbusplus::utility::dedup_variant_t<
${",\n ".join(sorted(setOfPropertyTypes()))}>;
+ % else:
+ using PropertiesVariant = std::variant<std::monostate>;
% endif \
% for p in interface.paths:
diff --git a/tools/sdbusplus/templates/property.aserver.value.hpp.mako b/tools/sdbusplus/templates/property.aserver.value.hpp.mako
deleted file mode 100644
index c7794e8..0000000
--- a/tools/sdbusplus/templates/property.aserver.value.hpp.mako
+++ /dev/null
@@ -1,17 +0,0 @@
-<%
-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/signal.aserver.emit.hpp.mako b/tools/sdbusplus/templates/signal.aserver.emit.hpp.mako
index 797c428..06bb19f 100644
--- a/tools/sdbusplus/templates/signal.aserver.emit.hpp.mako
+++ b/tools/sdbusplus/templates/signal.aserver.emit.hpp.mako
@@ -5,17 +5,12 @@
def parameter(p):
r = "%s %s%s" % \
- (p.cppTypeParam(interface.name), p.camelCase, default_value(p))
+ (p.cppTypeParam(interface.name), p.camelCase, p.default_value(interface.name))
return r
def parameters_as_list():
return ", ".join([ p.camelCase for p in signal.properties ])
- def default_value(p):
- if p.defaultValue != None:
- return " = " + str(p.defaultValue)
- else:
- return ""
%>\
/** @brief Send signal '${signal.name}'
*
diff --git a/tools/sdbusplus/templates/signal.prototype.hpp.mako b/tools/sdbusplus/templates/signal.prototype.hpp.mako
index 2cae5f2..4a14c92 100644
--- a/tools/sdbusplus/templates/signal.prototype.hpp.mako
+++ b/tools/sdbusplus/templates/signal.prototype.hpp.mako
@@ -6,7 +6,7 @@
def parameter(p, defaultValue=False):
r = "%s %s" % (p.cppTypeParam(interface.name), p.camelCase)
if defaultValue:
- r += default_value(p)
+ r += p.default_value(interface.name)
return r
def parameters_as_list():
@@ -15,12 +15,6 @@
def parameters_types_as_list():
return ", ".join([ p.cppTypeParam(interface.name, full=True)
for p in signal.properties ])
-
- def default_value(p):
- if p.defaultValue != None:
- return " = " + str(p.defaultValue)
- else:
- return ""
%>
###
### Emit 'header'