ROS 2 rclcpp + rcl - rolling  rolling-20536064
ROS 2 C++ Client Library with ROS Client Library
publisher_base.cpp
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 #include "rclcpp/publisher_base.hpp"
16 
17 #include <rmw/error_handling.h>
18 #include <rmw/rmw.h>
19 
20 #include <functional>
21 #include <memory>
22 #include <mutex>
23 #include <sstream>
24 #include <stdexcept>
25 #include <string>
26 #include <unordered_map>
27 #include <vector>
28 
29 #include "rcl/event.h"
30 #include "rcutils/logging_macros.h"
31 #include "rmw/impl/cpp/demangle.hpp"
32 
33 #include "rclcpp/allocator/allocator_common.hpp"
34 #include "rclcpp/allocator/allocator_deleter.hpp"
35 #include "rclcpp/exceptions.hpp"
36 #include "rclcpp/expand_topic_or_service_name.hpp"
37 #include "rclcpp/experimental/intra_process_manager.hpp"
38 #include "rclcpp/logging.hpp"
39 #include "rclcpp/macros.hpp"
40 #include "rclcpp/network_flow_endpoint.hpp"
41 #include "rclcpp/node.hpp"
42 #include "rclcpp/event_handler.hpp"
43 
45 
46 PublisherBase::PublisherBase(
48  const std::string & topic,
49  const rosidl_message_type_support_t & type_support,
50  const rcl_publisher_options_t & publisher_options,
51  const PublisherEventCallbacks & event_callbacks,
52  bool use_default_callbacks)
53 : rcl_node_handle_(node_base->get_shared_rcl_node_handle()),
54  intra_process_is_enabled_(false),
55  intra_process_publisher_id_(0),
56  type_support_(type_support),
57  event_callbacks_(event_callbacks)
58 {
59  auto custom_deleter = [node_handle = this->rcl_node_handle_](rcl_publisher_t * rcl_pub)
60  {
61  if (rcl_publisher_fini(rcl_pub, node_handle.get()) != RCL_RET_OK) {
62  RCLCPP_ERROR(
63  rclcpp::get_node_logger(node_handle.get()).get_child("rclcpp"),
64  "Error in destruction of rcl publisher handle: %s",
65  rcl_get_error_string().str);
66  rcl_reset_error();
67  }
68  delete rcl_pub;
69  };
70 
71  publisher_handle_ = std::shared_ptr<rcl_publisher_t>(
72  new rcl_publisher_t, custom_deleter);
73  *publisher_handle_.get() = rcl_get_zero_initialized_publisher();
74 
76  publisher_handle_.get(),
77  rcl_node_handle_.get(),
78  &type_support,
79  topic.c_str(),
80  &publisher_options);
81  if (ret != RCL_RET_OK) {
82  if (ret == RCL_RET_TOPIC_NAME_INVALID) {
83  auto rcl_node_handle = rcl_node_handle_.get();
84  // this will throw on any validation problem
85  rcl_reset_error();
87  topic,
88  rcl_node_get_name(rcl_node_handle),
89  rcl_node_get_namespace(rcl_node_handle));
90  }
91 
92  rclcpp::exceptions::throw_from_rcl_error(ret, "could not create publisher");
93  }
94  // Life time of this object is tied to the publisher handle.
95  rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(publisher_handle_.get());
96  if (!publisher_rmw_handle) {
97  auto msg = std::string("failed to get rmw handle: ") + rcl_get_error_string().str;
98  rcl_reset_error();
99  throw std::runtime_error(msg);
100  }
101  if (rmw_get_gid_for_publisher(publisher_rmw_handle, &rmw_gid_) != RMW_RET_OK) {
102  auto msg = std::string("failed to get publisher gid: ") + rmw_get_error_string().str;
103  rmw_reset_error();
104  throw std::runtime_error(msg);
105  }
106 
107  bind_event_callbacks(event_callbacks_, use_default_callbacks);
108 }
109 
110 PublisherBase::~PublisherBase()
111 {
112  // must fini the events before fini-ing the publisher
113  event_handlers_.clear();
114 
115  auto ipm = weak_ipm_.lock();
116 
117  if (!intra_process_is_enabled_) {
118  return;
119  }
120  if (!ipm) {
121  // TODO(ivanpauno): should this raise an error?
122  RCLCPP_WARN(
123  rclcpp::get_logger("rclcpp"),
124  "Intra process manager died before a publisher.");
125  return;
126  }
127  ipm->remove_publisher(intra_process_publisher_id_);
128 }
129 
130 const char *
132 {
133  return rcl_publisher_get_topic_name(publisher_handle_.get());
134 }
135 
136 bool
138 {
139  return rcl_publisher_event_type_is_supported(event_type);
140 }
141 
142 void
144  const PublisherEventCallbacks & event_callbacks, bool use_default_callbacks)
145 {
146  try {
147  if (event_callbacks.deadline_callback) {
148  this->add_event_handler(
149  event_callbacks.deadline_callback,
150  RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
151  }
152  } catch (const UnsupportedEventTypeException & /*exc*/) {
153  RCLCPP_WARN(
154  rclcpp::get_logger("rclcpp"),
155  "Failed to add event handler for deadline; not supported");
156  }
157 
158  try {
159  if (event_callbacks.liveliness_callback) {
160  this->add_event_handler(
161  event_callbacks.liveliness_callback,
162  RCL_PUBLISHER_LIVELINESS_LOST);
163  }
164  } catch (const UnsupportedEventTypeException & /*exc*/) {
165  RCLCPP_WARN(
166  rclcpp::get_logger("rclcpp"),
167  "Failed to add event handler for liveliness; not supported");
168  }
169 
170  QOSOfferedIncompatibleQoSCallbackType incompatible_qos_cb;
171  if (event_callbacks.incompatible_qos_callback) {
172  incompatible_qos_cb = event_callbacks.incompatible_qos_callback;
173  } else if (use_default_callbacks) {
174  // Register default callback when not specified
175  incompatible_qos_cb = [this](QOSOfferedIncompatibleQoSInfo & info) {
176  this->default_incompatible_qos_callback(info);
177  };
178  }
179  try {
180  if (incompatible_qos_cb) {
181  this->add_event_handler(incompatible_qos_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
182  }
183  } catch (const UnsupportedEventTypeException & /*exc*/) {
184  RCLCPP_WARN(
185  rclcpp::get_logger("rclcpp"),
186  "Failed to add event handler for incompatible qos; not supported");
187  }
188 
189  IncompatibleTypeCallbackType incompatible_type_cb;
190  if (event_callbacks.incompatible_type_callback) {
191  incompatible_type_cb = event_callbacks.incompatible_type_callback;
192  } else if (use_default_callbacks) {
193  // Register default callback when not specified
194  incompatible_type_cb = [this](IncompatibleTypeInfo & info) {
195  this->default_incompatible_type_callback(info);
196  };
197  }
198  try {
199  if (incompatible_type_cb) {
200  this->add_event_handler(incompatible_type_cb, RCL_PUBLISHER_INCOMPATIBLE_TYPE);
201  }
202  } catch (UnsupportedEventTypeException & /*exc*/) {
203  RCLCPP_WARN(
204  rclcpp::get_logger("rclcpp"),
205  "Failed to add event handler for incompatible type; not supported");
206  }
207 
208  try {
209  if (event_callbacks.matched_callback) {
210  this->add_event_handler(
211  event_callbacks.matched_callback,
212  RCL_PUBLISHER_MATCHED);
213  }
214  } catch (const UnsupportedEventTypeException & /*exc*/) {
215  RCLCPP_WARN(
216  rclcpp::get_logger("rclcpp"),
217  "Failed to add event handler for matched; not supported");
218  }
219 }
220 
221 size_t
223 {
224  const rcl_publisher_options_t * publisher_options = rcl_publisher_get_options(
225  publisher_handle_.get());
226  if (!publisher_options) {
227  auto msg = std::string("failed to get publisher options: ") + rcl_get_error_string().str;
228  rcl_reset_error();
229  throw std::runtime_error(msg);
230  }
231  return publisher_options->qos.depth;
232 }
233 
234 const rmw_gid_t &
236 {
237  return rmw_gid_;
238 }
239 
240 std::shared_ptr<rcl_publisher_t>
242 {
243  return publisher_handle_;
244 }
245 
246 std::shared_ptr<const rcl_publisher_t>
248 {
249  return publisher_handle_;
250 }
251 
252 const
253 std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
255 {
256  return event_handlers_;
257 }
258 
259 size_t
261 {
262  size_t inter_process_subscription_count = 0;
263 
265  publisher_handle_.get(),
266  &inter_process_subscription_count);
267 
268  if (RCL_RET_PUBLISHER_INVALID == status) {
269  rcl_reset_error(); /* next call will reset error message if not context */
270  if (rcl_publisher_is_valid_except_context(publisher_handle_.get())) {
271  rcl_context_t * context = rcl_publisher_get_context(publisher_handle_.get());
272  if (nullptr != context && !rcl_context_is_valid(context)) {
273  /* publisher is invalid due to context being shutdown */
274  return 0;
275  }
276  }
277  }
278  if (RCL_RET_OK != status) {
279  rclcpp::exceptions::throw_from_rcl_error(status, "failed to get get subscription count");
280  }
281  return inter_process_subscription_count;
282 }
283 
284 size_t
286 {
287  auto ipm = weak_ipm_.lock();
288  if (!intra_process_is_enabled_) {
289  return 0;
290  }
291  if (!ipm) {
292  // TODO(ivanpauno): should this just return silently? Or maybe return with a warning?
293  // Same as wjwwood comment in publisher_factory create_shared_publish_callback.
294  throw std::runtime_error(
295  "intra process subscriber count called after "
296  "destruction of intra process manager");
297  }
298  return ipm->get_subscription_count(intra_process_publisher_id_);
299 }
300 
301 bool
303 {
304  return rcl_publisher_get_actual_qos(publisher_handle_.get())->durability ==
305  RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL;
306 }
307 
310 {
311  const rmw_qos_profile_t * qos = rcl_publisher_get_actual_qos(publisher_handle_.get());
312  if (!qos) {
313  auto msg = std::string("failed to get qos settings: ") + rcl_get_error_string().str;
314  rcl_reset_error();
315  throw std::runtime_error(msg);
316  }
317 
319 }
320 
321 bool
323 {
324  return RCL_RET_OK == rcl_publisher_assert_liveliness(publisher_handle_.get());
325 }
326 
327 bool
329 {
330  return !intra_process_is_enabled_ && rcl_publisher_can_loan_messages(publisher_handle_.get());
331 }
332 
333 bool
334 PublisherBase::operator==(const rmw_gid_t & gid) const
335 {
336  return *this == &gid;
337 }
338 
339 bool
340 PublisherBase::operator==(const rmw_gid_t * gid) const
341 {
342  bool result = false;
343  auto ret = rmw_compare_gids_equal(gid, &this->get_gid(), &result);
344  if (ret != RMW_RET_OK) {
345  auto msg = std::string("failed to compare gids: ") + rmw_get_error_string().str;
346  rmw_reset_error();
347  throw std::runtime_error(msg);
348  }
349  return result;
350 }
351 
352 void
354  uint64_t intra_process_publisher_id,
355  const IntraProcessManagerSharedPtr & ipm)
356 {
357  intra_process_publisher_id_ = intra_process_publisher_id;
358  weak_ipm_ = ipm;
359  intra_process_is_enabled_ = true;
360 }
361 
362 void
363 PublisherBase::default_incompatible_qos_callback(
364  rclcpp::QOSOfferedIncompatibleQoSInfo & event) const
365 {
366  std::string policy_name = qos_policy_name_from_kind(event.last_policy_kind);
367  RCLCPP_WARN(
368  rclcpp::get_logger(rcl_node_get_logger_name(rcl_node_handle_.get())),
369  "New subscription discovered on topic '%s', requesting incompatible QoS. "
370  "No messages will be sent to it. "
371  "Last incompatible policy: %s",
372  get_topic_name(),
373  policy_name.c_str());
374 }
375 
376 void
377 PublisherBase::default_incompatible_type_callback(
378  [[maybe_unused]] rclcpp::IncompatibleTypeInfo & event) const
379 {
380  RCLCPP_WARN(
381  rclcpp::get_logger(rcl_node_get_logger_name(rcl_node_handle_.get())),
382  "Incompatible type on topic '%s', no messages will be sent to it.", get_topic_name());
383 }
384 
385 std::vector<rclcpp::NetworkFlowEndpoint> PublisherBase::get_network_flow_endpoints() const
386 {
387  rcutils_allocator_t allocator = rcutils_get_default_allocator();
388  rcl_network_flow_endpoint_array_t network_flow_endpoint_array =
389  rcl_get_zero_initialized_network_flow_endpoint_array();
390  rcl_ret_t ret = rcl_publisher_get_network_flow_endpoints(
391  publisher_handle_.get(), &allocator, &network_flow_endpoint_array);
392  if (RCL_RET_OK != ret) {
393  auto error_msg = std::string("error obtaining network flows of publisher: ") +
394  rcl_get_error_string().str;
395  rcl_reset_error();
396  if (RCL_RET_OK !=
397  rcl_network_flow_endpoint_array_fini(&network_flow_endpoint_array))
398  {
399  error_msg += std::string(", also error cleaning up network flow array: ") +
400  rcl_get_error_string().str;
401  rcl_reset_error();
402  }
403  rclcpp::exceptions::throw_from_rcl_error(ret, error_msg);
404  }
405 
406  std::vector<rclcpp::NetworkFlowEndpoint> network_flow_endpoint_vector;
407  network_flow_endpoint_vector.reserve(network_flow_endpoint_array.size);
408  for (size_t i = 0; i < network_flow_endpoint_array.size; ++i) {
409  network_flow_endpoint_vector.emplace_back(
410  network_flow_endpoint_array.network_flow_endpoint[i]);
411  }
412 
413  ret = rcl_network_flow_endpoint_array_fini(&network_flow_endpoint_array);
414  if (RCL_RET_OK != ret) {
415  rclcpp::exceptions::throw_from_rcl_error(ret, "error cleaning up network flow array");
416  }
417 
418  return network_flow_endpoint_vector;
419 }
420 
422 {
423  if (!intra_process_is_enabled_) {
424  return 0u;
425  }
426 
427  auto ipm = weak_ipm_.lock();
428 
429  if (!ipm) {
430  // TODO(ivanpauno): should this raise an error?
431  RCLCPP_WARN(
432  rclcpp::get_logger("rclcpp"),
433  "Intra process manager died for a publisher.");
434  return 0u;
435  }
436 
437  return ipm->lowest_available_capacity(intra_process_publisher_id_);
438 }
439 
440 void
442  const std::function<void(size_t)> & callback,
443  rcl_publisher_event_type_t event_type)
444 {
445  if (event_handlers_.count(event_type) == 0) {
446  RCLCPP_WARN(
447  rclcpp::get_logger("rclcpp"),
448  "Calling set_on_new_qos_event_callback for non registered publisher event_type");
449  return;
450  }
451 
452  if (!callback) {
453  throw std::invalid_argument(
454  "The callback passed to set_on_new_qos_event_callback "
455  "is not callable.");
456  }
457 
458  // The on_ready_callback signature has an extra `int` argument used to disambiguate between
459  // possible different entities within a generic waitable.
460  // We hide that detail to users of this method.
461  std::function<void(size_t, int)> new_callback = [callback] (size_t nr, int) {callback(nr);};
462  event_handlers_[event_type]->set_on_ready_callback(new_callback);
463 }
464 
465 void
467 {
468  if (event_handlers_.count(event_type) == 0) {
469  RCLCPP_WARN(
470  rclcpp::get_logger("rclcpp"),
471  "Calling clear_on_new_qos_event_callback for non registered event_type");
472  return;
473  }
474 
475  event_handlers_[event_type]->clear_on_ready_callback();
476 }
RCLCPP_PUBLIC Logger get_child(const std::string &suffix)
Return a logger that is a descendant of this logger.
Definition: logger.cpp:57
RCLCPP_PUBLIC const rmw_gid_t & get_gid() const
Get the global identifier for this publisher (used in rmw and by DDS).
RCLCPP_PUBLIC void set_on_new_qos_event_callback(const std::function< void(size_t)> &callback, rcl_publisher_event_type_t event_type)
Set a callback to be called when each new qos event instance occurs.
RCLCPP_PUBLIC std::shared_ptr< rcl_publisher_t > get_publisher_handle()
Get the rcl publisher handle.
RCLCPP_PUBLIC size_t get_intra_process_subscription_count() const
Get intraprocess subscription count.
RCLCPP_PUBLIC void clear_on_new_qos_event_callback(rcl_publisher_event_type_t event_type)
Unset the callback registered for new qos events, if any.
RCLCPP_PUBLIC void bind_event_callbacks(const PublisherEventCallbacks &event_callbacks, bool use_default_callbacks)
Add event handlers for passed in event_callbacks.
RCLCPP_PUBLIC rclcpp::QoS get_actual_qos() const
Get the actual QoS settings, after the defaults have been determined.
RCLCPP_PUBLIC const char * get_topic_name() const
Get the topic that this publisher publishes on.
RCLCPP_PUBLIC void setup_intra_process(uint64_t intra_process_publisher_id, const IntraProcessManagerSharedPtr &ipm)
Implementation utility function used to setup intra process publishing after creation.
RCLCPP_PUBLIC bool operator==(const rmw_gid_t &gid) const
Compare this publisher to a gid.
RCLCPP_PUBLIC size_t get_queue_size() const
Get the queue size for this publisher.
RCLCPP_PUBLIC bool can_loan_messages() const
Check if publisher instance can loan messages.
RCLCPP_PUBLIC bool is_durability_transient_local() const
Get if durability is transient local.
RCLCPP_PUBLIC size_t get_subscription_count() const
Get subscription count.
RCLCPP_PUBLIC std::vector< rclcpp::NetworkFlowEndpoint > get_network_flow_endpoints() const
Get network flow endpoints.
RCLCPP_PUBLIC RCUTILS_WARN_UNUSED bool assert_liveliness() const
Manually assert that this Publisher is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC).
RCLCPP_PUBLIC const std::unordered_map< rcl_publisher_event_type_t, std::shared_ptr< rclcpp::EventHandlerBase > > & get_event_handlers() const
Get all the QoS event handlers associated with this publisher.
RCLCPP_PUBLIC size_t lowest_available_ipm_capacity() const
Return the lowest available capacity for all subscription buffers.
static RCLCPP_PUBLIC bool event_type_is_supported(const rcl_publisher_event_type_t event_type)
Check if a publisher event type is supported by the active RMW implementation.
Encapsulation of Quality of Service settings.
Definition: qos.hpp:114
Pure virtual interface class for the NodeBase part of the Node API.
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
enum rcl_publisher_event_type_e rcl_publisher_event_type_t
Enumeration of all of the publisher events that may fire.
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_publisher_event_type_is_supported(const rcl_publisher_event_type_t event_type)
Check if a publisher event type is supported by the active RMW implementation.
Definition: event.c:233
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.
RCLCPP_PUBLIC Logger get_node_logger(const rcl_node_t *node)
Return a named logger using an rcl_node_t.
Definition: logger.cpp:43
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:32
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:416
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:425
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_node_get_logger_name(const rcl_node_t *node)
Return the logger name of the node.
Definition: node.c:493
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_init(rcl_publisher_t *publisher, const rcl_node_t *node, const rosidl_message_type_support_t *type_support, const char *topic_name, const rcl_publisher_options_t *options)
Initialize a rcl publisher.
Definition: publisher.c:45
RCL_PUBLIC RCL_WARN_UNUSED rcl_context_t * rcl_publisher_get_context(const rcl_publisher_t *publisher)
Return the context associated with this publisher.
Definition: publisher.c:406
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_get_subscription_count(const rcl_publisher_t *publisher, size_t *subscription_count)
Get the number of subscriptions matched to a publisher.
Definition: publisher.c:445
RCL_PUBLIC RCL_WARN_UNUSED rmw_publisher_t * rcl_publisher_get_rmw_handle(const rcl_publisher_t *publisher)
Return the rmw publisher handle.
Definition: publisher.c:397
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_publisher_get_topic_name(const rcl_publisher_t *publisher)
Get the topic name for the publisher.
Definition: publisher.c:379
RCL_PUBLIC RCL_WARN_UNUSED const rmw_qos_profile_t * rcl_publisher_get_actual_qos(const rcl_publisher_t *publisher)
Get the actual qos settings of the publisher.
Definition: publisher.c:465
RCL_PUBLIC bool rcl_publisher_is_valid_except_context(const rcl_publisher_t *publisher)
Return true if the publisher is valid except the context, otherwise false.
Definition: publisher.c:434
RCL_PUBLIC RCL_WARN_UNUSED const rcl_publisher_options_t * rcl_publisher_get_options(const rcl_publisher_t *publisher)
Return the rcl publisher options.
Definition: publisher.c:388
RCL_PUBLIC RCL_WARN_UNUSED rcl_publisher_t rcl_get_zero_initialized_publisher(void)
Return a rcl_publisher_t struct with members set to NULL.
Definition: publisher.c:37
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_fini(rcl_publisher_t *publisher, rcl_node_t *node)
Finalize a rcl_publisher_t.
Definition: publisher.c:176
RCL_PUBLIC bool rcl_publisher_can_loan_messages(const rcl_publisher_t *publisher)
Check if publisher instance can loan messages.
Definition: publisher.c:474
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_publisher_assert_liveliness(const rcl_publisher_t *publisher)
Manually assert that this Publisher is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC)
Definition: publisher.c:331
Encapsulates the non-global state of an init/shutdown cycle.
Definition: context.h:114
Options available for a rcl publisher.
Definition: publisher.h:44
rmw_qos_profile_t qos
Middleware quality of service settings for the publisher.
Definition: publisher.h:46
Structure which encapsulates a ROS Publisher.
Definition: publisher.h:37
Contains callbacks for various types of events a Publisher can receive from the middleware.
static QoSInitialization from_rmw(const rmw_qos_profile_t &rmw_qos)
Create a QoSInitialization from an existing rmw_qos_profile_t, using its history and depth.
Definition: qos.cpp:70
#define RCL_RET_OK
Success return code.
Definition: types.h:27
#define RCL_RET_TOPIC_NAME_INVALID
Topic name does not pass validation.
Definition: types.h:47
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24
#define RCL_RET_PUBLISHER_INVALID
Invalid rcl_publisher_t given return code.
Definition: types.h:69