• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <type_traits>
17 #include <utility>
18 
19 #include "pw_rpc/internal/method.h"
20 #include "pw_rpc/internal/method_type.h"
21 
22 namespace pw::rpc::internal {
23 
24 // Base class for different combinations of possible service methods. Derived
25 // classes should contain a union of different method types, one of which is a
26 // base Method.
27 class MethodUnion {
28  public:
29   constexpr const Method& method() const;
30 };
31 
32 class CoreMethodUnion : public MethodUnion {
33  public:
method()34   constexpr const Method& method() const { return impl_.method; }
35 
36  private:
37   // All derived MethodUnions must contain a union of different method
38   // implementations as their only member.
39   union {
40     Method method;
41   } impl_;
42 };
43 
method()44 constexpr const Method& MethodUnion::method() const {
45   // This is an ugly hack. As all MethodUnion classes contain a union of Method
46   // derivatives, CoreMethodUnion is used to extract a generic Method from the
47   // specific implementation.
48   return static_cast<const CoreMethodUnion*>(this)->method();
49 }
50 
51 // Templated false value for use in static_assert(false) statements.
52 template <typename...>
53 constexpr std::false_type kCheckMethodSignature{};
54 
55 // In static_assert messages, use newlines in GCC since it displays them
56 // correctly. Clang displays \n, which is not helpful.
57 #ifdef __clang__
58 #define _PW_RPC_FORMAT_ERROR_MESSAGE(msg, signature) msg " " signature
59 #else
60 #define _PW_RPC_FORMAT_ERROR_MESSAGE(msg, signature) \
61   "\n" msg "\n\n    " signature "\n"
62 #endif  // __clang__
63 
64 #define _PW_RPC_FUNCTION_ERROR(type, return_type, args)                   \
65   _PW_RPC_FORMAT_ERROR_MESSAGE(                                           \
66       "This RPC is a " type                                               \
67       " RPC, but its function signature is not correct. The function "    \
68       "signature is determined by the protobuf library in use, but " type \
69       " RPC implementations generally take the form:",                    \
70       return_type " MethodName(ServerContext&, " args ")")
71 
72 // This function is called if an RPC method implementation's signature is not
73 // correct. It triggers a static_assert with an error message tailored to the
74 // expected RPC type.
75 template <auto method,
76           MethodType expected,
77           typename InvalidImpl = MethodImplementation<method>>
InvalidMethod(uint32_t)78 constexpr auto InvalidMethod(uint32_t) {
79   if constexpr (expected == MethodType::kUnary) {
80     static_assert(
81         kCheckMethodSignature<decltype(method)>,
82         _PW_RPC_FUNCTION_ERROR("unary", "Status", "Request, Response"));
83   } else if constexpr (expected == MethodType::kServerStreaming) {
84     static_assert(
85         kCheckMethodSignature<decltype(method)>,
86         _PW_RPC_FUNCTION_ERROR(
87             "server streaming", "void", "Request, ServerWriter<Response>&"));
88   } else if constexpr (expected == MethodType::kClientStreaming) {
89     static_assert(
90         kCheckMethodSignature<decltype(method)>,
91         _PW_RPC_FUNCTION_ERROR(
92             "client streaming", "Status", "ServerReader<Request>&, Response"));
93   } else if constexpr (expected == MethodType::kBidirectionalStreaming) {
94     static_assert(kCheckMethodSignature<decltype(method)>,
95                   _PW_RPC_FUNCTION_ERROR(
96                       "bidirectional streaming",
97                       "void",
98                       "ServerReader<Request>&, ServerWriter<Response>&"));
99   } else {
100     static_assert(kCheckMethodSignature<decltype(method)>,
101                   "Unsupported MethodType");
102   }
103   return InvalidImpl::Invalid();
104 }
105 
106 #undef _PW_RPC_FORMAT_ERROR_MESSAGE
107 #undef _PW_RPC_FUNCTION_ERROR
108 
109 // This function checks the type of the method and calls the appropriate
110 // function to create the method instance.
111 template <auto method, typename MethodImpl, MethodType type, typename... Args>
GetMethodFor(uint32_t id,Args &&...args)112 constexpr auto GetMethodFor(uint32_t id, Args&&... args) {
113   if constexpr (MethodTraits<decltype(method)>::kType != type) {
114     return InvalidMethod<method, type>(id);
115   } else if constexpr (type == MethodType::kUnary) {
116     return MethodImpl::template Unary<method>(id, std::forward<Args>(args)...);
117   } else if constexpr (type == MethodType::kServerStreaming) {
118     return MethodImpl::template ServerStreaming<method>(
119         id, std::forward<Args>(args)...);
120   } else if constexpr (type == MethodType::kClientStreaming) {
121     return MethodImpl::template ClientStreaming<method>(
122         id, std::forward<Args>(args)...);
123   } else if constexpr (type == MethodType::kBidirectionalStreaming) {
124     return MethodImpl::template BidirectionalStreaming<method>(
125         id, std::forward<Args>(args)...);
126   } else {
127     static_assert(kCheckMethodSignature<MethodImpl>, "Invalid MethodType");
128   }
129 }
130 
131 }  // namespace pw::rpc::internal
132