19
19
#include < memory>
20
20
#include < future>
21
21
#include < string>
22
+ #include < tuple>
22
23
#include < vector>
23
24
#include < utility>
24
25
@@ -46,6 +47,8 @@ class GenericClient : public ClientBase
46
47
using Future = std::future<SharedResponse>;
47
48
using SharedFuture = std::shared_future<SharedResponse>;
48
49
50
+ using CallbackType = std::function<void (SharedFuture)>;
51
+
49
52
RCLCPP_SMART_PTR_DEFINITIONS (GenericClient)
50
53
51
54
// / A convenient GenericClient::Future and request id pair.
@@ -76,6 +79,20 @@ class GenericClient : public ClientBase
76
79
~FutureAndRequestId () = default ;
77
80
};
78
81
82
+ // / A convenient GenericClient::SharedFuture and request id pair.
83
+ /* *
84
+ * Public members:
85
+ * - future: a std::shared_future<SharedResponse>.
86
+ * - request_id: the request id associated with the future.
87
+ *
88
+ * All the other methods are equivalent to the ones std::shared_future provides.
89
+ */
90
+ struct SharedFutureAndRequestId
91
+ : detail::FutureAndRequestId<std::shared_future<SharedResponse>>
92
+ {
93
+ using detail::FutureAndRequestId<std::shared_future<SharedResponse>>::FutureAndRequestId;
94
+ };
95
+
79
96
GenericClient (
80
97
rclcpp::node_interfaces::NodeBaseInterface * node_base,
81
98
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
@@ -106,16 +123,16 @@ class GenericClient : public ClientBase
106
123
* If the future never completes,
107
124
* e.g. the call to Executor::spin_until_future_complete() times out,
108
125
* GenericClient::remove_pending_request() must be called to clean the client internal state.
109
- * Not doing so will make the `Client ` instance to use more memory each time a response is not
110
- * received from the service server.
126
+ * Not doing so will make the `GenericClient ` instance to use more memory each time a response is
127
+ * not received from the service server.
111
128
*
112
129
* ```cpp
113
- * auto future = client ->async_send_request(my_request);
130
+ * auto future = generic_client ->async_send_request(my_request);
114
131
* if (
115
132
* rclcpp::FutureReturnCode::TIMEOUT ==
116
133
* executor->spin_until_future_complete(future, timeout))
117
134
* {
118
- * client ->remove_pending_request(future);
135
+ * generic_client ->remove_pending_request(future);
119
136
* // handle timeout
120
137
* } else {
121
138
* handle_response(future.get());
@@ -129,6 +146,45 @@ class GenericClient : public ClientBase
129
146
FutureAndRequestId
130
147
async_send_request (const Request request);
131
148
149
+ // / Send a request to the service server and schedule a callback in the executor.
150
+ /* *
151
+ * Similar to the previous overload, but a callback will automatically be called when a response
152
+ * is received.
153
+ *
154
+ * If the callback is never called, because we never got a reply for the service server,
155
+ * remove_pending_request() has to be called with the returned request id or
156
+ * prune_pending_requests().
157
+ * Not doing so will make the `GenericClient` instance use more memory each time a response is not
158
+ * received from the service server.
159
+ * In this case, it's convenient to setup a timer to cleanup the pending requests.
160
+ *
161
+ * \param[in] request request to be send.
162
+ * \param[in] cb callback that will be called when we get a response for this request.
163
+ * \return the request id representing the request just sent.
164
+ */
165
+ template <
166
+ typename CallbackT,
167
+ typename std::enable_if<
168
+ rclcpp::function_traits::same_arguments<
169
+ CallbackT,
170
+ CallbackType
171
+ >::value
172
+ >::type * = nullptr
173
+ >
174
+ SharedFutureAndRequestId
175
+ async_send_request (const Request request, CallbackT && cb)
176
+ {
177
+ Promise promise;
178
+ auto shared_future = promise.get_future ().share ();
179
+ auto req_id = async_send_request_impl (
180
+ request,
181
+ std::make_tuple (
182
+ CallbackType{std::forward<CallbackT>(cb)},
183
+ shared_future,
184
+ std::move (promise)));
185
+ return SharedFutureAndRequestId{std::move (shared_future), req_id};
186
+ }
187
+
132
188
// / Clean all pending requests older than a time_point.
133
189
/* *
134
190
* \param[in] time_point Requests that were sent before this point are going to be removed.
@@ -149,15 +205,52 @@ class GenericClient : public ClientBase
149
205
pruned_requests);
150
206
}
151
207
208
+ // / Clean all pending requests.
209
+ /* *
210
+ * \return number of pending requests that were removed.
211
+ */
152
212
RCLCPP_PUBLIC
153
213
size_t
154
214
prune_pending_requests ();
155
215
216
+ // / Cleanup a pending request.
217
+ /* *
218
+ * This notifies the client that we have waited long enough for a response from the server
219
+ * to come, we have given up and we are not waiting for a response anymore.
220
+ *
221
+ * Not calling this will make the client start using more memory for each request
222
+ * that never got a reply from the server.
223
+ *
224
+ * \param[in] request_id request id returned by async_send_request().
225
+ * \return true when a pending request was removed, false if not (e.g. a response was received).
226
+ */
156
227
RCLCPP_PUBLIC
157
228
bool
158
229
remove_pending_request (
159
230
int64_t request_id);
160
231
232
+ // / Cleanup a pending request.
233
+ /* *
234
+ * Convenient overload, same as:
235
+ *
236
+ * `GenericClient::remove_pending_request(this, future.request_id)`.
237
+ */
238
+ RCLCPP_PUBLIC
239
+ bool
240
+ remove_pending_request (
241
+ const FutureAndRequestId & future);
242
+
243
+ // / Cleanup a pending request.
244
+ /* *
245
+ * Convenient overload, same as:
246
+ *
247
+ * `GenericClient::remove_pending_request(this, future.request_id)`.
248
+ */
249
+ RCLCPP_PUBLIC
250
+ bool
251
+ remove_pending_request (
252
+ const SharedFutureAndRequestId & future);
253
+
161
254
// / Take the next response for this client.
162
255
/* *
163
256
* \sa ClientBase::take_type_erased_response().
@@ -179,9 +272,12 @@ class GenericClient : public ClientBase
179
272
}
180
273
181
274
protected:
275
+ using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
182
276
using CallbackInfoVariant = std::variant<
183
- std::promise<SharedResponse>>; // Use variant for extension
277
+ std::promise<SharedResponse>,
278
+ CallbackTypeValueVariant>; // Use variant for extension
184
279
280
+ RCLCPP_PUBLIC
185
281
int64_t
186
282
async_send_request_impl (
187
283
const Request request,
0 commit comments