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_CPP_PUBLISHER_HPP
11#define NROS_CPP_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 "nros/config.hpp"
18#include "nros/result.hpp"
19// RFC-0088 D5 — NROS_CPP_ASSERT_MESSAGE_FORMAT, expanded in the creator below.
21
22#include "nros_cpp_ffi.h"
23
24namespace nros {
25
28static constexpr size_t PUBLISHER_TOPIC_NAME_MAX = 256;
29
48template <typename M> class Publisher {
49 public:
54 Result publish(const M& msg) { return Result(M::ffi_publish(storage_, &msg)); }
55
57 Result publish_raw(const uint8_t* data, size_t len) {
58 if (!initialized_) return Result(ErrorCode::NotInitialized);
59 return Result(nros_cpp_publish_raw(storage_, data, len));
60 }
61
72 template <typename W> Result publish_streamed(size_t total_len, W&& writer) {
73 if (!initialized_) return Result(ErrorCode::NotInitialized);
74 struct Ctx {
75 W writer;
76 size_t total;
77 } ctx{static_cast<W&&>(writer), total_len};
78 auto size_cb = [](size_t* out_total, void* user) {
79 auto* c = static_cast<Ctx*>(user);
80 *out_total = c->total;
81 };
82 auto chunk_cb = [](uint8_t* out_buf, size_t cap, size_t* out_written, void* user) {
83 auto* c = static_cast<Ctx*>(user);
84 *out_written = c->writer(out_buf, cap);
85 };
86 return Result(nros_cpp_publisher_publish_streamed(storage_, size_cb, chunk_cb,
87 static_cast<void*>(&ctx)));
88 }
89
90 // ====================================================================
91 // Phase 124.A.7 — zero-copy publish (loan / commit / discard)
92 // ====================================================================
93
97 class Loan {
98 public:
99 Loan() : pub_(nullptr), buf_(nullptr), cap_(0), token_(nullptr) {}
100 Loan(Loan&& o) : pub_(o.pub_), buf_(o.buf_), cap_(o.cap_), token_(o.token_) {
101 o.pub_ = nullptr;
102 o.token_ = nullptr;
103 }
105 if (this != &o) {
106 release();
107 pub_ = o.pub_;
108 buf_ = o.buf_;
109 cap_ = o.cap_;
110 token_ = o.token_;
111 o.pub_ = nullptr;
112 o.token_ = nullptr;
113 }
114 return *this;
115 }
116 Loan(const Loan&) = delete;
117 Loan& operator=(const Loan&) = delete;
118 ~Loan() { release(); }
119
121 uint8_t* data() { return buf_; }
122 const uint8_t* data() const { return buf_; }
123 size_t capacity() const { return cap_; }
124
125 bool is_valid() const { return token_ != nullptr; }
126
128 Result commit(size_t actual_len) {
129 if (!token_) return Result(ErrorCode::NotInitialized);
130 nros_cpp_ret_t ret = nros_cpp_publisher_commit(pub_, token_, actual_len);
131 pub_ = nullptr;
132 token_ = nullptr;
133 buf_ = nullptr;
134 cap_ = 0;
135 return Result(ret);
136 }
137
140 if (!token_) return Result::success();
141 nros_cpp_ret_t ret = nros_cpp_publisher_discard(pub_, token_);
142 pub_ = nullptr;
143 token_ = nullptr;
144 buf_ = nullptr;
145 cap_ = 0;
146 return Result(ret);
147 }
148
151 Loan(void* pub, uint8_t* buf, size_t cap, void* token)
152 : pub_(pub), buf_(buf), cap_(cap), token_(token) {}
153
154 private:
155 void release() {
156 if (token_ && pub_) {
157 nros_cpp_publisher_discard(pub_, token_);
158 token_ = nullptr;
159 }
160 }
161
162 void* pub_;
163 uint8_t* buf_;
164 size_t cap_;
165 void* token_;
166 };
167
171 Expected<Loan> loan(size_t requested_len) {
172 if (!initialized_) return Expected<Loan>::error(Result(ErrorCode::NotInitialized));
173 uint8_t* buf = nullptr;
174 size_t cap = 0;
175 void* token = nullptr;
176 nros_cpp_ret_t ret = nros_cpp_publisher_loan(storage_, requested_len, &buf, &cap, &token);
177 if (ret != 0) return Expected<Loan>::error(Result(ret));
178 return Expected<Loan>::ok(Loan{storage_, buf, cap, token});
179 }
180
182 const char* get_topic_name() const { return initialized_ ? topic_name_ : ""; }
183
185 bool is_valid() const { return initialized_; }
186
189 if (initialized_) {
190 nros_cpp_publisher_destroy(storage_);
191 initialized_ = false;
192 }
193 }
194
195 // Move semantics (non-copyable). Relocation goes through the
196 // `nros_cpp_publisher_relocate` runtime call.
197 Publisher(Publisher&& other) : initialized_(other.initialized_) {
198 if (other.initialized_) {
199 nros_cpp_publisher_relocate(other.storage_, storage_);
200 ::memcpy(topic_name_, other.topic_name_, sizeof(topic_name_));
201 other.initialized_ = false;
202 }
203 }
204
206 if (this != &other) {
207 if (initialized_) {
208 nros_cpp_publisher_destroy(storage_);
209 initialized_ = false;
210 }
211 if (other.initialized_) {
212 nros_cpp_publisher_relocate(other.storage_, storage_);
213 ::memcpy(topic_name_, other.topic_name_, sizeof(topic_name_));
214 initialized_ = true;
215 other.initialized_ = false;
216 }
217 }
218 return *this;
219 }
220
223 Publisher() : storage_(), topic_name_{}, initialized_(false) {}
224
225 // ====================================================================
226 // Phase 108 — status events
227 // ====================================================================
228
230 Result on_liveliness_lost(nros_cpp_publisher_count_cb_t cb, void* user_context = nullptr) {
231 if (!initialized_) return Result(ErrorCode::NotInitialized);
232 return Result(nros_cpp_publisher_set_liveliness_lost(storage_, cb, user_context));
233 }
234
237 Result on_offered_deadline_missed(uint32_t deadline_ms, nros_cpp_publisher_count_cb_t cb,
238 void* user_context = nullptr) {
239 if (!initialized_) return Result(ErrorCode::NotInitialized);
240 return Result(nros_cpp_publisher_set_offered_deadline_missed(storage_, deadline_ms, cb,
241 user_context));
242 }
243
248 if (!initialized_) return Result(ErrorCode::NotInitialized);
249 return Result(nros_cpp_publisher_assert_liveliness(storage_));
250 }
251
252 private:
253 Publisher(const Publisher&) = delete;
254 Publisher& operator=(const Publisher&) = delete;
255
256 friend class Node;
257
258 alignas(8) uint8_t storage_[NROS_PUBLISHER_SIZE];
259 char topic_name_[PUBLISHER_TOPIC_NAME_MAX];
260 bool initialized_;
261};
262
263} // namespace nros
264
265// Phase 84.G8: out-of-line definition of Node::create_publisher<M>().
266// Placed here so consumers only pay for this template when they actually
267// use publishers — including `nros/node.hpp` alone no longer drags in
268// every entity's code path.
269#include "nros/node.hpp"
270
271namespace nros {
272
273template <typename M>
274Result Node::create_publisher(Publisher<M>& out, const char* topic, const QoS& qos) {
275 // RFC-0088 D5 — one image, one backend, one encoding. Compile-time, so a
276 // message the linked backend cannot encode never reaches the wire.
278 if (!initialized_) return Result(ErrorCode::NotInitialized);
279 nros_cpp_qos_t ffi_qos = detail::qos_to_ffi(qos);
280 nros_cpp_ret_t ret = nros_cpp_publisher_create(&handle_, topic, M::TYPE_NAME, M::TYPE_HASH,
281 ffi_qos, out.storage_);
282 if (ret == 0) {
283 // Topic name lives C++-side; copy + null-terminate into the
284 // fixed-size buffer. Truncation is silent.
285 size_t topic_len = 0;
286 while (topic[topic_len] != '\0' && topic_len + 1 < sizeof(out.topic_name_)) {
287 out.topic_name_[topic_len] = topic[topic_len];
288 ++topic_len;
289 }
290 out.topic_name_[topic_len] = '\0';
291 out.initialized_ = true;
292 }
293 return Result(ret);
294}
295
301template <typename M>
302Result Node::create_publisher(Publisher<M>& out, const char* topic, const QoS& qos,
303 const PublisherOptions& options) {
304 (void)options; // reserved — no live fields today
305 return create_publisher<M>(out, topic, qos);
306}
307
313template <typename M>
314inline Expected<Publisher<M>> create_publisher(Node& node, const char* topic,
315 const QoS& qos = QoS::default_profile()) {
316 Publisher<M> p;
317 Result r = node.create_publisher<M>(p, topic, qos);
318 if (!r.ok()) return Expected<Publisher<M>>::error(r);
319 return Expected<Publisher<M>>::ok(std::move(p));
320}
321
322} // namespace nros
323
324#endif // NROS_CPP_PUBLISHER_HPP
Definition result.hpp:198
ErrorCode error() const
Definition result.hpp:221
bool ok() const
Definition result.hpp:214
Definition node.hpp:211
Result create_publisher(Publisher< M > &out, const char *topic, const QoS &qos=QoS::default_profile())
Definition publisher.hpp:274
Definition publisher.hpp:97
Result commit(size_t actual_len)
Send actual_len bytes. Consumes the loan.
Definition publisher.hpp:128
Loan(Loan &&o)
Definition publisher.hpp:100
Loan(void *pub, uint8_t *buf, size_t cap, void *token)
Definition publisher.hpp:151
const uint8_t * data() const
Definition publisher.hpp:122
~Loan()
Definition publisher.hpp:118
Loan()
Definition publisher.hpp:99
Loan & operator=(const Loan &)=delete
size_t capacity() const
Definition publisher.hpp:123
Loan & operator=(Loan &&o)
Definition publisher.hpp:104
Result discard()
Abandon without sending. Consumes the loan.
Definition publisher.hpp:139
Loan(const Loan &)=delete
bool is_valid() const
Definition publisher.hpp:125
uint8_t * data()
Writable view of the loaned bytes.
Definition publisher.hpp:121
Definition publisher.hpp:48
Expected< Loan > loan(size_t requested_len)
Definition publisher.hpp:171
Result publish_streamed(size_t total_len, W &&writer)
Definition publisher.hpp:72
Publisher()
Definition publisher.hpp:223
Result publish_raw(const uint8_t *data, size_t len)
Publish raw CDR bytes.
Definition publisher.hpp:57
const char * get_topic_name() const
Get the topic name.
Definition publisher.hpp:182
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:230
Publisher(Publisher &&other)
Definition publisher.hpp:197
Result publish(const M &msg)
Definition publisher.hpp:54
~Publisher()
Destructor — releases publisher resources.
Definition publisher.hpp:188
Result on_offered_deadline_missed(uint32_t deadline_ms, nros_cpp_publisher_count_cb_t cb, void *user_context=nullptr)
Definition publisher.hpp:237
bool is_valid() const
Check if the publisher is initialized and valid.
Definition publisher.hpp:185
Result assert_liveliness()
Definition publisher.hpp:247
Publisher & operator=(Publisher &&other)
Definition publisher.hpp:205
Definition qos.hpp:173
static constexpr QoS default_profile()
Default profile: RELIABLE + VOLATILE + KEEP_LAST(10).
Definition qos.hpp:328
Definition result.hpp:90
static constexpr Result success()
Named constructors.
Definition result.hpp:112
bool ok() const
Returns true if the operation succeeded.
Definition result.hpp:100
Inline storage-size macros for opaque entity buffers.
int nros_cpp_ret_t
Definition future.hpp:21
Definition nros.hpp:55
static constexpr size_t PUBLISHER_TOPIC_NAME_MAX
Definition publisher.hpp:28
bool ok()
Check if the nros session is initialized.
Definition node.hpp:997
nros::Node and global session helpers.
nros::Result, nros::ErrorCode, and the NROS_TRY macro.
nros::format_of<M> — a message type's serialization format; nros::linked_format() — the linked backen...
#define NROS_CPP_ASSERT_MESSAGE_FORMAT(M)
Definition serialization_format.hpp:97
Definition qos.hpp:54