async: client: use CRTP to eliminate excess context refs
The types used to create a client proxy object need to have access
to the async::context, but the sdbusplus::async::client_t that holds
them already has a reference. Use CRTP so that there is only a single
held reference per client, no matter how many types it contains.
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I9b3356a6d28f09f10dea74ca310812c7cb9acfa0
diff --git a/include/sdbusplus/async/client.hpp b/include/sdbusplus/async/client.hpp
index ab2e9e9..186ecdc 100644
--- a/include/sdbusplus/async/client.hpp
+++ b/include/sdbusplus/async/client.hpp
@@ -8,6 +8,11 @@
namespace client
{
+namespace details
+{
+struct client_context_friend;
+}
+
/** An aggregation class of sdbusplus::async::proxy-based client types.
*
* The resulting class acts as a union of all Types from the template
@@ -16,13 +21,20 @@
* Like a `proxy`, the class only becomes functional once the service and
* path are populated.
*/
-template <bool S, bool P, bool Preserved, template <typename> typename... Types>
+template <bool S, bool P, bool Preserved,
+ template <typename, typename> typename... Types>
class client :
public context_ref,
- public Types<sdbusplus::async::proxy_ns::proxy<S, P, false, Preserved>>...
+ public Types<client<S, P, Preserved, Types...>,
+ sdbusplus::async::proxy_ns::proxy<S, P, false, Preserved>>...
{
+ public:
+ using Self = client<S, P, Preserved, Types...>;
+ using Proxy = sdbusplus::async::proxy_ns::proxy<S, P, false, Preserved>;
+ friend details::client_context_friend;
+
private:
- sdbusplus::async::proxy_ns::proxy<S, P, false, Preserved> proxy{};
+ Proxy proxy{};
public:
constexpr client() = delete;
@@ -33,14 +45,13 @@
/* Default (empty) constructor only when Service and Path are missing. */
explicit client(sdbusplus::async::context& ctx)
requires(!S && !P)
- : context_ref(ctx), Types<decltype(proxy)>(ctx, decltype(proxy){})...
+ : context_ref(ctx), Types<Self, Proxy>(Proxy{})...
{}
/* Conversion constructor for a non-empty (Service and/or Path) proxy. */
- explicit client(sdbusplus::async::context& ctx,
- sdbusplus::async::proxy_ns::proxy<S, P, false, Preserved> p)
+ explicit client(sdbusplus::async::context& ctx, Proxy p)
requires(S || P)
- : context_ref(ctx), Types<decltype(proxy)>(ctx, p)..., proxy(p)
+ : context_ref(ctx), Types<Self, Proxy>(p)..., proxy(p)
{}
/* Convert a non-Service instance to a Service instance. */
@@ -65,14 +76,31 @@
* This holds Service/Path in string-views, which must exist longer than
* the lifetime of this client_t.
*/
-template <template <typename> typename... Types>
+template <template <typename, typename> typename... Types>
using client_t = client::client<false, false, false, Types...>;
/** A Preserved client alias.
*
* This holds Service/Path in strings, which thus have lifetimes that are
* the same as the client itself.
*/
-template <template <typename> typename... Types>
+template <template <typename, typename> typename... Types>
using client_preserved_t = client::client<false, false, false, Types...>;
+namespace client::details
+{
+/* Indirect so that the generated Types can access the client_t's context.
+ *
+ * If P2893 gets into C++26 we could eliminate this because we can set all
+ * the Types as friends directly.
+ */
+struct client_context_friend
+{
+ template <typename T>
+ sdbusplus::async::context& context()
+ {
+ return static_cast<T*>(this)->ctx;
+ }
+};
+} // namespace client::details
+
} // namespace sdbusplus::async
diff --git a/tools/sdbusplus/templates/interface.client.hpp.mako b/tools/sdbusplus/templates/interface.client.hpp.mako
index 8c111bc..e5ef488 100644
--- a/tools/sdbusplus/templates/interface.client.hpp.mako
+++ b/tools/sdbusplus/templates/interface.client.hpp.mako
@@ -23,7 +23,7 @@
namespace details
{
// forward declaration
-template <typename Proxy>
+template <typename Client, typename Proxy>
class ${interface.classname};
} // namespace details
@@ -33,17 +33,17 @@
* sdbusplus::async::client_t<${interface.classname}>() or
* ${interface.classname}() both construct an equivalent instance.
*/
-template <typename Proxy = void>
+template <typename Client = void, typename Proxy = void>
struct ${interface.classname} :
- public std::conditional_t<std::is_void_v<Proxy>,
+ public std::conditional_t<std::is_void_v<Client>,
sdbusplus::async::client_t<details::${interface.classname}>,
- details::${interface.classname}<Proxy>>
+ details::${interface.classname}<Client, Proxy>>
{
template <typename... Args>
${interface.classname}(Args&&... args) :
- std::conditional_t<std::is_void_v<Proxy>,
+ std::conditional_t<std::is_void_v<Client>,
sdbusplus::async::client_t<details::${interface.classname}>,
- details::${interface.classname}<Proxy>>(
+ details::${interface.classname}<Client, Proxy>>(
std::forward<Args>(args)...)
{}
};
@@ -51,12 +51,13 @@
namespace details
{
-template <typename Proxy>
-class ${interface.classname} : public sdbusplus::common::${interface.cppNamespacedClass()}
+template <typename Client, typename Proxy>
+class ${interface.classname} :
+ public sdbusplus::common::${interface.cppNamespacedClass()},
+ public sdbusplus::async::client::details::client_context_friend
{
public:
- template <bool, bool, bool, template <typename> typename...>
- friend class sdbusplus::async::client::client;
+ friend Client;
template <typename>
friend struct sdbusplus::client::${interface.cppNamespacedClass()};
@@ -72,11 +73,16 @@
% endfor
private:
// Conversion constructor from proxy used by client_t.
- constexpr ${interface.classname}(sdbusplus::async::context& ctx, Proxy p) :
- ctx(ctx), proxy(p.interface(interface))
+ explicit constexpr ${interface.classname}(Proxy p) :
+ proxy(p.interface(interface))
{}
- sdbusplus::async::context& ctx;
+ sdbusplus::async::context& context()
+ {
+ return sdbusplus::async::client::details::client_context_friend::
+ context<Client>();
+ }
+
decltype(std::declval<Proxy>().interface(interface)) proxy = {};
};
diff --git a/tools/sdbusplus/templates/method.client.hpp.mako b/tools/sdbusplus/templates/method.client.hpp.mako
index 16bbfce..8001370 100644
--- a/tools/sdbusplus/templates/method.client.hpp.mako
+++ b/tools/sdbusplus/templates/method.client.hpp.mako
@@ -22,7 +22,7 @@
)
{
return proxy.template call<\
-${method.returns_as_list(interface)}>(ctx, "${method.name}"\
+${method.returns_as_list(interface)}>(context(), "${method.name}"\
% if len(method.parameters) != 0:
, ${method.parameters_as_list()}\
% else:
diff --git a/tools/sdbusplus/templates/property.client.hpp.mako b/tools/sdbusplus/templates/property.client.hpp.mako
index 46be6ea..c03ee3f 100644
--- a/tools/sdbusplus/templates/property.client.hpp.mako
+++ b/tools/sdbusplus/templates/property.client.hpp.mako
@@ -4,7 +4,7 @@
auto ${property.camelCase}()
{
return proxy.template get_property<\
-${property.cppTypeParam(interface.name)}>(ctx, "${property.name}");
+${property.cppTypeParam(interface.name)}>(context(), "${property.name}");
}
% if 'const' not in property.flags and 'readonly' not in property.flags:
@@ -15,6 +15,6 @@
{
return proxy.template set_property<\
${property.cppTypeParam(interface.name)}>(
- ctx, "${property.name}", std::forward<decltype(value)>(value));
+ context(), "${property.name}", std::forward<decltype(value)>(value));
}
% endif