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
23 namespace pw::rpc {
24
25 class Service;
26
27 namespace internal {
28
29 class Packet;
30
31 // Each supported protobuf implementation provides a class that derives from
32 // Method. The implementation classes provide the following public interface:
33 /*
34 class MethodImpl : public Method {
35 // True if the provided function signature is valid for this method impl.
36 template <auto kMethod>
37 static constexpr bool matches();
38
39 // Creates a unary method instance.
40 template <auto kMethod>
41 static constexpr MethodImpl Unary(uint32_t id, [optional args]);
42
43 // Creates a server streaming method instance.
44 template <auto kMethod>
45 static constexpr MethodImpl ServerStreaming(uint32_t id, [optional args]);
46
47 // Creates a client streaming method instance.
48 static constexpr MethodImpl ClientStreaming(uint32_t id, [optional args]);
49
50 // Creates a bidirectional streaming method instance.
51 static constexpr MethodImpl BidirectionalStreaming(uint32_t id,
52 [optional args]);
53
54 // Creates a method instance for when the method implementation function has
55 // an incorrect signature. Having this helps reduce error message verbosity.
56 static constexpr MethodImpl Invalid();
57 };
58 */
59 // Method implementations must pass a test that uses the MethodImplTester class
60 // in pw_rpc/internal/method_impl_tester.h.
61 class Method {
62 public:
id()63 constexpr uint32_t id() const { return id_; }
64
65 // The pw::rpc::Server calls method.Invoke to call a user-defined RPC. Invoke
66 // calls the invoker function, which handles the RPC request and response
67 // according to the RPC type and protobuf implementation and calls the
68 // user-defined RPC function.
69 //
70 // The rpc_lock() must be held through creating the call object and released
71 // before calling into the RPC body.
Invoke(const CallContext & context,const Packet & request)72 void Invoke(const CallContext& context, const Packet& request) const
73 PW_UNLOCK_FUNCTION(rpc_lock()) PW_NO_LOCK_SAFETY_ANALYSIS {
74 return invoker_(context, request); // The invoker must unlock rpc_lock().
75 }
76
77 protected:
78 using Invoker = void (&)(const CallContext&, const Packet&);
79
InvalidInvoker(const CallContext &,const Packet &)80 static constexpr void InvalidInvoker(const CallContext&, const Packet&) {}
81
Method(uint32_t id,Invoker invoker)82 constexpr Method(uint32_t id, Invoker invoker) : id_(id), invoker_(invoker) {}
83
84 private:
85 uint32_t id_;
86 Invoker invoker_;
87 };
88
89 // MethodTraits inspects an RPC implementation function. It determines which
90 // Method API is in use and the type of the RPC based on the function signature.
91 // pw_rpc Method implementations specialize MethodTraits for each RPC type.
92 template <typename Method>
93 struct MethodTraits {
94 // Specializations must set Implementation as an alias for their method
95 // implementation class.
96 using Implementation = Method;
97
98 // Specializations must set kType to the MethodType.
99 // static constexpr MethodType kType = (method type);
100
101 // Specializations for member function types must set Service to an alias to
102 // for the implemented service class.
103 // using Service = (derived service class);
104
105 // Specializations may provide the C++ types of the requests and responses if
106 // relevant.
107 using Request = void;
108 using Response = void;
109 };
110
111 template <auto kMethod>
112 using MethodImplementation =
113 typename MethodTraits<decltype(kMethod)>::Implementation;
114
115 template <auto kMethod>
116 using Request = typename MethodTraits<decltype(kMethod)>::Request;
117
118 template <auto kMethod>
119 using Response = typename MethodTraits<decltype(kMethod)>::Response;
120
121 // Function that calls a user-defined RPC function on the given Service.
122 template <auto kMethod, typename... Args>
CallMethodImplFunction(Service & service,Args &&...args)123 constexpr auto CallMethodImplFunction(Service& service, Args&&... args) {
124 // If the method impl is a member function, deduce the type of the
125 // user-defined service from it, then call the method on the service.
126 if constexpr (std::is_member_function_pointer_v<decltype(kMethod)>) {
127 return (static_cast<typename MethodTraits<decltype(kMethod)>::Service&>(
128 service).*
129 kMethod)(std::forward<Args>(args)...);
130 } else {
131 return kMethod(std::forward<Args>(args)...);
132 }
133 }
134
135 } // namespace internal
136 } // namespace pw::rpc
137