xref: /aosp_15_r20/external/pigweed/pw_rpc/public/pw_rpc/internal/method.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <cstddef>
17 #include <cstdint>
18 #include <utility>
19 
20 #include "pw_rpc/internal/call_context.h"
21 #include "pw_rpc/internal/lock.h"
22 #include "pw_rpc/method_type.h"
23 
24 namespace pw::rpc {
25 
26 class Service;
27 
28 namespace internal {
29 
30 class Packet;
31 
32 // Each supported protobuf implementation provides a class that derives from
33 // Method. The implementation classes provide the following public interface:
34 /*
35 class MethodImpl : public Method {
36   // True if the provided function signature is valid for this method impl.
37   template <auto kMethod>
38   static constexpr bool matches();
39 
40   // Creates a unary method instance.
41   template <auto kMethod>
42   static constexpr MethodImpl Unary(uint32_t id, [optional args]);
43 
44   // Creates a server streaming method instance.
45   template <auto kMethod>
46   static constexpr MethodImpl ServerStreaming(uint32_t id, [optional args]);
47 
48   // Creates a client streaming method instance.
49   static constexpr MethodImpl ClientStreaming(uint32_t id, [optional args]);
50 
51   // Creates a bidirectional streaming method instance.
52   static constexpr MethodImpl BidirectionalStreaming(uint32_t id,
53                                                      [optional args]);
54 
55   // Creates a method instance for when the method implementation function has
56   // an incorrect signature. Having this helps reduce error message verbosity.
57   static constexpr MethodImpl Invalid();
58 };
59 */
60 // Method implementations must pass a test that uses the MethodImplTester class
61 // in pw_rpc/internal/method_impl_tester.h.
62 class Method {
63  public:
id()64   constexpr uint32_t id() const { return id_; }
65 
66   template <typename UnusedType = void>
type()67   constexpr MethodType type() const {
68     static_assert(cfg::kMethodStoresType<UnusedType>,
69                   "The MethodType accessor is disabled. To enable set "
70                   "PW_RPC_METHOD_STORES_TYPE to 1.");
71 #if PW_RPC_METHOD_STORES_TYPE
72     return type_;
73 #else
74     return MethodType::kUnary;
75 #endif
76   }
77 
78   // The pw::rpc::Server calls method.Invoke to call a user-defined RPC. Invoke
79   // calls the invoker function, which handles the RPC request and response
80   // according to the RPC type and protobuf implementation and calls the
81   // user-defined RPC function.
82   //
83   // The rpc_lock() must be held through creating the call object and released
84   // before calling into the RPC body.
Invoke(const CallContext & context,const Packet & request)85   void Invoke(const CallContext& context, const Packet& request) const
86       PW_UNLOCK_FUNCTION(rpc_lock()) PW_NO_LOCK_SAFETY_ANALYSIS {
87     return invoker_(context, request);  // The invoker must unlock rpc_lock().
88   }
89 
90  protected:
91   using Invoker = void (&)(const CallContext&, const Packet&);
92 
InvalidInvoker(const CallContext &,const Packet &)93   static constexpr void InvalidInvoker(const CallContext&, const Packet&) {}
94 
Method(uint32_t id,Invoker invoker,MethodType type)95   constexpr Method(uint32_t id,
96                    Invoker invoker,
97                    [[maybe_unused]] MethodType type)
98       : id_(id),
99         invoker_(invoker)
100 #if PW_RPC_METHOD_STORES_TYPE
101         ,
102         type_(type)
103 #endif
104   {
105   }
106 
107  private:
108   uint32_t id_;
109   Invoker invoker_;
110 #if PW_RPC_METHOD_STORES_TYPE
111   MethodType type_;
112 #endif
113 };
114 
115 // MethodTraits inspects an RPC implementation function. It determines which
116 // Method API is in use and the type of the RPC based on the function signature.
117 // pw_rpc Method implementations specialize MethodTraits for each RPC type.
118 template <typename Method>
119 struct MethodTraits {
120   // Specializations must set Implementation as an alias for their method
121   // implementation class.
122   using Implementation = Method;
123 
124   // Specializations must set kType to the MethodType.
125   // static constexpr MethodType kType = (method type);
126 
127   // Specializations for member function types must set Service to an alias to
128   // for the implemented service class.
129   // using Service = (derived service class);
130 
131   // Specializations may provide the C++ types of the requests and responses if
132   // relevant.
133   using Request = void;
134   using Response = void;
135 };
136 
137 template <auto kMethod>
138 using MethodImplementation =
139     typename MethodTraits<decltype(kMethod)>::Implementation;
140 
141 template <auto kMethod>
142 using Request = typename MethodTraits<decltype(kMethod)>::Request;
143 
144 template <auto kMethod>
145 using Response = typename MethodTraits<decltype(kMethod)>::Response;
146 
147 // Function that calls a user-defined RPC function on the given Service.
148 template <auto kMethod, typename... Args>
CallMethodImplFunction(Service & service,Args &&...args)149 constexpr auto CallMethodImplFunction(Service& service, Args&&... args) {
150   // If the method impl is a member function, deduce the type of the
151   // user-defined service from it, then call the method on the service.
152   if constexpr (std::is_member_function_pointer_v<decltype(kMethod)>) {
153     return (static_cast<typename MethodTraits<decltype(kMethod)>::Service&>(
154                 service).*
155             kMethod)(std::forward<Args>(args)...);
156   } else {
157     return kMethod(std::forward<Args>(args)...);
158   }
159 }
160 
161 }  // namespace internal
162 }  // namespace pw::rpc
163