ROS 2 rclcpp + rcl - kilted  kilted
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 "rclcpp/visibility_control.hpp"
27 #include "rmw/types.h"
28 #include "tracetools/tracetools.h"
29 #include "tracetools/utils.hpp"
30 
31 namespace rclcpp
32 {
33 
34 namespace detail
35 {
36 template<typename T, typename = void>
37 struct can_be_nullptr : std::false_type {};
38 
39 // Some lambdas define a comparison with nullptr,
40 // but we see a warning that they can never be null when using it.
41 // We also test if `T &` can be assigned to `nullptr` to avoid the issue.
42 template<typename T>
43 #ifdef __QNXNTO__
44 struct can_be_nullptr<T, std::void_t<
45  decltype(std::declval<T>() == nullptr)>>: std::true_type {};
46 #else
47 struct can_be_nullptr<T, std::void_t<
48  decltype(std::declval<T>() == nullptr), decltype(std::declval<T &>() = nullptr)>>
49  : std::true_type {};
50 #endif
51 } // namespace detail
52 
53 // Forward declare
54 template<typename ServiceT>
55 class Service;
56 
57 template<typename ServiceT>
59 {
60 public:
62  : callback_(std::monostate{})
63  {}
64 
65  template<
66  typename CallbackT,
67  typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
68  void
69  set(CallbackT && callback)
70  {
71  // Workaround Windows issue with std::bind
72  if constexpr (
74  CallbackT,
75  SharedPtrCallback
76  >::value)
77  {
78  callback_.template emplace<SharedPtrCallback>(callback);
79  } else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
81  CallbackT,
82  SharedPtrWithRequestHeaderCallback
83  >::value)
84  {
85  callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
86  } else if constexpr ( // NOLINT
88  CallbackT,
89  SharedPtrDeferResponseCallback
90  >::value)
91  {
92  callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
93  } else if constexpr ( // NOLINT
95  CallbackT,
96  SharedPtrDeferResponseCallbackWithServiceHandle
97  >::value)
98  {
99  callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
100  } else {
101  // the else clause is not needed, but anyways we should only be doing this instead
102  // of all the above workaround ...
103  callback_ = std::forward<CallbackT>(callback);
104  }
105  }
106 
107  template<
108  typename CallbackT,
109  typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
110  void
111  set(CallbackT && callback)
112  {
113  if (!callback) {
114  throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
115  }
116  // Workaround Windows issue with std::bind
117  if constexpr (
119  CallbackT,
120  SharedPtrCallback
121  >::value)
122  {
123  callback_.template emplace<SharedPtrCallback>(callback);
124  } else if constexpr ( // NOLINT
126  CallbackT,
127  SharedPtrWithRequestHeaderCallback
128  >::value)
129  {
130  callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
131  } else if constexpr ( // NOLINT
133  CallbackT,
134  SharedPtrDeferResponseCallback
135  >::value)
136  {
137  callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
138  } else if constexpr ( // NOLINT
140  CallbackT,
141  SharedPtrDeferResponseCallbackWithServiceHandle
142  >::value)
143  {
144  callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
145  } else {
146  // the else clause is not needed, but anyways we should only be doing this instead
147  // of all the above workaround ...
148  callback_ = std::forward<CallbackT>(callback);
149  }
150  }
151 
152  // template<typename Allocator = std::allocator<typename ServiceT::Response>>
153  std::shared_ptr<typename ServiceT::Response>
154  dispatch(
155  const std::shared_ptr<rclcpp::Service<ServiceT>> & service_handle,
156  const std::shared_ptr<rmw_request_id_t> & request_header,
157  std::shared_ptr<typename ServiceT::Request> request)
158  {
159  TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
160  if (std::holds_alternative<std::monostate>(callback_)) {
161  // TODO(ivanpauno): Remove the set method, and force the users of this class
162  // to pass a callback at construnciton.
163  throw std::runtime_error{"unexpected request without any callback set"};
164  }
165  if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
166  const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
167  cb(request_header, std::move(request));
168  TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
169  return nullptr;
170  }
171  if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
172  const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
173  cb(service_handle, request_header, std::move(request));
174  TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
175  return nullptr;
176  }
177  // auto response = allocate_shared<typename ServiceT::Response, Allocator>();
178  auto response = std::make_shared<typename ServiceT::Response>();
179  if (std::holds_alternative<SharedPtrCallback>(callback_)) {
180  (void)request_header;
181  const auto & cb = std::get<SharedPtrCallback>(callback_);
182  cb(std::move(request), response);
183  } else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
184  const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
185  cb(request_header, std::move(request), response);
186  }
187  TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
188  return response;
189  }
190 
191  void register_callback_for_tracing()
192  {
193 #ifndef TRACETOOLS_DISABLED
194  std::visit(
195  [this](auto && arg) {
196  if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
197  char * symbol = tracetools::get_symbol(arg);
198  TRACETOOLS_DO_TRACEPOINT(
199  rclcpp_callback_register,
200  static_cast<const void *>(this),
201  symbol);
202  std::free(symbol);
203  }
204  }, callback_);
205 #endif // TRACETOOLS_DISABLED
206  }
207 
208 private:
209  using SharedPtrCallback = std::function<
210  void (
211  std::shared_ptr<typename ServiceT::Request>,
212  std::shared_ptr<typename ServiceT::Response>
213  )>;
214  using SharedPtrWithRequestHeaderCallback = std::function<
215  void (
216  std::shared_ptr<rmw_request_id_t>,
217  std::shared_ptr<typename ServiceT::Request>,
218  std::shared_ptr<typename ServiceT::Response>
219  )>;
220  using SharedPtrDeferResponseCallback = std::function<
221  void (
222  std::shared_ptr<rmw_request_id_t>,
223  std::shared_ptr<typename ServiceT::Request>
224  )>;
225  using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
226  void (
227  std::shared_ptr<rclcpp::Service<ServiceT>>,
228  std::shared_ptr<rmw_request_id_t>,
229  std::shared_ptr<typename ServiceT::Request>
230  )>;
231 
232  std::variant<
233  std::monostate,
234  SharedPtrCallback,
235  SharedPtrWithRequestHeaderCallback,
236  SharedPtrDeferResponseCallback,
237  SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
238 };
239 
240 } // namespace rclcpp
241 
242 #endif // RCLCPP__ANY_SERVICE_CALLBACK_HPP_
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.