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
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/entity_name.hpp" // phase-444 — the one entity-name copy
20#include "rclcpp/executor.hpp" // phase-444 — the graph counter these forward to
21#include "rclcpp/owned.hpp" // phase-456 W5 — `Publisher<M>::SharedPtr` IS `Owned<Publisher<M>>`
22#include "rclcpp/result.hpp"
23// RFC-0088 D5 — NROS_CPP_ASSERT_MESSAGE_FORMAT, expanded in the creator below.
25
26#include "rclcpp/detail/nros_cpp_ffi.h"
27
28// issue 1437 — `get_actual_qos()` returns a `rclcpp::QoS` BY VALUE from an
29// inline body, so the complete type must be here, not only by the time
30// `rclcpp/node.hpp` is pulled in below.
31//
32// AFTER `nros_cpp_ffi.h`, never before: `qos.hpp` defines the four
33// `nros_cpp_qos_*_t` enums ITSELF under `#ifndef NROS_CPP_FFI_H`, so
34// reaching it first makes the cbindgen header a REDEFINITION of all four.
35#include "rclcpp/qos.hpp"
36
37// phase-456 W5 — THIS HEADER NO LONGER DETECTS ANYTHING, and the include that
38// used to sit here is gone with the block it served.
39//
40// From phase-417 W1.a until now, `Publisher<M>::SharedPtr` and friends were
41// `std::shared_ptr` / `std::unique_ptr`, so they needed `<memory>` — which a
42// MINIMAL freestanding libcpp does not ship (Zephyr's is `<cstddef>`,
43// `<cstdint>`, `<new>`, plus this repo's `zephyr/cxx-compat/`) — and the
44// aliases were therefore ABSENT on exactly the targets ported source needs them
45// on. The predicate that decided it needed three separate corrections to get
46// right (issues 0112, 1187, 1240).
47//
48// The aliases name `rclcpp::Owned<Publisher<M>>` now, which is ours and exists
49// everywhere, so there is no capability left to probe: the alias is present
50// unconditionally and means the same thing on every target. That is RFC-0096 D1
51// — one API, no shape decided by a toolchain flag — reached by deleting the
52// question rather than by answering it more carefully.
53
54namespace rclcpp {
55
61static constexpr size_t PUBLISHER_TOPIC_NAME_MAX = 256;
62
63} // namespace rclcpp
64
72namespace rclcpp {
73class Node;
74}
75
76// ============================================================================
77// `rclcpp::Publisher<M>` -- DEFINED here (RFC-0089: rclcpp:: is the home)
78// ============================================================================
79//
80// phase-428: the definition moved from `nros::` to `rclcpp::` (the `nros::`
81// alias went in phase-483 W1). The nested `SharedPtr` / `ConstSharedPtr` / `UniquePtr`
82// aliases live on the class itself, so `rclcpp::Publisher<M>::SharedPtr` -- the
83// way rclcpp users index types -- resolves with no wrapper in between.
84namespace rclcpp {
85
140template <typename M> class Publisher {
141 public:
180
185 static constexpr size_t GID_SIZE = 24;
186
205 Result publish(const M& msg) {
206 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
207 return Result(M::ffi_publish(storage_, &msg));
208 }
209
211 Result publish_raw(const uint8_t* data, size_t len) {
212 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
213 return Result(nros_cpp_publish_raw(storage_, data, len));
214 }
215
226 template <typename W> Result publish_streamed(size_t total_len, W&& writer) {
227 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
228 struct Ctx {
229 W writer;
230 size_t total;
231 } ctx{static_cast<W&&>(writer), total_len};
232 auto size_cb = [](size_t* out_total, void* user) {
233 auto* c = static_cast<Ctx*>(user);
234 *out_total = c->total;
235 };
236 auto chunk_cb = [](uint8_t* out_buf, size_t cap, size_t* out_written, void* user) {
237 auto* c = static_cast<Ctx*>(user);
238 *out_written = c->writer(out_buf, cap);
239 };
241 static_cast<void*>(&ctx)));
242 }
243
244 // ====================================================================
245 // Phase 124.A.7 — zero-copy publish (loan / commit / discard)
246 // ====================================================================
247
251 class Loan {
252 public:
253 Loan() : pub_(nullptr), buf_(nullptr), cap_(0), token_(nullptr) {}
254 Loan(Loan&& o) : pub_(o.pub_), buf_(o.buf_), cap_(o.cap_), token_(o.token_) {
255 o.pub_ = nullptr;
256 o.token_ = nullptr;
257 }
259 if (this != &o) {
260 release();
261 pub_ = o.pub_;
262 buf_ = o.buf_;
263 cap_ = o.cap_;
264 token_ = o.token_;
265 o.pub_ = nullptr;
266 o.token_ = nullptr;
267 }
268 return *this;
269 }
270 Loan(const Loan&) = delete;
271 Loan& operator=(const Loan&) = delete;
272 ~Loan() { release(); }
273
275 uint8_t* data() { return buf_; }
276 const uint8_t* data() const { return buf_; }
277 size_t capacity() const { return cap_; }
278
279 bool is_valid() const { return token_ != nullptr; }
280
283 if (!token_) return Result(::rclcpp::ErrorCode::NotInitialized);
285 pub_ = nullptr;
286 token_ = nullptr;
287 buf_ = nullptr;
288 cap_ = 0;
289 return Result(ret);
290 }
291
294 if (!token_) return Result::success();
296 pub_ = nullptr;
297 token_ = nullptr;
298 buf_ = nullptr;
299 cap_ = 0;
300 return Result(ret);
301 }
302
305 Loan(void* pub, uint8_t* buf, size_t cap, void* token)
306 : pub_(pub), buf_(buf), cap_(cap), token_(token) {}
307
308 private:
309 void release() {
310 if (token_ && pub_) {
311 nros_cpp_publisher_discard(pub_, token_);
312 token_ = nullptr;
313 }
314 }
315
316 void* pub_;
317 uint8_t* buf_;
318 size_t cap_;
319 void* token_;
320 };
321
326 if (!initialized_)
327 return ::rclcpp::ResultOf<Loan>::error(Result(::rclcpp::ErrorCode::NotInitialized));
328 uint8_t* buf = nullptr;
329 size_t cap = 0;
330 void* token = nullptr;
332 if (ret != 0) return ::rclcpp::ResultOf<Loan>::error(Result(ret));
333 return ::rclcpp::ResultOf<Loan>::ok(Loan{storage_, buf, cap, token});
334 }
335
337 const char* get_topic_name() const { return initialized_ ? topic_name_ : ""; }
338
360 if (!initialized_ || nros_cpp_publisher_get_actual_qos(storage_, &f) != 0) {
361 return ::rclcpp::detail::qos_all_unknown();
362 }
363 return ::rclcpp::detail::qos_from_ffi(f);
364 }
365
395 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
396 return executor.count_subscribers(topic_name_, out_count);
397 }
398
400 bool is_valid() const { return initialized_; }
401
404 if (initialized_) {
406 initialized_ = false;
407 }
408 }
409
410 // Move semantics (non-copyable). Relocation goes through the
411 // `nros_cpp_publisher_relocate` runtime call.
412 Publisher(Publisher&& other) : initialized_(other.initialized_) {
413 if (other.initialized_) {
414 nros_cpp_publisher_relocate(other.storage_, storage_);
415 ::memcpy(topic_name_, other.topic_name_, sizeof(topic_name_));
416 other.initialized_ = false;
417 }
418 }
419
421 if (this != &other) {
422 if (initialized_) {
424 initialized_ = false;
425 }
426 if (other.initialized_) {
427 nros_cpp_publisher_relocate(other.storage_, storage_);
428 ::memcpy(topic_name_, other.topic_name_, sizeof(topic_name_));
429 initialized_ = true;
430 other.initialized_ = false;
431 }
432 }
433 return *this;
434 }
435
438 Publisher() : storage_(), topic_name_{}, initialized_(false) {}
439
440 // ====================================================================
441 // Phase 108 — status events
442 // ====================================================================
443
449
453 void* user_context = nullptr) {
454 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
455 return Result(nros_cpp_publisher_set_offered_deadline_missed(storage_, deadline_ms, cb,
456 user_context));
457 }
458
473
500 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
502 }
503
504 private:
505 Publisher(const Publisher&) = delete;
506 Publisher& operator=(const Publisher&) = delete;
507
508 friend class ::rclcpp::Node;
509
510 // `+ sizeof(void*)`: the monitor cell pointer the runtime stores beside
511 // the handle (`CppPublisher` in nros-cpp's publisher.rs, whose
512 // compile-time assert is the other half of this number).
513 alignas(8) uint8_t storage_[NROS_PUBLISHER_SIZE + sizeof(void*)];
514 char topic_name_[::rclcpp::PUBLISHER_TOPIC_NAME_MAX];
515 bool initialized_;
516};
517
518} // namespace rclcpp
519
520// Phase 84.G8: out-of-line definition of Node::create_publisher<M>().
521// Placed here so consumers only pay for this template when they actually
522// use publishers — including `rclcpp/node.hpp` alone no longer drags in
523// every entity's code path.
524#include "rclcpp/node.hpp"
525
526namespace rclcpp {} // namespace rclcpp
527
528namespace rclcpp {
529template <typename M>
530Result Node::create_publisher(Publisher<M>& out, const char* topic, const ::rclcpp::QoS& qos) {
531 // RFC-0088 D5 — one image, one backend, one encoding. Compile-time, so a
532 // message the linked backend cannot encode never reaches the wire.
534 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
535 nros_cpp_qos_t ffi_qos = ::rclcpp::detail::qos_to_ffi(qos);
536 nros_cpp_ret_t ret = nros_cpp_publisher_create(&handle_, topic, M::TYPE_NAME, M::TYPE_HASH,
537 ffi_qos, out.storage_);
538 if (ret == 0) {
539 // Topic name lives C++-side; copy + null-terminate into the
540 // fixed-size buffer. Truncation is silent.
541 ::rclcpp::detail::assign_entity_name(out.topic_name_, topic);
542 out.initialized_ = true;
543 }
544 return Result(ret);
545}
546} // namespace rclcpp
547
548namespace rclcpp {
549
555} // namespace rclcpp
556
557namespace rclcpp {
558template <typename M>
559Result Node::create_publisher(Publisher<M>& out, const char* topic, const ::rclcpp::QoS& qos,
560 const ::rclcpp::PublisherOptions& options) {
561 (void)options; // reserved — no live fields today
562 return create_publisher<M>(out, topic, qos);
563}
564} // namespace rclcpp
565
566namespace rclcpp {
567
577template <typename M>
578inline ResultOf<Publisher<M>> create_publisher(::rclcpp::Node& node, const char* topic,
579 const QoS& qos = QoS::default_profile()) {
580 Publisher<M> p;
581 Result r = node.create_publisher<M>(p, topic, qos);
582 if (!r.ok()) return ResultOf<Publisher<M>>::error(r);
583 return ResultOf<Publisher<M>>::ok(::rclcpp::tr::forward_rvalue(p));
584}
585
586} // namespace rclcpp
587
588#endif // NROS_RCLCPP_PUBLISHER_HPP
Definition client.hpp:129
Definition executor.hpp:116
Result count_subscribers(const char *topic_name, size_t *out_count)
Definition executor.hpp:328
Definition node.hpp:703
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:251
~Loan()
Definition publisher.hpp:272
size_t capacity() const
Definition publisher.hpp:277
const uint8_t * data() const
Definition publisher.hpp:276
Loan(void *pub, uint8_t *buf, size_t cap, void *token)
Definition publisher.hpp:305
Loan(const Loan &)=delete
Result discard()
Abandon without sending. Consumes the loan.
Definition publisher.hpp:293
uint8_t * data()
Writable view of the loaned bytes.
Definition publisher.hpp:275
bool is_valid() const
Definition publisher.hpp:279
Loan(Loan &&o)
Definition publisher.hpp:254
Loan & operator=(Loan &&o)
Definition publisher.hpp:258
Loan & operator=(const Loan &)=delete
Result commit(size_t actual_len)
Send actual_len bytes. Consumes the loan.
Definition publisher.hpp:282
Loan()
Definition publisher.hpp:253
Definition publisher.hpp:140
Result assert_liveliness()
Definition publisher.hpp:469
~Publisher()
Destructor — releases publisher resources.
Definition publisher.hpp:403
::rclcpp::QoS get_actual_qos() const
Definition publisher.hpp:358
Result publish_streamed(size_t total_len, W &&writer)
Definition publisher.hpp:226
Result get_gid(uint8_t(&out_gid)[GID_SIZE]) const
Definition publisher.hpp:499
const char * get_topic_name() const
Get the topic name.
Definition publisher.hpp:337
::rclcpp::ResultOf< Loan > loan(size_t requested_len)
Definition publisher.hpp:325
Publisher & operator=(Publisher &&other)
Definition publisher.hpp:420
bool is_valid() const
Check if the publisher is initialized and valid.
Definition publisher.hpp:400
Result get_subscription_count(::rclcpp::Executor &executor, size_t *out_count) const
Definition publisher.hpp:394
Result publish_raw(const uint8_t *data, size_t len)
Publish raw CDR bytes.
Definition publisher.hpp:211
Result publish(const M &msg)
Definition publisher.hpp:205
Publisher()
Definition publisher.hpp:438
Publisher(Publisher &&other)
Definition publisher.hpp:412
Result on_liveliness_lost(nros_cpp_publisher_count_cb_t cb, void *user_context=nullptr)
Register a callback for liveliness-lost events on this publisher.
Definition publisher.hpp:445
Result on_offered_deadline_missed(uint32_t deadline_ms, nros_cpp_publisher_count_cb_t cb, void *user_context=nullptr)
Definition publisher.hpp:452
static constexpr size_t GID_SIZE
Definition publisher.hpp:185
Definition qos.hpp:237
static constexpr QoS default_profile()
Default profile: RELIABLE + VOLATILE + KEEP_LAST(10).
Definition qos.hpp:714
Definition result.hpp:216
static constexpr ResultOf success()
Named constructors.
Definition result.hpp:238
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:21
Definition rclcpp.hpp:75
static constexpr size_t PUBLISHER_TOPIC_NAME_MAX
Definition publisher.hpp:61
ResultOf< void > Result
Definition result.hpp:246
bool ok()
Definition node.hpp:3031
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.
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:81