nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
future.hpp
Go to the documentation of this file.
1// nros-cpp: Future<T> -- single-shot deferred result
2// Freestanding C++ -- no exceptions, no STL required
3
10#ifndef NROS_CPP_FUTURE_HPP
11#define NROS_CPP_FUTURE_HPP
12
13#include <cstdint>
14#include <cstddef>
15
16#include "nros/result.hpp"
17
18// FFI declarations
19extern "C" {
20typedef int nros_cpp_ret_t;
21nros_cpp_ret_t nros_cpp_spin_once(void* handle, int32_t timeout_ms);
24uint64_t nros_cpp_time_ns(void);
25}
26
27namespace nros {
28
40template <typename T> class Future {
41 public:
43 bool is_ready() {
44 if (slot_ < 0 || !try_recv_fn_) return false;
45 if (ready_) return true;
46 uint8_t buf[T::SERIALIZED_SIZE_MAX];
47 size_t len = 0;
48 nros_cpp_ret_t ret = try_recv_fn_(client_storage_, buf, sizeof(buf), &len);
49 if (ret == 0 && len > 0) {
50 ready_ = true;
51 cached_len_ = len < sizeof(cached_buf_) ? len : sizeof(cached_buf_);
52 for (size_t i = 0; i < cached_len_; ++i)
53 cached_buf_[i] = buf[i];
54 return true;
55 }
56 return false;
57 }
58
64 if (slot_ < 0) return Result(ErrorCode::Error);
65 if (!ready_ && !is_ready()) return Result(ErrorCode::Error);
66 slot_ = -1; // consume
67 if (T::ffi_deserialize(cached_buf_, cached_len_, &out) != 0) {
69 }
70 return Result::success();
71 }
72
84 Result wait(void* executor_handle, uint32_t timeout_ms, T& out, uint32_t poll_ms = 10) {
85 if (slot_ < 0) return Result(ErrorCode::Error);
86 if (poll_ms == 0) poll_ms = 1;
87 // Phase 89.2: budget by wall-clock. Accumulating `step` per iteration
88 // breaks when the underlying `zpico_spin_once` returns early on a
89 // signaled condvar (keep-alives, discovery gossip) — the 500-step
90 // default-timeout loop can then collapse into milliseconds and
91 // return ErrorCode::Timeout before the reply has a chance to land.
93 const uint64_t budget_ns = static_cast<uint64_t>(timeout_ms) * 1000000ULL;
94 while (true) {
95 nros_cpp_ret_t ret = nros_cpp_spin_once(executor_handle, static_cast<int32_t>(poll_ms));
96 // Transient conditions: keep polling. Anything else propagates.
97 // - Ok (0): nothing to dispatch this round.
98 // - Timeout (-2): spin_once returned after its timeout — normal.
99 // - TryAgain (-6): transport hint to retry.
100 if (ret != 0 && ret != static_cast<nros_cpp_ret_t>(ErrorCode::Timeout) &&
101 ret != static_cast<nros_cpp_ret_t>(ErrorCode::TryAgain)) {
102 return Result(ret);
103 }
104 if (is_ready()) return try_take(out);
106 if (now_ns - start_ns >= budget_ns) break;
107 }
109 }
110
112 void cancel() { slot_ = -1; }
113
115 bool is_consumed() const { return slot_ < 0; }
116
117 // Move semantics (non-copyable, single-shot)
119 : client_storage_(other.client_storage_), try_recv_fn_(other.try_recv_fn_),
120 slot_(other.slot_), ready_(other.ready_), cached_len_(other.cached_len_) {
121 for (size_t i = 0; i < cached_len_; ++i)
122 cached_buf_[i] = other.cached_buf_[i];
123 other.slot_ = -1;
124 other.ready_ = false;
125 }
126
128 if (this != &other) {
129 client_storage_ = other.client_storage_;
130 try_recv_fn_ = other.try_recv_fn_;
131 slot_ = other.slot_;
132 ready_ = other.ready_;
133 cached_len_ = other.cached_len_;
134 for (size_t i = 0; i < cached_len_; ++i)
135 cached_buf_[i] = other.cached_buf_[i];
136 other.slot_ = -1;
137 other.ready_ = false;
138 }
139 return *this;
140 }
141
143
146 : client_storage_(nullptr), try_recv_fn_(nullptr), slot_(-1), ready_(false),
147 cached_len_(0) {}
148
149 private:
150 Future(const Future&) = delete;
151 Future& operator=(const Future&) = delete;
152
153 template <typename S> friend class Client;
154 template <typename A> friend class ActionClient;
155
156 using TryRecvFn = nros_cpp_ret_t (*)(void*, uint8_t*, size_t, size_t*);
157
158 Future(void* storage, TryRecvFn fn, int slot)
159 : client_storage_(storage), try_recv_fn_(fn), slot_(slot), ready_(false), cached_len_(0) {}
160
161 void* client_storage_;
162 TryRecvFn try_recv_fn_;
163 int slot_;
164 bool ready_;
165 size_t cached_len_;
166 uint8_t cached_buf_[T::SERIALIZED_SIZE_MAX];
167};
168
169} // namespace nros
170#endif // NROS_CPP_FUTURE_HPP
Definition action_client.hpp:67
Definition client.hpp:54
Definition future.hpp:40
Future & operator=(Future &&other) noexcept
Definition future.hpp:127
Result try_take(T &out)
Definition future.hpp:63
void cancel()
Cancel the pending operation (idempotent).
Definition future.hpp:112
bool is_ready()
Check if the result has arrived (non-blocking).
Definition future.hpp:43
Result wait(void *executor_handle, uint32_t timeout_ms, T &out, uint32_t poll_ms=10)
Definition future.hpp:84
bool is_consumed() const
Check if the future has been consumed or cancelled.
Definition future.hpp:115
Future(Future &&other) noexcept
Definition future.hpp:118
~Future()
Definition future.hpp:142
Future()
Default constructor – creates an empty/consumed future.
Definition future.hpp:145
Definition result.hpp:52
static constexpr Result success()
Named constructors.
Definition result.hpp:74
int nros_cpp_ret_t
Definition future.hpp:20
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)
Definition nros.hpp:42
@ 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.