nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
publisher.hpp
Go to the documentation of this file.
1// nros-cpp: Publisher class
2// Freestanding C++ — no exceptions, no STL required
3
9
10#ifndef NROS_RCLCPP_PUBLISHER_HPP
11#define NROS_RCLCPP_PUBLISHER_HPP
12
13#include <cstdint>
14#include <cstddef>
15#include <string.h> // memcpy — `<cstring>` isn't in Zephyr's minimal libcpp
16
17#include "rclcpp/detail/traits.hpp"
19#include "rclcpp/detail/std_detect.hpp" // phase-488 — `NROS_CPP_HAS_SHARED_PTR`
20#include "rclcpp/detail/entity_name.hpp" // phase-444 — the one entity-name copy
21#include "rclcpp/executor.hpp" // phase-444 — the graph counter these forward to
22#include "rclcpp/owned.hpp" // phase-456 W5 — `Publisher<M>::SharedPtr` IS `Owned<Publisher<M>>`
23#include "rclcpp/result.hpp"
24#if defined(NROS_CPP_STD) // hosted-family: ported-publish
25#include <rclc/log.h> // phase-488 — `detail::report_failed_publish`
26#endif
27// RFC-0088 D5 — NROS_CPP_ASSERT_MESSAGE_FORMAT, expanded in the creator below.
29
30#include "rclcpp/detail/nros_cpp_ffi.h"
31#include "rclcpp/event.hpp" // issue 1972 — MatchedInfo / IncompatibleTypeInfo
32
33// issue 1437 — `get_actual_qos()` returns a `rclcpp::QoS` BY VALUE from an
34// inline body, so the complete type must be here, not only by the time
35// `rclcpp/node.hpp` is pulled in below.
36//
37// AFTER `nros_cpp_ffi.h`, never before: `qos.hpp` defines the four
38// `nros_cpp_qos_*_t` enums ITSELF under `#ifndef NROS_CPP_FFI_H`, so
39// reaching it first makes the cbindgen header a REDEFINITION of all four.
40#include "rclcpp/qos.hpp"
41
42// phase-456 W5 — THIS HEADER NO LONGER DETECTS ANYTHING, and the include that
43// used to sit here is gone with the block it served.
44//
45// From phase-417 W1.a until now, `Publisher<M>::SharedPtr` and friends were
46// `std::shared_ptr` / `std::unique_ptr`, so they needed `<memory>` — which a
47// MINIMAL freestanding libcpp does not ship (Zephyr's is `<cstddef>`,
48// `<cstdint>`, `<new>`, plus this repo's `zephyr/cxx-compat/`) — and the
49// aliases were therefore ABSENT on exactly the targets ported source needs them
50// on. The predicate that decided it needed three separate corrections to get
51// right (issues 0112, 1187, 1240).
52//
53// The aliases name `rclcpp::Owned<Publisher<M>>` now, which is ours and exists
54// everywhere, so there is no capability left to probe: the alias is present
55// unconditionally and means the same thing on every target. That is RFC-0096 D1
56// — one API, no shape decided by a toolchain flag — reached by deleting the
57// question rather than by answering it more carefully.
58
59namespace rclcpp {
60
66static constexpr size_t PUBLISHER_TOPIC_NAME_MAX = 256;
67
68} // namespace rclcpp
69
77namespace rclcpp {
78class Node;
79}
80
81// ============================================================================
82// `rclcpp::Publisher<M>` -- DEFINED here (RFC-0089: rclcpp:: is the home)
83// ============================================================================
84//
85// phase-428: the definition moved from `nros::` to `rclcpp::` (the `nros::`
86// alias went in phase-483 W1). The nested `SharedPtr` / `ConstSharedPtr` / `UniquePtr`
87// aliases live on the class itself, so `rclcpp::Publisher<M>::SharedPtr` -- the
88// way rclcpp users index types -- resolves with no wrapper in between.
89namespace rclcpp {
90
145#if defined(NROS_CPP_STD) // hosted-family: ported-publish
146namespace detail {
157class PublishResult : public ::rclcpp::ResultOf<void> {
158 public:
159 constexpr PublishResult(::rclcpp::ResultOf<void> r) : ::rclcpp::ResultOf<void>(r) {}
160};
161
166template <typename M> inline void report_failed_publish(const char* topic, int32_t code) {
167 static unsigned long failures = 0;
168 if ((failures++ % 1024UL) == 0) {
169 RCLC_LOG_ERROR(rclc_log_default_logger(),
170 "rclcpp::Publisher::publish on \"%.64s\" failed: rclcpp::ErrorCode %d "
171 "(%lu failure(s) so far)",
172 topic, static_cast<int>(code), failures);
173 }
174}
175} // namespace detail
176#endif
177
178template <typename M> class Publisher {
179 public:
202 using SharedPtr = ::rclcpp::Owned<Publisher<M>>;
210 using ConstSharedPtr = ::rclcpp::Owned<Publisher<M>>;
217 using UniquePtr = ::rclcpp::Owned<Publisher<M>>;
218
223 static constexpr size_t GID_SIZE = 24;
224
246#if defined(NROS_CPP_STD) // hosted-family: ported-publish
247 ::rclcpp::detail::PublishResult publish(const M& msg) {
248 const Result r = initialized_ ? Result(M::ffi_publish(storage_, &msg))
250 if (!r.ok()) ::rclcpp::detail::report_failed_publish<M>(get_topic_name(), r.raw());
251 return r;
252 }
253#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
260 template <typename D>::rclcpp::detail::PublishResult publish(::std::unique_ptr<M, D> msg) {
261 if (!msg) {
263 ::rclcpp::detail::report_failed_publish<M>(get_topic_name(), r.raw());
264 return r;
265 }
266 return publish(static_cast<const M&>(*msg));
267 }
268#endif
269#else
270 Result publish(const M& msg) {
271 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
272 return Result(M::ffi_publish(storage_, &msg));
273 }
274#endif
275
282 Result publish_serialized(const uint8_t* data, size_t len) {
283 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
284 return Result(nros_cpp_publish_raw(storage_, data, len));
285 }
286
288 NROS_CPP_DEPRECATED_MSG("renamed to publish_serialized (phase-515 C8)")
289 Result publish_raw(const uint8_t* data, size_t len) { return publish_serialized(data, len); }
290
301 template <typename W> Result publish_streamed(size_t total_len, W&& writer) {
302 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
303 struct Ctx {
304 W writer;
305 size_t total;
306 } ctx{static_cast<W&&>(writer), total_len};
307 auto size_cb = [](size_t* out_total, void* user) {
308 auto* c = static_cast<Ctx*>(user);
309 *out_total = c->total;
310 };
311 auto chunk_cb = [](uint8_t* out_buf, size_t cap, size_t* out_written, void* user) {
312 auto* c = static_cast<Ctx*>(user);
313 *out_written = c->writer(out_buf, cap);
314 };
315 return Result(nros_cpp_publisher_publish_streamed(storage_, size_cb, chunk_cb,
316 static_cast<void*>(&ctx)));
317 }
318
319 // ====================================================================
320 // Phase 124.A.7 — zero-copy publish (loan / commit / discard)
321 // ====================================================================
322
326 class Loan {
327 public:
328 Loan() : pub_(nullptr), buf_(nullptr), cap_(0), token_(nullptr) {}
329 Loan(Loan&& o) : pub_(o.pub_), buf_(o.buf_), cap_(o.cap_), token_(o.token_) {
330 o.pub_ = nullptr;
331 o.token_ = nullptr;
332 }
334 if (this != &o) {
335 release();
336 pub_ = o.pub_;
337 buf_ = o.buf_;
338 cap_ = o.cap_;
339 token_ = o.token_;
340 o.pub_ = nullptr;
341 o.token_ = nullptr;
342 }
343 return *this;
344 }
345 Loan(const Loan&) = delete;
346 Loan& operator=(const Loan&) = delete;
347 ~Loan() { release(); }
348
350 uint8_t* data() { return buf_; }
351 const uint8_t* data() const { return buf_; }
352 size_t capacity() const { return cap_; }
353
354 bool is_valid() const { return token_ != nullptr; }
355
357 Result commit(size_t actual_len) {
358 if (!token_) return Result(::rclcpp::ErrorCode::NotInitialized);
359 nros_cpp_ret_t ret = nros_cpp_publisher_commit(pub_, token_, actual_len);
360 pub_ = nullptr;
361 token_ = nullptr;
362 buf_ = nullptr;
363 cap_ = 0;
364 return Result(ret);
365 }
366
369 if (!token_) return Result::success();
370 nros_cpp_ret_t ret = nros_cpp_publisher_discard(pub_, token_);
371 pub_ = nullptr;
372 token_ = nullptr;
373 buf_ = nullptr;
374 cap_ = 0;
375 return Result(ret);
376 }
377
380 Loan(void* pub, uint8_t* buf, size_t cap, void* token)
381 : pub_(pub), buf_(buf), cap_(cap), token_(token) {}
382
383 private:
384 void release() {
385 if (token_ && pub_) {
386 nros_cpp_publisher_discard(pub_, token_);
387 token_ = nullptr;
388 }
389 }
390
391 void* pub_;
392 uint8_t* buf_;
393 size_t cap_;
394 void* token_;
395 };
396
405 ::rclcpp::ResultOf<Loan> loan(size_t requested_len) {
406 if (!initialized_)
407 return ::rclcpp::ResultOf<Loan>::error(Result(::rclcpp::ErrorCode::NotInitialized));
408 uint8_t* buf = nullptr;
409 size_t cap = 0;
410 void* token = nullptr;
411 nros_cpp_ret_t ret = nros_cpp_publisher_loan(storage_, requested_len, &buf, &cap, &token);
412 if (ret != 0) return ::rclcpp::ResultOf<Loan>::error(Result(ret));
413 return ::rclcpp::ResultOf<Loan>::ok(Loan{storage_, buf, cap, token});
414 }
415
417 const char* get_topic_name() const { return initialized_ ? topic_name_ : ""; }
418
439 nros_cpp_qos_t f{};
440 if (!initialized_ || nros_cpp_publisher_get_actual_qos(storage_, &f) != 0) {
441 return ::rclcpp::detail::qos_all_unknown();
442 }
443 return ::rclcpp::detail::qos_from_ffi(f);
444 }
445
460
489 Result get_subscription_count(::rclcpp::Executor& executor, size_t* out_count) const {
490 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
491 return executor.count_subscribers(topic_name_, out_count);
492 }
493
495 bool is_valid() const { return initialized_; }
496
499 if (initialized_) {
500 nros_cpp_publisher_destroy(storage_);
501 initialized_ = false;
502 }
503 }
504
505 // Move semantics (non-copyable). Relocation goes through the
506 // `nros_cpp_publisher_relocate` runtime call.
507 Publisher(Publisher&& other) : initialized_(other.initialized_) {
508 if (other.initialized_) {
509 nros_cpp_publisher_relocate(other.storage_, storage_);
510 ::memcpy(topic_name_, other.topic_name_, sizeof(topic_name_));
511 other.initialized_ = false;
512 }
513 }
514
516 if (this != &other) {
517 if (initialized_) {
518 nros_cpp_publisher_destroy(storage_);
519 initialized_ = false;
520 }
521 if (other.initialized_) {
522 nros_cpp_publisher_relocate(other.storage_, storage_);
523 ::memcpy(topic_name_, other.topic_name_, sizeof(topic_name_));
524 initialized_ = true;
525 other.initialized_ = false;
526 }
527 }
528 return *this;
529 }
530
533 Publisher() : storage_(), topic_name_{}, initialized_(false) {}
534
535 // ====================================================================
536 // Phase 108 — status events
537 // ====================================================================
538
546 Result on_liveliness_lost(nros_cpp_publisher_count_cb_t cb, void* user_context = nullptr) {
547 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
548 return Result(nros_cpp_publisher_set_liveliness_lost(storage_, cb, user_context));
549 }
550
556 Result on_offered_deadline_missed(uint32_t deadline_ms, nros_cpp_publisher_count_cb_t cb,
557 void* user_context = nullptr) {
558 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
559 return Result(nros_cpp_publisher_set_offered_deadline_missed(storage_, deadline_ms, cb,
560 user_context));
561 }
562
573 Result on_matched(PublisherMatchedCallbackType cb, void* user_context = nullptr) {
574 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
575 return Result(nros_cpp_publisher_set_matched(storage_, cb, user_context));
576 }
577
581 Result on_incompatible_type(IncompatibleTypeCallbackType cb, void* user_context = nullptr) {
582 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
583 return Result(nros_cpp_publisher_set_incompatible_type(storage_, cb, user_context));
584 }
585
597 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
598 return Result(nros_cpp_publisher_assert_liveliness(storage_));
599 }
600
626 Result get_gid(uint8_t (&out_gid)[GID_SIZE]) const {
627 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
628 return Result(nros_cpp_publisher_get_gid(storage_, out_gid, GID_SIZE));
629 }
630
631 private:
632 Publisher(const Publisher&) = delete;
633 Publisher& operator=(const Publisher&) = delete;
634
635 friend class ::rclcpp::Node;
636
637 // `+ sizeof(void*)`: the monitor cell pointer the runtime stores beside
638 // the handle (`CppPublisher` in nros-cpp's publisher.rs, whose
639 // compile-time assert is the other half of this number).
640 alignas(8) uint8_t storage_[NROS_PUBLISHER_SIZE + sizeof(void*)];
641 char topic_name_[::rclcpp::PUBLISHER_TOPIC_NAME_MAX];
642 bool initialized_;
643};
644
645} // namespace rclcpp
646
647// Phase 84.G8: out-of-line definition of Node::create_publisher<M>().
648// Placed here so consumers only pay for this template when they actually
649// use publishers — including `rclcpp/node.hpp` alone no longer drags in
650// every entity's code path.
651#include "rclcpp/node.hpp"
652
653namespace rclcpp {} // namespace rclcpp
654
655namespace rclcpp {
656template <typename M>
657Result Node::create_publisher(Publisher<M>& out, const char* topic, const ::rclcpp::QoS& qos) {
658 // RFC-0088 D5 — one image, one backend, one encoding. Compile-time, so a
659 // message the linked backend cannot encode never reaches the wire.
661 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
663 nros_cpp_ret_t ret = nros_cpp_publisher_create(&handle_, topic, M::TYPE_NAME, M::TYPE_HASH,
664 ffi_qos, out.storage_);
665 if (ret == 0) {
666 // Topic name lives C++-side; copy + null-terminate into the
667 // fixed-size buffer. Truncation is silent.
668 ::rclcpp::detail::assign_entity_name(out.topic_name_, topic);
669 out.initialized_ = true;
670 }
671 return Result(ret);
672}
673} // namespace rclcpp
674
675namespace rclcpp {
676
682} // namespace rclcpp
683
684namespace rclcpp {
685template <typename M>
686Result Node::create_publisher(Publisher<M>& out, const char* topic, const ::rclcpp::QoS& qos,
687 const ::rclcpp::PublisherOptions& options) {
688 (void)options; // reserved — no live fields today
689 return create_publisher<M>(out, topic, qos);
690}
691} // namespace rclcpp
692
693namespace rclcpp {
694
704template <typename M>
706 const QoS& qos = QoS::default_profile()) {
707 Publisher<M> p;
708 Result r = node.create_publisher<M>(p, topic, qos);
709 if (!r.ok()) return ResultOf<Publisher<M>>::error(r);
710 return ResultOf<Publisher<M>>::ok(::rclcpp::tr::forward_rvalue(p));
711}
712
713} // namespace rclcpp
714
715#endif // NROS_RCLCPP_PUBLISHER_HPP
Definition executor.hpp:119
Result count_subscribers(const char *topic_name, size_t *out_count)
Definition executor.hpp:418
Definition node.hpp:845
typename::rclcpp::Publisher< M >::SharedPtr create_publisher(const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
create_publisher<M>(topic, qos) — available on every target.
Definition publisher.hpp:326
~Loan()
Definition publisher.hpp:347
size_t capacity() const
Definition publisher.hpp:352
const uint8_t * data() const
Definition publisher.hpp:351
Loan(void *pub, uint8_t *buf, size_t cap, void *token)
Definition publisher.hpp:380
Loan(const Loan &)=delete
Result discard()
Abandon without sending. Consumes the loan.
Definition publisher.hpp:368
uint8_t * data()
Writable view of the loaned bytes.
Definition publisher.hpp:350
bool is_valid() const
Definition publisher.hpp:354
Loan(Loan &&o)
Definition publisher.hpp:329
Loan & operator=(Loan &&o)
Definition publisher.hpp:333
Loan & operator=(const Loan &)=delete
Result commit(size_t actual_len)
Send actual_len bytes. Consumes the loan.
Definition publisher.hpp:357
Loan()
Definition publisher.hpp:328
Definition publisher.hpp:178
Result assert_liveliness()
Definition publisher.hpp:596
~Publisher()
Destructor — releases publisher resources.
Definition publisher.hpp:498
::rclcpp::Owned< Publisher< M > > UniquePtr
Definition publisher.hpp:217
::rclcpp::QoS get_actual_qos() const
Definition publisher.hpp:438
Result publish_streamed(size_t total_len, W &&writer)
Definition publisher.hpp:301
Result get_gid(uint8_t(&out_gid)[GID_SIZE]) const
Definition publisher.hpp:626
const char * get_topic_name() const
Get the topic name.
Definition publisher.hpp:417
::rclcpp::ResultOf< Loan > loan(size_t requested_len)
Definition publisher.hpp:405
Result on_matched(PublisherMatchedCallbackType cb, void *user_context=nullptr)
Definition publisher.hpp:573
Result publish_serialized(const uint8_t *data, size_t len)
Definition publisher.hpp:282
Publisher & operator=(Publisher &&other)
Definition publisher.hpp:515
bool is_valid() const
Check if the publisher is initialized and valid.
Definition publisher.hpp:495
Result get_subscription_count(::rclcpp::Executor &executor, size_t *out_count) const
Definition publisher.hpp:489
Result publish_raw(const uint8_t *data, size_t len)
Renamed to publish_serialized (phase-515 C8); kept one release.
Definition publisher.hpp:289
Result publish(const M &msg)
Definition publisher.hpp:270
Publisher()
Definition publisher.hpp:533
Publisher(Publisher &&other)
Definition publisher.hpp:507
Result on_liveliness_lost(nros_cpp_publisher_count_cb_t cb, void *user_context=nullptr)
Definition publisher.hpp:546
Result on_incompatible_type(IncompatibleTypeCallbackType cb, void *user_context=nullptr)
Definition publisher.hpp:581
Result on_offered_deadline_missed(uint32_t deadline_ms, nros_cpp_publisher_count_cb_t cb, void *user_context=nullptr)
Definition publisher.hpp:556
static constexpr size_t GID_SIZE
Definition publisher.hpp:223
::rclcpp::Owned< Publisher< M > > SharedPtr
Definition publisher.hpp:202
bool is_durability_transient_local() const
Definition publisher.hpp:457
::rclcpp::Owned< Publisher< M > > ConstSharedPtr
Definition publisher.hpp:210
Definition qos.hpp:276
static constexpr QoS default_profile()
Default profile: RELIABLE + VOLATILE + KEEP_LAST(10).
Definition qos.hpp:799
int32_t raw() const
Get the raw integer code (for FFI interop).
Definition result.hpp:236
bool ok() const
Returns true if the operation succeeded.
Definition result.hpp:227
static constexpr ResultOf success()
Named constructors.
Definition result.hpp:239
Definition result.hpp:332
Inline storage-size macros for opaque entity buffers.
rclcpp::Executor — drives transport I/O and dispatches callbacks.
int nros_cpp_ret_t
Definition future.hpp:25
Definition rclcpp.hpp:418
constexpr nros_cpp_qos_t qos_to_ffi(const QoS &qos)
Definition qos.hpp:719
Definition rclcpp.hpp:75
static constexpr size_t PUBLISHER_TOPIC_NAME_MAX
Definition publisher.hpp:66
bool ok()
Definition node.hpp:3564
ResultOf< Publisher< M > > create_publisher(::rclcpp::Node &node, const char *topic, const QoS &qos=QoS::default_profile())
Definition publisher.hpp:705
@ InvalidArgument
Null pointer, empty topic name, or out-of-range value.
Definition result.hpp:122
@ NotInitialized
Definition result.hpp:131
@ TransientLocal
Definition qos.hpp:165
ResultOf< void > Result
Definition result.hpp:247
rclcpp::Node and global session helpers.
rclcpp::QoS — full DDS-shaped QoS settings (Phase 108.B.7).
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.
#define NROS_CPP_DEPRECATED_MSG(msg)
Definition result.hpp:101
rclcpp::format_of<M> — a message type's serialization format; rclcpp::linked_format() — the linked ba...
#define NROS_CPP_ASSERT_MESSAGE_FORMAT(M)
Definition serialization_format.hpp:97
Definition qos.hpp:84