ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
context.cpp
1 // Copyright 2015-2020 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 #include "rclcpp/context.hpp"
16 
17 #include <memory>
18 #include <mutex>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22 #include <unordered_set>
23 #include <utility>
24 
25 #include "rcl/init.h"
26 #include "rcl/logging.h"
27 
28 #include "rclcpp/detail/utilities.hpp"
29 #include "rclcpp/exceptions.hpp"
30 #include "rclcpp/logging.hpp"
31 #include "rclcpp/graph_listener.hpp"
32 #include "rcpputils/scope_exit.hpp"
33 #include "rcutils/error_handling.h"
34 #include "rcutils/macros.h"
35 
36 #include "./logging_mutex.hpp"
37 
38 using rclcpp::Context;
39 
40 namespace rclcpp
41 {
44 {
45 public:
46  RCLCPP_SMART_PTR_DEFINITIONS(WeakContextsWrapper)
47 
48  void
49  add_context(const Context::SharedPtr & context)
50  {
51  std::lock_guard<std::mutex> guard(mutex_);
52  weak_contexts_.push_back(context);
53  }
54 
55  void
56  remove_context(const Context * context)
57  {
58  std::lock_guard<std::mutex> guard(mutex_);
59  weak_contexts_.erase(
60  std::remove_if(
61  weak_contexts_.begin(),
62  weak_contexts_.end(),
63  [context](const Context::WeakPtr & weak_context) {
64  auto locked_context = weak_context.lock();
65  if (!locked_context) {
66  // take advantage and removed expired contexts
67  return true;
68  }
69  return locked_context.get() == context;
70  }
71  ),
72  weak_contexts_.end());
73  }
74 
75  std::vector<Context::SharedPtr>
76  get_contexts()
77  {
78  std::lock_guard<std::mutex> lock(mutex_);
79  std::vector<Context::SharedPtr> shared_contexts;
80  for (auto it = weak_contexts_.begin(); it != weak_contexts_.end(); /* noop */) {
81  auto context_ptr = it->lock();
82  if (!context_ptr) {
83  // remove invalid weak context pointers
84  it = weak_contexts_.erase(it);
85  } else {
86  ++it;
87  shared_contexts.push_back(context_ptr);
88  }
89  }
90  return shared_contexts;
91  }
92 
93 private:
94  std::vector<std::weak_ptr<rclcpp::Context>> weak_contexts_;
95  std::mutex mutex_;
96 };
97 } // namespace rclcpp
98 
100 
102 static
103 WeakContextsWrapper::SharedPtr
104 get_weak_contexts()
105 {
106  static WeakContextsWrapper::SharedPtr weak_contexts = WeakContextsWrapper::make_shared();
107  if (!weak_contexts) {
108  throw std::runtime_error("weak contexts vector is not valid");
109  }
110  return weak_contexts;
111 }
112 
114 static
115 size_t &
116 get_logging_reference_count()
117 {
118  static size_t ref_count = 0;
119  return ref_count;
120 }
121 
122 extern "C"
123 {
124 static
125 void
126 rclcpp_logging_output_handler(
127  const rcutils_log_location_t * location,
128  int severity, const char * name, rcutils_time_point_value_t timestamp,
129  const char * format, va_list * args)
130 {
131  try {
132  std::shared_ptr<std::recursive_mutex> logging_mutex;
133  logging_mutex = get_global_logging_mutex();
134  std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
136  location, severity, name, timestamp, format, args);
137  } catch (std::exception & ex) {
138  RCUTILS_SAFE_FWRITE_TO_STDERR(ex.what());
139  RCUTILS_SAFE_FWRITE_TO_STDERR("\n");
140  } catch (...) {
141  RCUTILS_SAFE_FWRITE_TO_STDERR("failed to take global rclcpp logging mutex\n");
142  }
143 }
144 } // extern "C"
145 
146 Context::Context()
147 : rcl_context_(nullptr),
148  shutdown_reason_(""),
149  logging_mutex_(nullptr),
150  graph_listener_(nullptr)
151 {}
152 
153 Context::~Context()
154 {
155  // acquire the init lock to prevent race conditions with init and shutdown
156  // this will not prevent errors, but will maybe make them easier to reproduce
157  std::lock_guard<std::recursive_mutex> lock(init_mutex_);
158  try {
159  // Cannot rely on virtual dispatch in a destructor, so explicitly use the
160  // shutdown() provided by this base class.
161  Context::shutdown("context destructor was called while still not shutdown");
162  // at this point it is shutdown and cannot reinit
163  // clean_up will finalize the rcl context
164  this->clean_up();
165  } catch (const std::exception & exc) {
166  RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "unhandled exception in ~Context(): %s", exc.what());
167  } catch (...) {
168  RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "unhandled exception in ~Context()");
169  }
170 }
171 
172 RCLCPP_LOCAL
173 void
174 __delete_context(rcl_context_t * context)
175 {
176  if (context) {
177  if (rcl_context_is_valid(context)) {
178  RCLCPP_ERROR(
179  rclcpp::get_logger("rclcpp"), "rcl context unexpectedly not shutdown during cleanup");
180  } else {
181  // if context pointer is not null and is shutdown, then it's ready for fini
182  rcl_ret_t ret = rcl_context_fini(context);
183  if (RCL_RET_OK != ret) {
184  RCLCPP_ERROR(
185  rclcpp::get_logger("rclcpp"),
186  "failed to finalize context: %s", rcl_get_error_string().str);
187  rcl_reset_error();
188  }
189  }
190  delete context;
191  }
192 }
193 
194 void
196  int argc,
197  char const * const * argv,
198  const rclcpp::InitOptions & init_options)
199 {
200  std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
201  if (this->is_valid()) {
203  }
204  this->clean_up();
205  rcl_context_t * context = new rcl_context_t;
206  if (!context) {
207  throw std::runtime_error("failed to allocate memory for rcl context");
208  }
210  rcl_ret_t ret = rcl_init(argc, argv, init_options.get_rcl_init_options(), context);
211  if (RCL_RET_OK != ret) {
212  delete context;
213  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to initialize rcl");
214  }
215  rcl_context_.reset(context, __delete_context);
216 
217  try {
218  if (init_options.auto_initialize_logging()) {
219  logging_mutex_ = get_global_logging_mutex();
220  std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
221  size_t & count = get_logging_reference_count();
222  if (0u == count) {
224  &rcl_context_->global_arguments,
226  rclcpp_logging_output_handler);
227  if (RCL_RET_OK != ret) {
228  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
229  }
230  } else {
231  RCLCPP_WARN(
232  rclcpp::get_logger("rclcpp"),
233  "logging was initialized more than once");
234  }
235  ++count;
236  }
237 
238  std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
239  argc, argv, &(rcl_context_->global_arguments), rcl_get_default_allocator());
240  if (!unparsed_ros_arguments.empty()) {
241  throw exceptions::UnknownROSArgsError(std::move(unparsed_ros_arguments));
242  }
243 
244  init_options_ = init_options;
245 
246  weak_contexts_ = get_weak_contexts();
247  weak_contexts_->add_context(this->shared_from_this());
248 
249 
250  std::lock_guard<std::recursive_mutex> lock (on_shutdown_callbacks_mutex_);
251 
252  graph_listener_ = std::make_shared<graph_listener::GraphListener>(shared_from_this());
253 
254  if (!graph_listener_->is_started()) {
255  // Register an on_shutdown hook to shutdown the graph listener.
256  // This is important to ensure that the wait set is finalized before
257  // destruction of static objects occurs.
258  std::weak_ptr<rclcpp::graph_listener::GraphListener> weak_graph_listener = graph_listener_;
259  on_shutdown ([weak_graph_listener]() {
260  auto shared_graph_listener = weak_graph_listener.lock();
261  if(shared_graph_listener) {
262  shared_graph_listener->shutdown(std::nothrow);
263  }
264  });
265  }
266  } catch (const std::exception & e) {
267  ret = rcl_shutdown(rcl_context_.get());
268  rcl_context_.reset();
269  if (RCL_RET_OK != ret) {
270  std::ostringstream oss;
271  oss << "While handling: " << e.what() << std::endl <<
272  " another exception was thrown";
273  rclcpp::exceptions::throw_from_rcl_error(ret, oss.str());
274  }
275  throw;
276  }
277 }
278 
279 bool
281 {
282  // Take a local copy of the shared pointer to avoid it getting nulled under our feet.
283  auto local_rcl_context = rcl_context_;
284  if (!local_rcl_context) {
285  return false;
286  }
287  return rcl_context_is_valid(local_rcl_context.get());
288 }
289 
290 const rclcpp::InitOptions &
292 {
293  return init_options_;
294 }
295 
298 {
299  return init_options_;
300 }
301 
302 size_t
304 {
305  size_t domain_id;
306  rcl_ret_t ret = rcl_context_get_domain_id(rcl_context_.get(), &domain_id);
307  if (RCL_RET_OK != ret) {
308  rclcpp::exceptions::throw_from_rcl_error(ret, "failed to get domain id from context");
309  }
310  return domain_id;
311 }
312 
313 std::string
315 {
316  std::lock_guard<std::recursive_mutex> lock(init_mutex_);
317  return shutdown_reason_;
318 }
319 
321 
335 static thread_local std::unordered_set<const Context *> g_contexts_in_shutdown;
336 
337 bool
338 Context::shutdown(const std::string & reason)
339 {
340  // prevent races
341  std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
342  // ensure validity
343  if (!this->is_valid()) {
344  // if it is not valid, then it cannot be shutdown
345  return false;
346  }
347  // prevent reentrant calls, e.g. from a pre_shutdown callback
348  if (!g_contexts_in_shutdown.insert(this).second) {
349  // shutdown of this context is already in progress on this thread
350  return false;
351  }
352  RCPPUTILS_SCOPE_EXIT(g_contexts_in_shutdown.erase(this); );
353 
354  // call each pre-shutdown callback
355  {
356  std::lock_guard<std::recursive_mutex> lock{pre_shutdown_callbacks_mutex_};
357  // callbacks may delete other callbacks during the execution,
358  // therefore we need to save a copy and check before execution
359  // if the next callback is still present
360  auto cpy = pre_shutdown_callbacks_;
361  for (const auto & callback : cpy) {
362  auto it = std::find(pre_shutdown_callbacks_.begin(), pre_shutdown_callbacks_.end(), callback);
363  if(it != pre_shutdown_callbacks_.end()) {
364  (*callback)();
365  }
366  }
367  }
368 
369  // rcl shutdown
370  rcl_ret_t ret = rcl_shutdown(rcl_context_.get());
371  if (RCL_RET_OK != ret) {
372  rclcpp::exceptions::throw_from_rcl_error(ret);
373  }
374  // set shutdown reason
375  shutdown_reason_ = reason;
376  // call each shutdown callback
377  {
378  std::lock_guard<std::recursive_mutex> lock(on_shutdown_callbacks_mutex_);
379  // callbacks may delete other callbacks during the execution,
380  // therefore we need to save a copy and check before execution
381  // if the next callback is still present
382  auto cpy = on_shutdown_callbacks_;
383  for (const auto & callback : cpy) {
384  auto it = std::find(on_shutdown_callbacks_.begin(), on_shutdown_callbacks_.end(), callback);
385  if(it != on_shutdown_callbacks_.end()) {
386  (*callback)();
387  }
388  }
389  }
390 
391  // interrupt all blocking sleep_for() and all blocking executors or wait sets
392  this->interrupt_all_sleep_for();
393  // remove self from the global contexts
394  weak_contexts_->remove_context(this);
395  // shutdown logger
396  if (logging_mutex_) {
397  // logging was initialized by this context
398  std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
399  size_t & count = get_logging_reference_count();
400  if (0u == --count) {
401  rcl_ret_t rcl_ret = rcl_logging_fini();
402  if (RCL_RET_OK != rcl_ret) {
403  RCUTILS_SAFE_FWRITE_TO_STDERR(
404  RCUTILS_STRINGIFY(__file__) ":"
405  RCUTILS_STRINGIFY(__LINE__)
406  " failed to fini logging");
407  rcl_reset_error();
408  }
409  }
410  }
411  return true;
412 }
413 
414 rclcpp::Context::OnShutdownCallback
415 Context::on_shutdown(const OnShutdownCallback & callback)
416 {
417  add_on_shutdown_callback(callback);
418  return callback;
419 }
420 
422 Context::add_on_shutdown_callback(const OnShutdownCallback & callback)
423 {
424  return add_shutdown_callback<ShutdownType::on_shutdown>(callback);
425 }
426 
427 bool
429 {
430  return remove_shutdown_callback<ShutdownType::on_shutdown>(callback_handle);
431 }
432 
434 Context::add_pre_shutdown_callback(const PreShutdownCallback & callback)
435 {
436  return add_shutdown_callback<ShutdownType::pre_shutdown>(callback);
437 }
438 
439 bool
441  const PreShutdownCallbackHandle & callback_handle)
442 {
443  return remove_shutdown_callback<ShutdownType::pre_shutdown>(callback_handle);
444 }
445 
446 template<Context::ShutdownType shutdown_type>
448 Context::add_shutdown_callback(
449  const ShutdownCallback & callback)
450 {
451  auto callback_shared_ptr =
452  std::make_shared<ShutdownCallbackHandle::ShutdownCallbackType>(callback);
453 
454  static_assert(
455  shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
456 
457  if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
458  std::lock_guard<std::recursive_mutex> lock(pre_shutdown_callbacks_mutex_);
459  pre_shutdown_callbacks_.emplace_back(callback_shared_ptr);
460  } else {
461  std::lock_guard<std::recursive_mutex> lock(on_shutdown_callbacks_mutex_);
462  on_shutdown_callbacks_.emplace_back(callback_shared_ptr);
463  }
464 
465  ShutdownCallbackHandle callback_handle;
466  callback_handle.callback = callback_shared_ptr;
467  return callback_handle;
468 }
469 
470 template<Context::ShutdownType shutdown_type>
471 bool
472 Context::remove_shutdown_callback(
473  const rclcpp::ShutdownCallbackHandle & callback_handle)
474 {
475  const auto callback_shared_ptr = callback_handle.callback.lock();
476  if (callback_shared_ptr == nullptr) {
477  return false;
478  }
479 
480  const auto remove_callback = [&callback_shared_ptr](auto & mutex, auto & callback_vector) {
481  const std::lock_guard<std::recursive_mutex> lock(mutex);
482  auto iter = callback_vector.begin();
483  for (; iter != callback_vector.end(); iter++) {
484  if ((*iter).get() == callback_shared_ptr.get()) {
485  break;
486  }
487  }
488  if (iter == callback_vector.end()) {
489  return false;
490  }
491  callback_vector.erase(iter);
492  return true;
493  };
494 
495  static_assert(
496  shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
497 
498  if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
499  return remove_callback(pre_shutdown_callbacks_mutex_, pre_shutdown_callbacks_);
500  } else {
501  return remove_callback(on_shutdown_callbacks_mutex_, on_shutdown_callbacks_);
502  }
503 }
504 
505 std::vector<rclcpp::Context::OnShutdownCallback>
507 {
508  return get_shutdown_callback<ShutdownType::on_shutdown>();
509 }
510 
511 std::vector<rclcpp::Context::PreShutdownCallback>
513 {
514  return get_shutdown_callback<ShutdownType::pre_shutdown>();
515 }
516 
517 template<Context::ShutdownType shutdown_type>
518 std::vector<rclcpp::Context::ShutdownCallback>
519 Context::get_shutdown_callback() const
520 {
521  const auto get_callback_vector = [](auto & mutex, auto & callback_set) {
522  const std::lock_guard<std::recursive_mutex> lock(mutex);
523  std::vector<rclcpp::Context::ShutdownCallback> callbacks;
524  callbacks.reserve(callback_set.size());
525  for (auto & callback : callback_set) {
526  callbacks.emplace_back(*callback);
527  }
528  return callbacks;
529  };
530 
531  static_assert(
532  shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
533 
534  if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
535  return get_callback_vector(pre_shutdown_callbacks_mutex_, pre_shutdown_callbacks_);
536  } else {
537  return get_callback_vector(on_shutdown_callbacks_mutex_, on_shutdown_callbacks_);
538  }
539 }
540 
541 std::shared_ptr<rcl_context_t>
543 {
544  return rcl_context_;
545 }
546 
547 std::shared_ptr<rclcpp::graph_listener::GraphListener>
548 Context::get_graph_listener()
549 {
550  return graph_listener_;
551 }
552 
553 bool
554 Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
555 {
556  std::chrono::nanoseconds time_left = nanoseconds;
557  do {
558  {
559  std::unique_lock<std::mutex> lock(interrupt_mutex_);
560  auto start = std::chrono::steady_clock::now();
561  // this will release the lock while waiting
562  interrupt_condition_variable_.wait_for(lock, time_left);
563  time_left -= std::chrono::steady_clock::now() - start;
564  }
565  } while (time_left > std::chrono::nanoseconds::zero() && this->is_valid());
566  // Return true if the timeout elapsed successfully, otherwise false.
567  return this->is_valid();
568 }
569 
570 void
572 {
573  interrupt_condition_variable_.notify_all();
574 }
575 
576 void
577 Context::clean_up()
578 {
579  shutdown_reason_ = "";
580  rcl_context_.reset();
581  sub_contexts_.clear();
582 }
583 
584 std::vector<Context::SharedPtr>
586 {
587  WeakContextsWrapper::SharedPtr weak_contexts = get_weak_contexts();
588  return weak_contexts->get_contexts();
589 }
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
Thrown when init is called on an already initialized context.
Definition: context.hpp:47
Context which encapsulates shared state between nodes and other similar entities.
Definition: context.hpp:80
virtual RCLCPP_PUBLIC PreShutdownCallbackHandle add_pre_shutdown_callback(const PreShutdownCallback &callback)
Add a pre_shutdown callback to be called before shutdown is called for this context.
Definition: context.cpp:434
virtual RCLCPP_PUBLIC OnShutdownCallbackHandle add_on_shutdown_callback(const OnShutdownCallback &callback)
Add a on_shutdown callback to be called when shutdown is called for this context.
Definition: context.cpp:422
virtual RCLCPP_PUBLIC void init(int argc, char const *const *argv, const rclcpp::InitOptions &init_options=rclcpp::InitOptions())
Initialize the context, and the underlying elements like the rcl context.
Definition: context.cpp:195
RCLCPP_PUBLIC std::vector< OnShutdownCallback > get_on_shutdown_callbacks() const
Return the shutdown callbacks.
Definition: context.cpp:506
RCLCPP_PUBLIC std::vector< PreShutdownCallback > get_pre_shutdown_callbacks() const
Return the pre-shutdown callbacks.
Definition: context.cpp:512
RCLCPP_PUBLIC size_t get_domain_id() const
Return actual domain id.
Definition: context.cpp:303
RCLCPP_PUBLIC std::string shutdown_reason() const
Return the shutdown reason, or empty string if not shutdown.
Definition: context.cpp:314
RCLCPP_PUBLIC const rclcpp::InitOptions & get_init_options() const
Return the init options used during init.
Definition: context.cpp:291
RCLCPP_PUBLIC bool sleep_for(const std::chrono::nanoseconds &nanoseconds)
Sleep for a given period of time or until shutdown() is called.
Definition: context.cpp:554
RCLCPP_PUBLIC void interrupt_all_sleep_for()
Interrupt any blocking sleep_for calls, causing them to return immediately and return true.
Definition: context.cpp:571
virtual RCLCPP_PUBLIC OnShutdownCallback on_shutdown(const OnShutdownCallback &callback)
Add a on_shutdown callback to be called when shutdown is called for this context.
Definition: context.cpp:415
virtual RCLCPP_PUBLIC bool remove_pre_shutdown_callback(const PreShutdownCallbackHandle &callback_handle)
Remove an registered pre_shutdown callback.
Definition: context.cpp:440
RCLCPP_PUBLIC bool is_valid() const
Return true if the context is valid, otherwise false.
Definition: context.cpp:280
virtual RCLCPP_PUBLIC bool remove_on_shutdown_callback(const OnShutdownCallbackHandle &callback_handle)
Remove an registered on_shutdown callbacks.
Definition: context.cpp:428
virtual RCLCPP_PUBLIC bool shutdown(const std::string &reason)
Shutdown the context, making it uninitialized and therefore invalid for derived entities.
Definition: context.cpp:338
RCLCPP_PUBLIC std::shared_ptr< rcl_context_t > get_rcl_context()
Return the internal rcl context.
Definition: context.cpp:542
Encapsulation of options for initializing rclcpp.
RCLCPP_PUBLIC const rcl_init_options_t * get_rcl_init_options() const
Return the rcl init options.
RCLCPP_PUBLIC bool auto_initialize_logging() const
Return true if logging should be initialized when rclcpp::Context::init is called.
Class to manage vector of weak pointers to all created contexts.
Definition: context.cpp:44
Thrown when unparsed ROS specific arguments are found.
Definition: exceptions.hpp:206
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_context_fini(rcl_context_t *context)
Finalize a context.
Definition: context.c:49
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_context_get_domain_id(rcl_context_t *context, size_t *domain_id)
Returns the context domain id.
Definition: context.c:83
struct rcl_context_s rcl_context_t
Encapsulates the non-global state of an init/shutdown cycle.
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_context_is_valid(const rcl_context_t *context)
Return true if the given context is currently valid, otherwise false.
Definition: context.c:94
RCL_PUBLIC RCL_WARN_UNUSED rcl_context_t rcl_get_zero_initialized_context(void)
Return a zero initialization context object.
Definition: context.c:29
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_shutdown(rcl_context_t *context)
Shutdown a given rcl context.
Definition: init.c:286
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_init(int argc, char const *const *argv, const rcl_init_options_t *options, rcl_context_t *context)
Initialization of rcl.
Definition: init.c:50
RCL_PUBLIC RCL_WARN_UNUSED const rcl_allocator_t * rcl_init_options_get_allocator(const rcl_init_options_t *init_options)
Return the allocator stored in the init_options.
Definition: init_options.c:175
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_fini(void)
Definition: logging.c:127
RCL_PUBLIC void rcl_logging_multiple_output_handler(const rcutils_log_location_t *location, int severity, const char *name, rcutils_time_point_value_t timestamp, const char *format, va_list *args)
Default output handler used by rcl.
Definition: logging.c:154
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_logging_configure_with_output_handler(const rcl_arguments_t *global_args, const rcl_allocator_t *allocator, rcl_logging_output_handler_t output_handler)
Configure the logging system with the provided output handler.
Definition: logging.c:57
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC std::vector< Context::SharedPtr > get_contexts()
Return a copy of the list of context shared pointers.
Definition: context.cpp:585
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:34
Encapsulates the non-global state of an init/shutdown cycle.
Definition: context.h:114
#define RCL_RET_OK
Success return code.
Definition: types.h:27
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24