ROS 2 rclcpp + rcl - rolling  rolling-a919a6e5
ROS 2 C++ Client Library with ROS Client Library
generic_service.hpp
1 // Copyright 2024 Sony Group Corporation.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef RCLCPP__GENERIC_SERVICE_HPP_
16 #define RCLCPP__GENERIC_SERVICE_HPP_
17 
18 #include <cstdlib>
19 #include <functional>
20 #include <memory>
21 #include <string>
22 #include <type_traits>
23 #include <utility>
24 #include <variant>
25 
26 #include "rclcpp/typesupport_helpers.hpp"
27 
28 #include "rosidl_runtime_c/service_type_support_struct.h"
29 #include "rosidl_typesupport_introspection_cpp/identifier.hpp"
30 #include "rosidl_typesupport_introspection_cpp/service_introspection.hpp"
31 
32 #include "service.hpp"
33 
34 namespace rclcpp
35 {
36 class GenericService;
37 
39 {
40 public:
41  using SharedRequest = std::shared_ptr<void>;
42  using SharedResponse = std::shared_ptr<void>;
43 
45  : callback_(std::monostate{})
46  {}
47 
48  template<
49  typename CallbackT,
50  typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
51  void
52  set(CallbackT && callback)
53  {
54  // Workaround Windows issue with std::bind
55  if constexpr (
57  CallbackT,
58  SharedPtrCallback
59  >::value)
60  {
61  callback_.template emplace<SharedPtrCallback>(callback);
62  } else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
64  CallbackT,
65  SharedPtrWithRequestHeaderCallback
66  >::value)
67  {
68  callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
69  } else if constexpr ( // NOLINT
71  CallbackT,
72  SharedPtrDeferResponseCallback
73  >::value)
74  {
75  callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
76  } else if constexpr ( // NOLINT
78  CallbackT,
79  SharedPtrDeferResponseCallbackWithServiceHandle
80  >::value)
81  {
82  callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
83  } else {
84  // the else clause is not needed, but anyways we should only be doing this instead
85  // of all the above workaround ...
86  callback_ = std::forward<CallbackT>(callback);
87  }
88  }
89 
90  template<
91  typename CallbackT,
92  typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
93  void
94  set(CallbackT && callback)
95  {
96  if (!callback) {
97  throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
98  }
99  // Workaround Windows issue with std::bind
100  if constexpr (
102  CallbackT,
103  SharedPtrCallback
104  >::value)
105  {
106  callback_.template emplace<SharedPtrCallback>(callback);
107  } else if constexpr ( // NOLINT
109  CallbackT,
110  SharedPtrWithRequestHeaderCallback
111  >::value)
112  {
113  callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
114  } else if constexpr ( // NOLINT
116  CallbackT,
117  SharedPtrDeferResponseCallback
118  >::value)
119  {
120  callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
121  } else if constexpr ( // NOLINT
123  CallbackT,
124  SharedPtrDeferResponseCallbackWithServiceHandle
125  >::value)
126  {
127  callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
128  } else {
129  // the else clause is not needed, but anyways we should only be doing this instead
130  // of all the above workaround ...
131  callback_ = std::forward<CallbackT>(callback);
132  }
133  }
134 
135  SharedResponse
136  dispatch(
137  const std::shared_ptr<rclcpp::GenericService> & service_handle,
138  const std::shared_ptr<rmw_request_id_t> & request_header,
139  SharedRequest request,
140  SharedRequest response)
141  {
142  TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
143  if (std::holds_alternative<std::monostate>(callback_)) {
144  // TODO(ivanpauno): Remove the set method, and force the users of this class
145  // to pass a callback at construnciton.
146  throw std::runtime_error{"unexpected request without any callback set"};
147  }
148  if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
149  const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
150  cb(request_header, std::move(request));
151  return nullptr;
152  }
153  if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
154  const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
155  cb(service_handle, request_header, std::move(request));
156  return nullptr;
157  }
158 
159  if (std::holds_alternative<SharedPtrCallback>(callback_)) {
160  (void)request_header;
161  const auto & cb = std::get<SharedPtrCallback>(callback_);
162  cb(std::move(request), std::move(response));
163  } else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
164  const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
165  cb(request_header, std::move(request), std::move(response));
166  }
167  TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
168  return response;
169  }
170 
171  void register_callback_for_tracing()
172  {
173 #ifndef TRACETOOLS_DISABLED
174  std::visit(
175  [this](auto && arg) {
176  if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
177  char * symbol = tracetools::get_symbol(arg);
178  TRACETOOLS_DO_TRACEPOINT(
179  rclcpp_callback_register,
180  static_cast<const void *>(this),
181  symbol);
182  std::free(symbol);
183  }
184  }, callback_);
185 #endif // TRACETOOLS_DISABLED
186  }
187 
188 private:
189  using SharedPtrCallback = std::function<void (SharedRequest, SharedResponse)>;
190  using SharedPtrWithRequestHeaderCallback = std::function<
191  void (
192  std::shared_ptr<rmw_request_id_t>,
193  SharedRequest,
194  SharedResponse
195  )>;
196  using SharedPtrDeferResponseCallback = std::function<
197  void (
198  std::shared_ptr<rmw_request_id_t>,
199  SharedRequest
200  )>;
201  using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
202  void (
203  std::shared_ptr<rclcpp::GenericService>,
204  std::shared_ptr<rmw_request_id_t>,
205  SharedRequest
206  )>;
207 
208  std::variant<
209  std::monostate,
210  SharedPtrCallback,
211  SharedPtrWithRequestHeaderCallback,
212  SharedPtrDeferResponseCallback,
213  SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
214 };
215 
217  : public ServiceBase,
218  public std::enable_shared_from_this<GenericService>
219 {
220 public:
221  using Request = void *; // Serialized/Deserialized data pointer of request message
222  using Response = void *; // Serialized/Deserialized data pointer of response message
223  using SharedRequest = std::shared_ptr<void>;
224  using SharedResponse = std::shared_ptr<void>;
225  using CallbackType = std::function<void (const SharedRequest, SharedResponse)>;
226 
227  using CallbackWithHeaderType =
228  std::function<void (const std::shared_ptr<rmw_request_id_t>,
229  const SharedRequest,
230  SharedResponse)>;
231 
232  RCLCPP_SMART_PTR_DEFINITIONS(GenericService)
233 
234 
246  RCLCPP_PUBLIC
248  std::shared_ptr<rcl_node_t> node_handle,
249  const std::string & service_name,
250  const std::string & service_type,
251  GenericServiceCallback any_callback,
252  rcl_service_options_t & service_options);
253 
254  GenericService() = delete;
255 
256  RCLCPP_PUBLIC
257  virtual ~GenericService() {}
258 
260 
271  RCLCPP_PUBLIC
272  bool
273  take_request(SharedRequest request_out, rmw_request_id_t & request_id_out);
274 
275  RCLCPP_PUBLIC
276  std::shared_ptr<void>
277  create_request() override;
278 
279  RCLCPP_PUBLIC
280  std::shared_ptr<void>
281  create_response();
282 
283  RCLCPP_PUBLIC
284  std::shared_ptr<rmw_request_id_t>
285  create_request_header() override;
286 
287  RCLCPP_PUBLIC
288  void
289  handle_request(
290  std::shared_ptr<rmw_request_id_t> request_header,
291  std::shared_ptr<void> request) override;
292 
293  RCLCPP_PUBLIC
294  void
295  send_response(rmw_request_id_t & req_id, SharedResponse & response);
296 
297 private:
298  RCLCPP_DISABLE_COPY(GenericService)
299 
300  GenericServiceCallback any_callback_;
301 
302  std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
303  const rosidl_typesupport_introspection_cpp::MessageMembers * request_members_;
304  const rosidl_typesupport_introspection_cpp::MessageMembers * response_members_;
305 };
306 
307 } // namespace rclcpp
308 #endif // RCLCPP__GENERIC_SERVICE_HPP_
RCLCPP_PUBLIC bool take_request(SharedRequest request_out, rmw_request_id_t &request_id_out)
Take the next request from the service.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
Options available for a rcl service.
Definition: service.h:50