ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
client.hpp
1 // Copyright 2014 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__CLIENT_HPP_
16 #define RCLCPP__CLIENT_HPP_
17 
18 #include <atomic>
19 #include <future>
20 #include <unordered_map>
21 #include <memory>
22 #include <mutex>
23 #include <optional> // NOLINT, cpplint doesn't think this is a cpp std header
24 #include <sstream>
25 #include <string>
26 #include <tuple>
27 #include <utility>
28 #include <variant> // NOLINT
29 #include <vector>
30 
31 #include "rcl/client.h"
32 #include "rcl/error_handling.h"
33 #include "rcl/event_callback.h"
34 #include "rcl/wait.h"
35 
36 #include "rclcpp/detail/cpp_callback_trampoline.hpp"
37 #include "rclcpp/exceptions.hpp"
38 #include "rclcpp/expand_topic_or_service_name.hpp"
39 #include "rclcpp/function_traits.hpp"
40 #include "rclcpp/logging.hpp"
41 #include "rclcpp/macros.hpp"
42 #include "rclcpp/node_interfaces/node_graph_interface.hpp"
43 #include "rclcpp/qos.hpp"
44 #include "rclcpp/type_support_decl.hpp"
45 #include "rclcpp/utilities.hpp"
46 #include "rclcpp/visibility_control.hpp"
47 
48 #include "rmw/error_handling.h"
49 #include "rmw/impl/cpp/demangle.hpp"
50 #include "rmw/rmw.h"
51 
52 namespace rclcpp
53 {
54 
55 namespace detail
56 {
57 template<typename FutureT>
59 {
60  FutureT future;
61  int64_t request_id;
62 
63  FutureAndRequestId(FutureT impl, int64_t req_id)
64  : future(std::move(impl)), request_id(req_id)
65  {}
66 
68  operator FutureT &() {return this->future;}
69 
71 
75  [[deprecated("FutureAndRequestId: use .future instead of an implicit conversion")]]
76  operator FutureT() {return this->future;}
77 
78  // delegate future like methods in the std::future impl_
79 
81  auto get() {return this->future.get();}
83  bool valid() const noexcept {return this->future.valid();}
85  void wait() const {return this->future.wait();}
87  template<class Rep, class Period>
88  std::future_status wait_for(
89  const std::chrono::duration<Rep, Period> & timeout_duration) const
90  {
91  return this->future.wait_for(timeout_duration);
92  }
94  template<class Clock, class Duration>
95  std::future_status wait_until(
96  const std::chrono::time_point<Clock, Duration> & timeout_time) const
97  {
98  return this->future.wait_until(timeout_time);
99  }
100 
101  // Rule of five, we could use the rule of zero here, but better be explicit as some of the
102  // methods are deleted.
103 
105  FutureAndRequestId(FutureAndRequestId && other) noexcept = default;
107  FutureAndRequestId(const FutureAndRequestId & other) = delete;
109  FutureAndRequestId & operator=(FutureAndRequestId && other) noexcept = default;
113  ~FutureAndRequestId() = default;
114 };
115 } // namespace detail
116 
117 namespace node_interfaces
118 {
119 class NodeBaseInterface;
120 } // namespace node_interfaces
121 
123 {
124 public:
125  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase)
126 
127  RCLCPP_PUBLIC
128  ClientBase(
130  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph);
131 
132  RCLCPP_PUBLIC
133  virtual ~ClientBase();
134 
136 
152  RCLCPP_PUBLIC
153  bool
154  take_type_erased_response(void * response_out, rmw_request_id_t & request_header_out);
155 
157 
158  RCLCPP_PUBLIC
159  const char *
160  get_service_name() const;
161 
163 
167  RCLCPP_PUBLIC
168  std::shared_ptr<rcl_client_t>
170 
172 
176  RCLCPP_PUBLIC
177  std::shared_ptr<const rcl_client_t>
178  get_client_handle() const;
179 
181 
184  RCLCPP_PUBLIC
185  bool
186  service_is_ready() const;
187 
189 
193  template<typename RepT = int64_t, typename RatioT = std::milli>
194  bool
196  std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
197  {
198  return wait_for_service_nanoseconds(
199  std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
200  );
201  }
202 
203  virtual std::shared_ptr<void> create_response() = 0;
204  virtual std::shared_ptr<rmw_request_id_t> create_request_header() = 0;
205  virtual void handle_response(
206  std::shared_ptr<rmw_request_id_t> request_header, std::shared_ptr<void> response) = 0;
207 
209 
218  RCLCPP_PUBLIC
219  bool
220  exchange_in_use_by_wait_set_state(bool in_use_state);
221 
223 
234  RCLCPP_PUBLIC
237 
239 
250  RCLCPP_PUBLIC
253 
255 
280  void
281  set_on_new_response_callback(std::function<void(size_t)> callback)
282  {
283  if (!callback) {
284  throw std::invalid_argument(
285  "The callback passed to set_on_new_response_callback "
286  "is not callable.");
287  }
288 
289  auto new_callback =
290  [callback, this](size_t number_of_responses) {
291  try {
292  callback(number_of_responses);
293  } catch (const std::exception & exception) {
294  RCLCPP_ERROR_STREAM(
295  node_logger_,
296  "rclcpp::ClientBase@" << this <<
297  " caught " << rmw::impl::cpp::demangle(exception) <<
298  " exception in user-provided callback for the 'on new response' callback: " <<
299  exception.what());
300  } catch (...) {
301  RCLCPP_ERROR_STREAM(
302  node_logger_,
303  "rclcpp::ClientBase@" << this <<
304  " caught unhandled exception in user-provided callback " <<
305  "for the 'on new response' callback");
306  }
307  };
308 
309  std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
310 
311  // Set it temporarily to the new callback, while we replace the old one.
312  // This two-step setting, prevents a gap where the old std::function has
313  // been replaced but the middleware hasn't been told about the new one yet.
315  rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
316  static_cast<const void *>(&new_callback));
317 
318  // Store the std::function to keep it in scope, also overwrites the existing one.
319  on_new_response_callback_ = new_callback;
320 
321  // Set it again, now using the permanent storage.
323  rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
324  static_cast<const void *>(&on_new_response_callback_));
325  }
326 
328  void
330  {
331  std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
332  if (on_new_response_callback_) {
333  set_on_new_response_callback(nullptr, nullptr);
334  on_new_response_callback_ = nullptr;
335  }
336  }
337 
338 protected:
339  RCLCPP_DISABLE_COPY(ClientBase)
340 
341  RCLCPP_PUBLIC
342  bool
343  wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
344 
345  RCLCPP_PUBLIC
346  rcl_node_t *
347  get_rcl_node_handle();
348 
349  RCLCPP_PUBLIC
350  const rcl_node_t *
351  get_rcl_node_handle() const;
352 
353  RCLCPP_PUBLIC
354  void
355  set_on_new_response_callback(rcl_event_callback_t callback, const void * user_data);
356 
357  rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
358  std::shared_ptr<rcl_node_t> node_handle_;
359  std::shared_ptr<rclcpp::Context> context_;
360  rclcpp::Logger node_logger_;
361 
362  std::shared_ptr<rcl_client_t> client_handle_;
363 
364  std::atomic<bool> in_use_by_wait_set_{false};
365 
366  std::recursive_mutex callback_mutex_;
367  std::function<void(size_t)> on_new_response_callback_{nullptr};
368 };
369 
370 template<typename ServiceT>
371 class Client : public ClientBase
372 {
373 public:
374  using Request = typename ServiceT::Request;
375  using Response = typename ServiceT::Response;
376 
377  using SharedRequest = typename ServiceT::Request::SharedPtr;
378  using SharedResponse = typename ServiceT::Response::SharedPtr;
379 
380  using Promise = std::promise<SharedResponse>;
381  using PromiseWithRequest = std::promise<std::pair<SharedRequest, SharedResponse>>;
382 
383  using SharedPromise = std::shared_ptr<Promise>;
384  using SharedPromiseWithRequest = std::shared_ptr<PromiseWithRequest>;
385 
386  using Future = std::future<SharedResponse>;
387  using SharedFuture = std::shared_future<SharedResponse>;
388  using SharedFutureWithRequest = std::shared_future<std::pair<SharedRequest, SharedResponse>>;
389 
390  using CallbackType = std::function<void (SharedFuture)>;
391  using CallbackWithRequestType = std::function<void (SharedFutureWithRequest)>;
392 
393  RCLCPP_SMART_PTR_DEFINITIONS(Client)
394 
395 
404  : detail::FutureAndRequestId<std::future<SharedResponse>>
405  {
407 
409 
413  [[deprecated(
414  "FutureAndRequestId: use .future.share() instead of an implicit conversion")]]
415  operator SharedFuture() {return this->future.share();}
416 
417  // delegate future like methods in the std::future impl_
418 
420  SharedFuture share() noexcept {return this->future.share();}
421  };
422 
424 
432  : detail::FutureAndRequestId<std::shared_future<SharedResponse>>
433  {
435  };
436 
438 
446  : detail::FutureAndRequestId<std::shared_future<std::pair<SharedRequest, SharedResponse>>>
447  {
449  std::shared_future<std::pair<SharedRequest, SharedResponse>>
451  };
452 
454 
466  rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
467  const std::string & service_name,
468  rcl_client_options_t & client_options)
469  : ClientBase(node_base, node_graph)
470  {
471  using rosidl_typesupport_cpp::get_service_type_support_handle;
472  auto service_type_support_handle =
473  get_service_type_support_handle<ServiceT>();
475  this->get_client_handle().get(),
476  this->get_rcl_node_handle(),
477  service_type_support_handle,
478  service_name.c_str(),
479  &client_options);
480  if (ret != RCL_RET_OK) {
481  if (ret == RCL_RET_SERVICE_NAME_INVALID) {
482  auto rcl_node_handle = this->get_rcl_node_handle();
483  // this will throw on any validation problem
484  rcl_reset_error();
486  service_name,
487  rcl_node_get_name(rcl_node_handle),
488  rcl_node_get_namespace(rcl_node_handle),
489  true);
490  }
491  rclcpp::exceptions::throw_from_rcl_error(ret, "could not create client");
492  }
493  }
494 
495  virtual ~Client()
496  {
497  }
498 
500 
512  bool
513  take_response(typename ServiceT::Response & response_out, rmw_request_id_t & request_header_out)
514  {
515  return this->take_type_erased_response(&response_out, request_header_out);
516  }
517 
519 
522  std::shared_ptr<void>
523  create_response() override
524  {
525  return std::shared_ptr<void>(new typename ServiceT::Response());
526  }
527 
529 
532  std::shared_ptr<rmw_request_id_t>
534  {
535  // TODO(wjwwood): This should probably use rmw_request_id's allocator.
536  // (since it is a C type)
537  return std::shared_ptr<rmw_request_id_t>(new rmw_request_id_t);
538  }
539 
541 
545  void
547  std::shared_ptr<rmw_request_id_t> request_header,
548  std::shared_ptr<void> response) override
549  {
550  std::optional<CallbackInfoVariant>
551  optional_pending_request = this->get_and_erase_pending_request(request_header->sequence_number);
552  if (!optional_pending_request) {
553  return;
554  }
555  auto & value = *optional_pending_request;
556  auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(
557  std::move(response));
558  if (std::holds_alternative<Promise>(value)) {
559  auto & promise = std::get<Promise>(value);
560  promise.set_value(std::move(typed_response));
561  } else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
562  auto & inner = std::get<CallbackTypeValueVariant>(value);
563  const auto & callback = std::get<CallbackType>(inner);
564  auto & promise = std::get<Promise>(inner);
565  auto & future = std::get<SharedFuture>(inner);
566  promise.set_value(std::move(typed_response));
567  callback(std::move(future));
568  } else if (std::holds_alternative<CallbackWithRequestTypeValueVariant>(value)) {
569  auto & inner = std::get<CallbackWithRequestTypeValueVariant>(value);
570  const auto & callback = std::get<CallbackWithRequestType>(inner);
571  auto & promise = std::get<PromiseWithRequest>(inner);
572  auto & future = std::get<SharedFutureWithRequest>(inner);
573  auto & request = std::get<SharedRequest>(inner);
574  promise.set_value(std::make_pair(std::move(request), std::move(typed_response)));
575  callback(std::move(future));
576  }
577  }
578 
580 
607  FutureAndRequestId
608  async_send_request(SharedRequest request)
609  {
610  Promise promise;
611  auto future = promise.get_future();
612  auto req_id = async_send_request_impl(
613  *request,
614  std::move(promise));
615  return FutureAndRequestId(std::move(future), req_id);
616  }
617 
619 
633  template<
634  typename CallbackT,
635  typename std::enable_if<
637  CallbackT,
638  CallbackType
639  >::value
640  >::type * = nullptr
641  >
642  SharedFutureAndRequestId
643  async_send_request(SharedRequest request, CallbackT && cb)
644  {
645  Promise promise;
646  auto shared_future = promise.get_future().share();
647  auto req_id = async_send_request_impl(
648  *request,
649  std::make_tuple(
650  CallbackType{std::forward<CallbackT>(cb)},
651  shared_future,
652  std::move(promise)));
653  return SharedFutureAndRequestId{std::move(shared_future), req_id};
654  }
655 
657 
664  template<
665  typename CallbackT,
666  typename std::enable_if<
668  CallbackT,
669  CallbackWithRequestType
670  >::value
671  >::type * = nullptr
672  >
673  SharedFutureWithRequestAndRequestId
674  async_send_request(SharedRequest request, CallbackT && cb)
675  {
676  PromiseWithRequest promise;
677  auto shared_future = promise.get_future().share();
678  auto req_id = async_send_request_impl(
679  *request,
680  std::make_tuple(
681  CallbackWithRequestType{std::forward<CallbackT>(cb)},
682  request,
683  shared_future,
684  std::move(promise)));
685  return SharedFutureWithRequestAndRequestId{std::move(shared_future), req_id};
686  }
687 
689 
699  bool
700  remove_pending_request(int64_t request_id)
701  {
702  std::lock_guard guard(pending_requests_mutex_);
703  return pending_requests_.erase(request_id) != 0u;
704  }
705 
707 
712  bool
714  {
715  return this->remove_pending_request(future.request_id);
716  }
717 
719 
724  bool
726  {
727  return this->remove_pending_request(future.request_id);
728  }
729 
731 
736  bool
738  {
739  return this->remove_pending_request(future.request_id);
740  }
741 
743 
746  size_t
748  {
749  std::lock_guard guard(pending_requests_mutex_);
750  auto ret = pending_requests_.size();
751  pending_requests_.clear();
752  return ret;
753  }
754 
756 
762  template<typename AllocatorT = std::allocator<int64_t>>
763  size_t
765  std::chrono::time_point<std::chrono::system_clock> time_point,
766  std::vector<int64_t, AllocatorT> * pruned_requests = nullptr)
767  {
768  std::lock_guard guard(pending_requests_mutex_);
769  auto old_size = pending_requests_.size();
770  for (auto it = pending_requests_.begin(), last = pending_requests_.end(); it != last; ) {
771  if (it->second.first < time_point) {
772  if (pruned_requests) {
773  pruned_requests->push_back(it->first);
774  }
775  it = pending_requests_.erase(it);
776  } else {
777  ++it;
778  }
779  }
780  return old_size - pending_requests_.size();
781  }
782 
783 protected:
784  using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
785  using CallbackWithRequestTypeValueVariant = std::tuple<
786  CallbackWithRequestType, SharedRequest, SharedFutureWithRequest, PromiseWithRequest>;
787 
788  using CallbackInfoVariant = std::variant<
789  std::promise<SharedResponse>,
790  CallbackTypeValueVariant,
791  CallbackWithRequestTypeValueVariant>;
792 
793  int64_t
794  async_send_request_impl(const Request & request, CallbackInfoVariant value)
795  {
796  int64_t sequence_number;
797  std::lock_guard<std::mutex> lock(pending_requests_mutex_);
798  rcl_ret_t ret = rcl_send_request(get_client_handle().get(), &request, &sequence_number);
799  if (RCL_RET_OK != ret) {
800  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
801  }
802  pending_requests_.try_emplace(
803  sequence_number,
804  std::make_pair(std::chrono::system_clock::now(), std::move(value)));
805  return sequence_number;
806  }
807 
808  std::optional<CallbackInfoVariant>
809  get_and_erase_pending_request(int64_t request_number)
810  {
811  std::unique_lock<std::mutex> lock(pending_requests_mutex_);
812  auto it = this->pending_requests_.find(request_number);
813  if (it == this->pending_requests_.end()) {
814  RCUTILS_LOG_DEBUG_NAMED(
815  "rclcpp",
816  "Received invalid sequence number. Ignoring...");
817  return std::nullopt;
818  }
819  std::optional<CallbackInfoVariant> value = std::move(it->second.second);
820  this->pending_requests_.erase(request_number);
821  return value;
822  }
823 
824  RCLCPP_DISABLE_COPY(Client)
825 
826  std::unordered_map<
827  int64_t,
828  std::pair<
829  std::chrono::time_point<std::chrono::system_clock>,
830  CallbackInfoVariant>>
831  pending_requests_;
832  std::mutex pending_requests_mutex_;
833 };
834 
835 } // namespace rclcpp
836 
837 #endif // RCLCPP__CLIENT_HPP_
RCLCPP_PUBLIC bool exchange_in_use_by_wait_set_state(bool in_use_state)
Exchange the "in use by wait set" state for this client.
Definition: client.cpp:199
RCLCPP_PUBLIC rclcpp::QoS get_request_publisher_actual_qos() const
Get the actual request publsher QoS settings, after the defaults have been determined.
Definition: client.cpp:205
RCLCPP_PUBLIC std::shared_ptr< rcl_client_t > get_client_handle()
Return the rcl_client_t client handle in a std::shared_ptr.
Definition: client.cpp:97
void set_on_new_response_callback(std::function< void(size_t)> callback)
Set a callback to be called when each new response is received.
Definition: client.hpp:281
RCLCPP_PUBLIC bool take_type_erased_response(void *response_out, rmw_request_id_t &request_header_out)
Take the next response for this client as a type erased pointer.
Definition: client.cpp:76
bool wait_for_service(std::chrono::duration< RepT, RatioT > timeout=std::chrono::duration< RepT, RatioT >(-1))
Wait for a service to be ready.
Definition: client.hpp:195
RCLCPP_PUBLIC const char * get_service_name() const
Return the name of the service.
Definition: client.cpp:91
void clear_on_new_response_callback()
Unset the callback registered for new responses, if any.
Definition: client.hpp:329
RCLCPP_PUBLIC rclcpp::QoS get_response_subscription_actual_qos() const
Get the actual response subscription QoS settings, after the defaults have been determined.
Definition: client.cpp:224
RCLCPP_PUBLIC bool service_is_ready() const
Return if the service is ready.
Definition: client.cpp:109
bool take_response(typename ServiceT::Response &response_out, rmw_request_id_t &request_header_out)
Take the next response for this client.
Definition: client.hpp:513
std::shared_ptr< rmw_request_id_t > create_request_header() override
Create a shared pointer with a rmw_request_id_t.
Definition: client.hpp:533
bool remove_pending_request(int64_t request_id)
Cleanup a pending request.
Definition: client.hpp:700
std::shared_ptr< void > create_response() override
Create a shared pointer with the response type.
Definition: client.hpp:523
size_t prune_requests_older_than(std::chrono::time_point< std::chrono::system_clock > time_point, std::vector< int64_t, AllocatorT > *pruned_requests=nullptr)
Clean all pending requests older than a time_point.
Definition: client.hpp:764
SharedFutureWithRequestAndRequestId async_send_request(SharedRequest request, CallbackT &&cb)
Send a request to the service server and schedule a callback in the executor.
Definition: client.hpp:674
SharedFutureAndRequestId async_send_request(SharedRequest request, CallbackT &&cb)
Send a request to the service server and schedule a callback in the executor.
Definition: client.hpp:643
void handle_response(std::shared_ptr< rmw_request_id_t > request_header, std::shared_ptr< void > response) override
Handle a server response.
Definition: client.hpp:546
FutureAndRequestId async_send_request(SharedRequest request)
Send a request to the service server.
Definition: client.hpp:608
size_t prune_pending_requests()
Clean all pending requests.
Definition: client.hpp:747
bool remove_pending_request(const SharedFutureAndRequestId &future)
Cleanup a pending request.
Definition: client.hpp:725
bool remove_pending_request(const SharedFutureWithRequestAndRequestId &future)
Cleanup a pending request.
Definition: client.hpp:737
Client(rclcpp::node_interfaces::NodeBaseInterface *node_base, rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph, const std::string &service_name, rcl_client_options_t &client_options)
Default constructor.
Definition: client.hpp:464
bool remove_pending_request(const FutureAndRequestId &future)
Cleanup a pending request.
Definition: client.hpp:713
Encapsulation of Quality of Service settings.
Definition: qos.hpp:111
Pure virtual interface class for the NodeBase part of the Node API.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_client_init(rcl_client_t *client, const rcl_node_t *node, const rosidl_service_type_support_t *type_support, const char *service_name, const rcl_client_options_t *options)
Initialize a rcl client.
Definition: client.c:53
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_send_request(const rcl_client_t *client, const void *ros_request, int64_t *sequence_number)
Send a ROS request using a client.
Definition: client.c:239
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC std::string expand_topic_or_service_name(const std::string &name, const std::string &node_name, const std::string &namespace_, bool is_service=false)
Expand a topic or service name and throw if it is not valid.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_name(const rcl_node_t *node)
Return the name of the node.
Definition: node.c:435
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_namespace(const rcl_node_t *node)
Return the namespace of the node.
Definition: node.c:444
Options available for a rcl_client_t.
Definition: client.h:44
Structure which encapsulates a ROS Node.
Definition: node.h:42
A convenient Client::Future and request id pair.
Definition: client.hpp:405
SharedFuture share() noexcept
See std::future::share().
Definition: client.hpp:420
A convenient Client::SharedFuture and request id pair.
Definition: client.hpp:433
A convenient Client::SharedFutureWithRequest and request id pair.
Definition: client.hpp:447
std::future_status wait_for(const std::chrono::duration< Rep, Period > &timeout_duration) const
See std::future::wait_for().
Definition: client.hpp:88
std::future_status wait_until(const std::chrono::time_point< Clock, Duration > &timeout_time) const
See std::future::wait_until().
Definition: client.hpp:95
FutureAndRequestId & operator=(FutureAndRequestId &&other) noexcept=default
Move assignment.
FutureAndRequestId & operator=(const FutureAndRequestId &other)=delete
Deleted copy assignment, each instance is a unique owner of the future.
FutureAndRequestId(const FutureAndRequestId &other)=delete
Deleted copy constructor, each instance is a unique owner of the future.
void wait() const
See std::future::wait().
Definition: client.hpp:85
FutureAndRequestId(FutureAndRequestId &&other) noexcept=default
Move constructor.
~FutureAndRequestId()=default
Destructor.
auto get()
See std::future::get().
Definition: client.hpp:81
bool valid() const noexcept
See std::future::valid().
Definition: client.hpp:83
#define RCL_RET_SERVICE_NAME_INVALID
Service name (same as topic name) does not pass validation.
Definition: types.h:48
#define RCL_RET_OK
Success return code.
Definition: types.h:26
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:23