Viam C++ SDK current
Loading...
Searching...
No Matches
client_helper.hpp
1#pragma once
2
4#include <viam/sdk/common/grpc_fwd.hpp>
5#include <viam/sdk/common/private/utils.hpp>
6#include <viam/sdk/common/proto_value.hpp>
7#include <viam/sdk/rpc/dial.hpp>
8
9namespace viam {
10namespace sdk {
11
12namespace client_helper_details {
13
14[[noreturn]] void errorHandlerReturnedUnexpectedly(const ::grpc::Status*) noexcept;
15
16// Helper function to test equality of status with grpc::StatusCode::CANCELLED.
17bool isStatusCancelled(int status) noexcept;
18
19// Set the mutable name of a request to the client name.
20// This function only participates in overload resolution if the request has a mutable_name method.
21template <typename RequestType,
22 typename ClientType,
23 typename = decltype(&RequestType::mutable_name)>
24void set_name(RequestType* req, const ClientType* client) {
25 *req->mutable_name() = client->name();
26}
27
28// No-op version of set_name above. This overload is only selected if the request type does not have
29// a mutable_name field.
30void set_name(...);
31
32} // namespace client_helper_details
33
34// the authority on a grpc::ClientContext is sometimes set to an invalid uri on mac, causing
35// `rust-utils` to fail to process gRPC requests. This class provides a convenience wrapper around a
36// grpc ClientContext that allows us to make any necessary modifications to authority or else where
37// to avoid runtime issues.
38// For more details, see https://viam.atlassian.net/browse/RSDK-5194.
40 public:
42 ClientContext(const ViamChannel& channel);
43
45
46 void try_cancel();
47
48 operator GrpcClientContext*();
49 operator const GrpcClientContext*() const;
50
51 void set_debug_key(const std::string& debug_key);
52
53 private:
54 void set_client_ctx_authority_();
55 void add_viam_client_version_();
56 std::unique_ptr<GrpcClientContext> wrapped_context_;
57};
58
59// Method type for a gRPC call that returns a response message type.
60template <typename StubType, typename RequestType, typename ResponseType>
61using SyncMethodType = ::grpc::Status (StubType::*)(GrpcClientContext*,
62 const RequestType&,
63 ResponseType*);
64
65// Method type for a gRPC call that returns a stream of response message type.
66template <typename StubType, typename RequestType, typename ResponseType>
67using StreamingMethodType = std::unique_ptr<GrpcClientReaderInterface<ResponseType>> (StubType::*)(
68 GrpcClientContext*, const RequestType&);
69
70template <typename ClientType,
71 typename StubType,
72 typename RequestType,
73 typename ResponseType,
74 typename MethodType>
76 static void default_rsc_(RequestType&) {}
77 static void default_rhc_(const ResponseType&) {}
78 static void default_ehc_(const ::grpc::Status* status) {
79 throw GRPCException(status);
80 }
81
82 public:
83 explicit ClientHelper(ClientType* client, StubType* stub, MethodType pfn)
84 : client_(client), stub_(stub), pfn_(pfn) {}
85
86 ClientHelper& with(const ProtoStruct& extra) {
87 return with(extra, default_rsc_);
88 }
89
90 template <typename RequestSetupCallable>
91 ClientHelper& with(RequestSetupCallable&& rsc) {
92 std::forward<RequestSetupCallable>(rsc)(request_);
93 return *this;
94 }
95
96 template <typename RequestSetupCallable>
97 ClientHelper& with(const ProtoStruct& extra, RequestSetupCallable&& rsc) {
98 auto key = extra.find(impl::debug_map_key);
99 if (key != extra.end()) {
100 ProtoValue value = key->second;
101 debug_key_ = *value.get<std::string>();
102 }
103
104 proto_convert_details::to_proto_impl<ProtoStruct>{}(extra, request_.mutable_extra());
105 return with(std::forward<RequestSetupCallable>(rsc));
106 }
107
108 template <typename ResponseHandlerCallable = decltype(default_rhc_)>
109 auto invoke(ResponseHandlerCallable&& rhc = default_rhc_) {
110 return invoke(std::forward<ResponseHandlerCallable>(rhc), default_ehc_);
111 }
112
113 template <typename ResponseHandlerCallable, typename ErrorHandlerCallable>
114 auto invoke(ResponseHandlerCallable&& rhc, ErrorHandlerCallable&& ehc) {
115 client_helper_details::set_name(&request_, client_);
116 ClientContext ctx(client_->channel());
117
118 if (debug_key_ != "") {
119 ctx.set_debug_key(debug_key_);
120 }
121 const auto result = (stub_->*pfn_)(ctx, request_, &response_);
122 if (result.ok()) {
123 return std::forward<ResponseHandlerCallable>(rhc)(
124 const_cast<const ResponseType&>(response_));
125 }
126
127 std::forward<ErrorHandlerCallable>(ehc)(&result);
128 client_helper_details::errorHandlerReturnedUnexpectedly(&result);
129 }
130
131 // A version of invoke for gRPC calls returning `(stream ResponseType)`.
132 // ResponseHandlerCallable will be called for every response in the reader, and should return
133 // false to indicate it is no longer interested in the stream.
134 template <typename ResponseHandlerCallable,
135 typename ErrorHandlerCallable = decltype(default_ehc_)>
136 auto invoke_stream(ResponseHandlerCallable rhc, ErrorHandlerCallable&& ehc = default_ehc_) {
137 *request_.mutable_name() = client_->name();
138 ClientContext ctx(client_->channel());
139
140 auto reader = (stub_->*pfn_)(ctx, request_);
141
142 bool cancelled_by_handler = false;
143
144 while (reader->Read(&response_)) {
145 if (!rhc(response_)) {
146 cancelled_by_handler = true;
147 ctx.try_cancel();
148 break;
149 }
150 }
151
152 const auto result = reader->Finish();
153
154 if (result.ok() || (cancelled_by_handler &&
155 client_helper_details::isStatusCancelled(result.error_code()))) {
156 return;
157 }
158
159 std::forward<ErrorHandlerCallable>(ehc)(&result);
160 client_helper_details::errorHandlerReturnedUnexpectedly(&result);
161 }
162
163 private:
164 ClientType* client_;
165 StubType* stub_;
166 std::string debug_key_;
167 MethodType pfn_;
168 RequestType request_;
169 ResponseType response_;
170};
171
172template <typename ClientType, typename StubType, typename RequestType, typename ResponseType>
173auto make_client_helper(ClientType* client,
174 StubType& stub,
175 SyncMethodType<StubType, RequestType, ResponseType> method) {
176 return ClientHelper<ClientType,
177 StubType,
178 RequestType,
179 ResponseType,
180 SyncMethodType<StubType, RequestType, ResponseType>>(client, &stub, method);
181}
182
183template <typename ClientType, typename StubType, typename RequestType, typename ResponseType>
184auto make_client_helper(ClientType* client,
185 StubType& stub,
186 StreamingMethodType<StubType, RequestType, ResponseType> method) {
187 return ClientHelper<ClientType,
188 StubType,
189 RequestType,
190 ResponseType,
191 StreamingMethodType<StubType, RequestType, ResponseType>>(
192 client, &stub, method);
193}
194
195} // namespace sdk
196} // namespace viam
Definition client_helper.hpp:39
Definition client_helper.hpp:75
Defines an exception from a gRPC interaction.
Definition exception.hpp:57
Type-erased value for storing google::protobuf::Value types. A ProtoValue can be nullptr,...
Definition proto_value.hpp:55
T * get()
Return a T pointer if this is_a<T>(), else return nullptr.
Definition proto_value.hpp:381
Definition dial.hpp:25
Defines custom exceptions for the SDK.