nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
stream.hpp
Go to the documentation of this file.
1// nros-cpp: Stream<T> -- multi-shot message receiver
2// Freestanding C++ -- no exceptions, no STL required
3
10#ifndef NROS_CPP_STREAM_HPP
11#define NROS_CPP_STREAM_HPP
12
13#include <cstdint>
14#include <cstddef>
15
16#include "nros/result.hpp"
17#include "nros/size_bound.hpp" // nros::rx_buffer_capacity<T> — the receive-buffer size
18
19// FFI declarations
20extern "C" {
21typedef int nros_cpp_ret_t;
22nros_cpp_ret_t nros_cpp_spin_once(void* handle, int32_t timeout_ms);
26uint64_t nros_cpp_time_ns(void);
27}
28
29namespace nros {
30
43template <typename T> class Stream {
44 public:
53 Result try_next(T& out) { return try_next_sized<::nros::rx_buffer_capacity<T>::value>(out); }
54
57 template <size_t Cap> Result try_next_sized(T& out) {
58 if (!take_fn_) return Result(ErrorCode::NotInitialized);
59 uint8_t buf[Cap];
60 size_t len = 0;
61 nros_cpp_ret_t ret = take_fn_(storage_, buf, sizeof(buf), &len);
62 if (ret != 0) return Result(ret);
63 if (len == 0) return Result(ErrorCode::TryAgain);
64 if (T::ffi_deserialize(buf, len, &out) != 0) return Result(ErrorCode::Error);
65 return Result::success();
66 }
67
78 Result wait_next(void* executor_handle, uint32_t timeout_ms, T& out, uint32_t poll_ms = 10) {
79 return wait_next_sized<::nros::rx_buffer_capacity<T>::value>(executor_handle, timeout_ms,
80 out, poll_ms);
81 }
82
85 template <size_t Cap>
86 Result wait_next_sized(void* executor_handle, uint32_t timeout_ms, T& out,
87 uint32_t poll_ms = 10) {
88 if (!take_fn_) return Result(ErrorCode::NotInitialized);
89 if (poll_ms == 0) poll_ms = 1;
90 // Phase 118.C: budget by wall-clock. Accumulating `step` per
91 // iteration breaks when `zpico_spin_once` returns early on a
92 // signaled condvar (keep-alives, discovery gossip) — the
93 // iteration loop collapses into milliseconds and returns
94 // Timeout before the message has a chance to land. Mirrors the
95 // Phase 89.2 fix on Future::wait().
96 const uint64_t start_ns = nros_cpp_time_ns();
97 const uint64_t budget_ns = static_cast<uint64_t>(timeout_ms) * 1000000ULL;
98 while (true) {
99 nros_cpp_ret_t ret = nros_cpp_spin_once(executor_handle, static_cast<int32_t>(poll_ms));
100 // Transient conditions: keep polling. Anything else propagates.
101 if (ret != 0 && ret != static_cast<nros_cpp_ret_t>(ErrorCode::Timeout) &&
102 ret != static_cast<nros_cpp_ret_t>(ErrorCode::TryAgain)) {
103 return Result(ret);
104 }
105 Result rn = try_next_sized<Cap>(out);
106 if (rn.ok()) return Result::success();
107 // TryAgain / NotInitialized / Error from try_next: keep polling
108 // unless we've hit a hard error that's not "no data yet".
110 return rn;
111 }
112 const uint64_t now_ns = nros_cpp_time_ns();
113 if (now_ns - start_ns >= budget_ns) break;
114 }
116 }
117
119 bool is_valid() const { return take_fn_ != nullptr; }
120
121 // Move semantics (non-copyable)
122 Stream(Stream&& other) noexcept : storage_(other.storage_), take_fn_(other.take_fn_) {
123 other.storage_ = nullptr;
124 other.take_fn_ = nullptr;
125 }
126
127 Stream& operator=(Stream&& other) noexcept {
128 if (this != &other) {
129 storage_ = other.storage_;
130 take_fn_ = other.take_fn_;
131 other.storage_ = nullptr;
132 other.take_fn_ = nullptr;
133 }
134 return *this;
135 }
136
138 Stream() : storage_(nullptr), take_fn_(nullptr) {}
139
140 private:
141 Stream(const Stream&) = delete;
142 Stream& operator=(const Stream&) = delete;
143
144 template <typename M> friend class Subscription;
145 template <typename A> friend class ActionClient;
146
147 using TakeFn = nros_cpp_ret_t (*)(void*, uint8_t*, size_t, size_t*);
148
149 Stream(void* storage, TakeFn fn) : storage_(storage), take_fn_(fn) {}
150
151 void bind(void* storage, TakeFn fn) {
152 storage_ = storage;
153 take_fn_ = fn;
154 }
155
156 void* storage_;
157 TakeFn take_fn_;
158};
159
160} // namespace nros
161#endif // NROS_CPP_STREAM_HPP
Definition action_client.hpp:73
Definition result.hpp:90
static constexpr Result success()
Named constructors.
Definition result.hpp:112
ErrorCode code() const
Get the underlying error code.
Definition result.hpp:106
bool ok() const
Returns true if the operation succeeded.
Definition result.hpp:100
Definition stream.hpp:43
Result wait_next(void *executor_handle, uint32_t timeout_ms, T &out, uint32_t poll_ms=10)
Definition stream.hpp:78
bool is_valid() const
Check if the stream is connected to a valid source.
Definition stream.hpp:119
Stream(Stream &&other) noexcept
Definition stream.hpp:122
Stream & operator=(Stream &&other) noexcept
Definition stream.hpp:127
Stream()
Default constructor – creates an unbound stream.
Definition stream.hpp:138
Result try_next_sized(T &out)
Definition stream.hpp:57
Result wait_next_sized(void *executor_handle, uint32_t timeout_ms, T &out, uint32_t poll_ms=10)
Definition stream.hpp:86
Result try_next(T &out)
Definition stream.hpp:53
Definition subscription.hpp:100
int nros_cpp_ret_t
Definition future.hpp:21
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)
Definition nros.hpp:55
@ Error
Generic failure not covered by a more specific code.
@ Timeout
Operation deadline elapsed before completion.
@ TryAgain
Transient — no data ready yet (non-blocking take). Retry later.
nros::Result, nros::ErrorCode, and the NROS_TRY macro.
int nros_cpp_ret_t
Definition stream.hpp:21
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)