1 #pragma once
2 
3 #include <sdbusplus/async/proxy.hpp>
4 
5 namespace sdbusplus::async
6 {
7 
8 namespace client
9 {
10 
11 namespace details
12 {
13 struct client_context_friend;
14 }
15 
16 /** An aggregation class of sdbusplus::async::proxy-based client types.
17  *
18  *  The resulting class acts as a union of all Types from the template
19  *  arguments.
20  *
21  *  Like a `proxy`, the class only becomes functional once the service and
22  *  path are populated.
23  */
24 template <bool S, bool P, bool Preserved,
25           template <typename, typename> typename... Types>
26 class client :
27     public context_ref,
28     public Types<client<S, P, Preserved, Types...>,
29                  sdbusplus::async::proxy_ns::proxy<S, P, false, Preserved>>...
30 {
31   public:
32     using Self = client<S, P, Preserved, Types...>;
33     using Proxy = sdbusplus::async::proxy_ns::proxy<S, P, false, Preserved>;
34     friend details::client_context_friend;
35 
36   private:
37     Proxy proxy{};
38 
39   public:
40     constexpr client() = delete;
41     /* Delete default constructor if Service or Path have been provided. */
42     explicit client(sdbusplus::async::context& ctx)
43         requires(S || P)
44     = delete;
45     /* Default (empty) constructor only when Service and Path are missing. */
46     explicit client(sdbusplus::async::context& ctx)
47         requires(!S && !P)
48         : context_ref(ctx), Types<Self, Proxy>(Proxy{})...
49     {}
50 
51     /* Conversion constructor for a non-empty (Service and/or Path) proxy. */
52     explicit client(sdbusplus::async::context& ctx, Proxy p)
53         requires(S || P)
54         : context_ref(ctx), Types<Self, Proxy>(p)..., proxy(p)
55     {}
56 
57     /* Convert a non-Service instance to a Service instance. */
58     auto service(auto& s) const noexcept
59         requires(!S)
60     {
61         return client<true, P, Preserved, Types...>(ctx, proxy.service(s));
62     }
63 
64     /* Convert a non-Path instance to a Path instance. */
65     auto path(auto& p) const noexcept
66         requires(!P)
67     {
68         return client<S, true, Preserved, Types...>(ctx, proxy.path(p));
69     }
70 
71     /* Convert client into a Preserved Proxy. */
72     auto preserve() const noexcept
73         requires(!Preserved)
74     {
75         return client<S, P, true, Types...>(ctx, proxy.preserve());
76     }
77 };
78 
79 } // namespace client
80 
81 /** A non-Preserved client alias.
82  *
83  *  This holds Service/Path in string-views, which must exist longer than
84  *  the lifetime of this client_t.
85  */
86 template <template <typename, typename> typename... Types>
87 using client_t = client::client<false, false, false, Types...>;
88 /** A Preserved client alias.
89  *
90  *  This holds Service/Path in strings, which thus have lifetimes that are
91  *  the same as the client itself.
92  */
93 template <template <typename, typename> typename... Types>
94 using client_preserved_t = client::client<false, false, true, Types...>;
95 
96 namespace client::details
97 {
98 /* Indirect so that the generated Types can access the client_t's context.
99  *
100  * If P2893 gets into C++26 we could eliminate this because we can set all
101  * the Types as friends directly.
102  */
103 struct client_context_friend
104 {
105     template <typename Client, typename Self>
106     static sdbusplus::async::context& context(Self* self)
107     {
108         return static_cast<Client*>(self)->ctx;
109     }
110 };
111 } // namespace client::details
112 
113 } // namespace sdbusplus::async
114