ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
ROS 2 C++ Client Library with ROS Client Library
any_service_callback.hpp
1 // Copyright 2015 Open Source Robotics Foundation, Inc.
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__ANY_SERVICE_CALLBACK_HPP_
16 #define RCLCPP__ANY_SERVICE_CALLBACK_HPP_
17 
18 #include <variant>
19 #include <functional>
20 #include <memory>
21 #include <stdexcept>
22 #include <type_traits>
23 #include <utility>
24 
25 #include "rclcpp/function_traits.hpp"
26 #include "rmw/types.h"
27 #include "tracetools/tracetools.h"
28 #include "tracetools/utils.hpp"
29 
30 namespace rclcpp
31 {
32 
33 namespace detail
34 {
35 template<typename T, typename = void>
36 struct can_be_nullptr : std::false_type {};
37 
38 // Some lambdas define a comparison with nullptr,
39 // but we see a warning that they can never be null when using it.
40 // We also test if `T &` can be assigned to `nullptr` to avoid the issue.
41 template<typename T>
42 #ifdef __QNXNTO__
43 struct can_be_nullptr<T, std::void_t<
44  decltype(std::declval<T>() == nullptr)>>: std::true_type {};
45 #else
46 struct can_be_nullptr<T, std::void_t<
47  decltype(std::declval<T>() == nullptr), decltype(std::declval<T &>() = nullptr)>>
48  : std::true_type {};
49 #endif
50 } // namespace detail
51 
52 // Forward declare
53 template<typename ServiceT>
54 class Service;
55 
56 template<typename ServiceT>
58 {
59 public:
61  : callback_(std::monostate{})
62  {}
63 
64  template<
65  typename CallbackT,
66  typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
67  void
68  set(CallbackT && callback)
69  {
70  // Workaround Windows issue with std::bind
71  if constexpr (
73  CallbackT,
74  SharedPtrCallback
75  >::value)
76  {
77  callback_.template emplace<SharedPtrCallback>(callback);
78  } else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
80  CallbackT,
81  SharedPtrWithRequestHeaderCallback
82  >::value)
83  {
84  callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
85  } else if constexpr ( // NOLINT
87  CallbackT,
88  SharedPtrDeferResponseCallback
89  >::value)
90  {
91  callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
92  } else if constexpr ( // NOLINT
94  CallbackT,
95  SharedPtrDeferResponseCallbackWithServiceHandle
96  >::value)
97  {
98  callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
99  } else if constexpr ( // NOLINT
101  CallbackT,
102  ConstRefCallback
103  >::value)
104  {
105  callback_.template emplace<ConstRefCallback>(callback);
106  } else if constexpr ( // NOLINT
108  CallbackT,
109  ConstRefWithRequestHeaderCallback
110  >::value)
111  {
112  callback_.template emplace<ConstRefWithRequestHeaderCallback>(callback);
113  } else {
114  // the else clause is not needed, but anyways we should only be doing this instead
115  // of all the above workaround ...
116  callback_ = std::forward<CallbackT>(callback);
117  }
118  }
119 
120  template<
121  typename CallbackT,
122  typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
123  void
124  set(CallbackT && callback)
125  {
126  if (!callback) {
127  throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
128  }
129  // Workaround Windows issue with std::bind
130  if constexpr (
132  CallbackT,
133  SharedPtrCallback
134  >::value)
135  {
136  callback_.template emplace<SharedPtrCallback>(callback);
137  } else if constexpr ( // NOLINT
139  CallbackT,
140  SharedPtrWithRequestHeaderCallback
141  >::value)
142  {
143  callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
144  } else if constexpr ( // NOLINT
146  CallbackT,
147  SharedPtrDeferResponseCallback
148  >::value)
149  {
150  callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
151  } else if constexpr ( // NOLINT
153  CallbackT,
154  SharedPtrDeferResponseCallbackWithServiceHandle
155  >::value)
156  {
157  callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
158  } else if constexpr ( // NOLINT
160  CallbackT,
161  ConstRefCallback
162  >::value)
163  {
164  callback_.template emplace<ConstRefCallback>(callback);
165  } else if constexpr ( // NOLINT
167  CallbackT,
168  ConstRefWithRequestHeaderCallback
169  >::value)
170  {
171  callback_.template emplace<ConstRefWithRequestHeaderCallback>(callback);
172  } else {
173  // the else clause is not needed, but anyways we should only be doing this instead
174  // of all the above workaround ...
175  callback_ = std::forward<CallbackT>(callback);
176  }
177  }
178 
179  // template<typename Allocator = std::allocator<typename ServiceT::Response>>
180  std::shared_ptr<typename ServiceT::Response>
181  dispatch(
182  const std::shared_ptr<rclcpp::Service<ServiceT>> & service_handle,
183  const std::shared_ptr<rmw_request_id_t> & request_header,
184  std::shared_ptr<typename ServiceT::Request> request)
185  {
186  TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
187  if (std::holds_alternative<std::monostate>(callback_)) {
188  // TODO(ivanpauno): Remove the set method, and force the users of this class
189  // to pass a callback at construnciton.
190  throw std::runtime_error{"unexpected request without any callback set"};
191  }
192  if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
193  const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
194  cb(request_header, std::move(request));
195  TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
196  return nullptr;
197  }
198  if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
199  const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
200  cb(service_handle, request_header, std::move(request));
201  TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
202  return nullptr;
203  }
204  // auto response = allocate_shared<typename ServiceT::Response, Allocator>();
205  auto response = std::make_shared<typename ServiceT::Response>();
206  if (std::holds_alternative<SharedPtrCallback>(callback_)) {
207  (void)request_header;
208  const auto & cb = std::get<SharedPtrCallback>(callback_);
209  cb(std::move(request), response);
210  } else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
211  const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
212  cb(request_header, std::move(request), response);
213  } else if (std::holds_alternative<ConstRefCallback>(callback_)) {
214  if (nullptr == request) {
215  throw std::runtime_error("dispatch called with a null request");
216  }
217  const auto & cb = std::get<ConstRefCallback>(callback_);
218  cb(*request, *response);
219  } else if (std::holds_alternative<ConstRefWithRequestHeaderCallback>(callback_)) {
220  if (nullptr == request_header || nullptr == request) {
221  throw std::runtime_error("dispatch called with a null request header or request");
222  }
223  const auto & cb = std::get<ConstRefWithRequestHeaderCallback>(callback_);
224  cb(*request_header, *request, *response);
225  }
226  TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
227  return response;
228  }
229 
230  void register_callback_for_tracing()
231  {
232 #ifndef TRACETOOLS_DISABLED
233  std::visit(
234  [this](auto && arg) {
235  if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
236  char * symbol = tracetools::get_symbol(arg);
237  TRACETOOLS_DO_TRACEPOINT(
238  rclcpp_callback_register,
239  static_cast<const void *>(this),
240  symbol);
241  std::free(symbol);
242  }
243  }, callback_);
244 #endif // TRACETOOLS_DISABLED
245  }
246 
247 private:
248  using SharedPtrCallback = std::function<
249  void (
250  std::shared_ptr<typename ServiceT::Request>,
251  std::shared_ptr<typename ServiceT::Response>
252  )>;
253  using SharedPtrWithRequestHeaderCallback = std::function<
254  void (
255  std::shared_ptr<rmw_request_id_t>,
256  std::shared_ptr<typename ServiceT::Request>,
257  std::shared_ptr<typename ServiceT::Response>
258  )>;
259  using SharedPtrDeferResponseCallback = std::function<
260  void (
261  std::shared_ptr<rmw_request_id_t>,
262  std::shared_ptr<typename ServiceT::Request>
263  )>;
264  using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
265  void (
266  std::shared_ptr<rclcpp::Service<ServiceT>>,
267  std::shared_ptr<rmw_request_id_t>,
268  std::shared_ptr<typename ServiceT::Request>
269  )>;
270  // The request and response are received as shared pointers and dereferenced before
271  // calling the user callback, like AnySubscriptionCallback does for `const MessageT &`.
272  using ConstRefCallback = std::function<
273  void (
274  const typename ServiceT::Request &,
275  typename ServiceT::Response &
276  )>;
277  using ConstRefWithRequestHeaderCallback = std::function<
278  void (
279  const rmw_request_id_t &,
280  const typename ServiceT::Request &,
281  typename ServiceT::Response &
282  )>;
283 
284  std::variant<
285  std::monostate,
286  SharedPtrCallback,
287  SharedPtrWithRequestHeaderCallback,
288  SharedPtrDeferResponseCallback,
289  SharedPtrDeferResponseCallbackWithServiceHandle,
290  ConstRefCallback,
291  ConstRefWithRequestHeaderCallback> callback_;
292 };
293 
294 } // namespace rclcpp
295 
296 #endif // RCLCPP__ANY_SERVICE_CALLBACK_HPP_
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.