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