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