nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
rclcpp.hpp
Go to the documentation of this file.
1// nros-cpp: Umbrella header
2// Include this single header to get the full nros C++ API.
3//
4// Freestanding C++ compatible — no STL, no exceptions, no RTTI required.
5
11
12#ifndef NROS_RCLCPP_RCLCPP_HPP
13#define NROS_RCLCPP_RCLCPP_HPP
14
15// Phase 118.D — pull cbindgen-generated FFI before any wrapper hpp so
16// `rclcpp/qos.hpp`'s `#ifndef NROS_CPP_FFI_H` guard skips its local
17// fallback definitions in favor of the canonical types.
18#include "rclcpp/detail/nros_cpp_ffi.h"
19
20#include "rclcpp/detail/traits.hpp"
21#include "rclcpp/logging.hpp"
22#include "rclcpp/result.hpp"
23// Issue 0789 — the clock / time / duration surface. `node.now()` and
24// `node.get_clock()->now()` are what a ported rclcpp publisher calls to
25// stamp a header, so the umbrella carries all three. Phase 379 W5 moved
26// `duration.hpp` ABOVE `qos.hpp`: the QoS deadline / lifespan / lease
27// accessors take and return `rclcpp::Duration`. `qos.hpp` also includes it
28// directly, so this order is legibility rather than load-bearing.
29#include "rclcpp/duration.hpp"
30#include "rclcpp/time.hpp"
31#include "rclcpp/clock.hpp"
32#include "rclcpp/qos.hpp"
33#include "rclcpp/options.hpp"
34#include "rclcpp/future.hpp"
35#include "rclcpp/stream.hpp"
36// RFC-0088 D5 — rclcpp::SerializationFormat / format_of<M> / linked_format().
38// Phase 84.G8: node.hpp no longer pulls in the heavy entity headers —
39// each entity header carries its own out-of-line `Node::create_X<>()`
40// template definition. The umbrella pulls in every entity explicitly so
41// `#include <rclcpp/rclcpp.hpp>` still yields the full API.
42#include "rclcpp/node.hpp"
43#include "rclcpp/publisher.hpp"
45#include "rclcpp/generic.hpp" // phase-515 P4
46#include "rclcpp/service.hpp"
47#include "rclcpp/client.hpp"
50#include "rclcpp/polling_action_server.hpp"
51#include "rclcpp/polling_action_client.hpp"
52#include "rclcpp/polling_client.hpp"
53#include "rclcpp/polling_service.hpp"
54#include "rclcpp/polling_subscription.hpp"
55#include "rclcpp/parameter.hpp"
56// phase-426 W4 — the ONE parameter facade. Forwards a node's
57// declare/get/set/has onto the executor's store across the FFI, so a parameter
58// means the same thing wherever it is declared. There is now one node type to
59// wear it (phase-427 W4), which is the other half of that guarantee.
60// Freestanding: `<string>`/`<vector>` behind `NROS_CPP_STD`.
61#include "rclcpp/node_parameters.hpp"
62#include "rclcpp_lifecycle/lifecycle_node.hpp"
63#include "rclcpp/rosout.hpp" // issue 1589 — `/rosout` from C++
64// phase-427 W4 — `nros/component_node.hpp` IS GONE. `nros::ComponentNode` was a
65// type that WRAPPED a node (RFC-0044 Q1's "wrap, not derive"), so a component
66// was not a `Node` and every verb had to be forwarded. Its members are on
67// `rclcpp::Node` now, its pool is the opt-in `rclcpp::NodeWithTimers<N>`, and its
68// macros are in `component.hpp` below. The rclcpp-shaped, value-returning
69// parameter facade phase-417 W2.b pulled in here for is `Node`'s own.
70// phase-417 stage 6 step A — `rclcpp::create_subscription_raw`, the
71// arena-registration entry point `rclcpp::Node::create_subscription`
72// below uses so it has no dispatch of its own.
73#include "rclcpp/detail/component.hpp"
74
75namespace rclcpp {
76
88inline void* global_handle();
89
97inline Result spin_once(int32_t timeout_ms = 10) {
98 if (!::rclcpp::Node::global_initialized()) {
100 }
101 return Result(nros_cpp_spin_once(::rclcpp::Node::global_storage(), timeout_ms));
102}
103
113inline void arm_monitors() {
114 nros_cpp_monitors_arm();
115}
116
137inline Result pre_shutdown(ShutdownCallback callback, void* context = nullptr,
138 PreShutdownCallbackHandle* out = nullptr) {
139 void* executor = global_handle();
140 if (executor == nullptr) {
142 }
143 nros_cpp_shutdown_callback_handle_t raw = NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID;
144 nros_cpp_ret_t ret = nros_cpp_add_pre_shutdown_callback(executor, callback, context, &raw);
145 if (out != nullptr) *out = PreShutdownCallbackHandle(raw);
146 return Result(ret);
147}
148
156inline Result on_shutdown(ShutdownCallback callback, void* context = nullptr,
157 OnShutdownCallbackHandle* out = nullptr) {
158 void* executor = global_handle();
159 if (executor == nullptr) {
161 }
162 nros_cpp_shutdown_callback_handle_t raw = NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID;
163 nros_cpp_ret_t ret = nros_cpp_add_on_shutdown_callback(executor, callback, context, &raw);
164 if (out != nullptr) *out = OnShutdownCallbackHandle(raw);
165 return Result(ret);
166}
167
171 void* executor = global_handle();
172 if (executor == nullptr) return false;
173 return nros_cpp_remove_pre_shutdown_callback(executor, handle.value()) == 0;
174}
175
179 void* executor = global_handle();
180 if (executor == nullptr) return false;
181 return nros_cpp_remove_on_shutdown_callback(executor, handle.value()) == 0;
182}
183
195class Context {
196 public:
198 Context() : executor_(nullptr) {}
199 explicit Context(void* executor) : executor_(executor) {}
200 Context* operator->() { return this; }
201 const Context* operator->() const { return this; }
203 void* executor_handle() const { return executor_; }
205 bool is_valid() const { return executor_ != nullptr && ::rclcpp::ok(); }
206
209 PreShutdownCallbackHandle* out = nullptr) {
210 if (executor_ == nullptr) return Result(ErrorCode::NotInitialized);
211 nros_cpp_shutdown_callback_handle_t raw = NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID;
212 nros_cpp_ret_t ret = nros_cpp_add_pre_shutdown_callback(executor_, callback, context, &raw);
213 if (out != nullptr) *out = PreShutdownCallbackHandle(raw);
214 return Result(ret);
215 }
216
218 OnShutdownCallbackHandle* out = nullptr) {
219 if (executor_ == nullptr) return Result(ErrorCode::NotInitialized);
220 nros_cpp_shutdown_callback_handle_t raw = NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID;
221 nros_cpp_ret_t ret = nros_cpp_add_on_shutdown_callback(executor_, callback, context, &raw);
222 if (out != nullptr) *out = OnShutdownCallbackHandle(raw);
223 return Result(ret);
224 }
225
227 if (executor_ == nullptr) return false;
228 const bool removed = nros_cpp_remove_pre_shutdown_callback(executor_, handle.value()) == 0;
229#if defined(NROS_CPP_STD) // hosted-family: shutdown-interop
230 if (removed) release_closure(handle.value());
231#endif
232 return removed;
233 }
234
236 if (executor_ == nullptr) return false;
237 const bool removed = nros_cpp_remove_on_shutdown_callback(executor_, handle.value()) == 0;
238#if defined(NROS_CPP_STD) // hosted-family: shutdown-interop
239 if (removed) release_closure(handle.value());
240#endif
241 return removed;
242 }
243
244#if defined(NROS_CPP_STD) // hosted-family: shutdown-interop
247 PreShutdownCallbackHandle add_pre_shutdown_callback(::std::function<void()> callback) {
248 PreShutdownCallbackHandle h(NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID);
249 auto* fn = new ::std::function<void()>(static_cast<::std::function<void()>&&>(callback));
250 if (!add_pre_shutdown_callback(&Context::run_closure, fn, &h).ok()) {
251 delete fn;
252 return PreShutdownCallbackHandle(NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID);
253 }
254 closures()[h.value()].reset(fn);
255 return h;
256 }
258 OnShutdownCallbackHandle add_on_shutdown_callback(::std::function<void()> callback) {
259 OnShutdownCallbackHandle h(NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID);
260 auto* fn = new ::std::function<void()>(static_cast<::std::function<void()>&&>(callback));
261 if (!add_on_shutdown_callback(&Context::run_closure, fn, &h).ok()) {
262 delete fn;
263 return OnShutdownCallbackHandle(NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID);
264 }
265 closures()[h.value()].reset(fn);
266 return h;
267 }
268
269 private:
270 static void run_closure(void* fn) {
271 if (fn != nullptr) (*static_cast<::std::function<void()>*>(fn))();
272 }
273 using ClosureMap =
274 ::std::map<nros_cpp_shutdown_callback_handle_t, ::std::unique_ptr<::std::function<void()>>>;
275 static ClosureMap& closures() {
276 static ClosureMap m;
277 return m;
278 }
279 static void release_closure(nros_cpp_shutdown_callback_handle_t h) { closures().erase(h); }
280#else
281 private:
282#endif
283 void* executor_;
284};
285
286namespace contexts {
292} // namespace contexts
293
294// --- Clock::sleep_until / sleep_for (phase-515 P5) ---------------------------
295//
296// One call into `nros_cpp_spin_until_clock`, which owns the loop Rust-side, as
297// `Rate::sleep` makes one call into `nros_cpp_spin_for`: a wrapper that looped
298// over `spin_once` here would be RFC-0020 violation class 2.
299inline bool Clock::sleep_until(Time until, Context context) {
300 if (until.get_clock_type() != get_clock_type()) return false;
301 void* executor = context.executor_handle();
302 if (executor == nullptr || !::rclcpp::ok()) return false;
303 const nros_cpp_ret_t ret =
304 nros_cpp_spin_until_clock(executor, &clock_, until.nanoseconds(), 10);
305 return ret == NROS_CPP_RET_OK && now() >= until;
306}
307inline bool Clock::sleep_until(Time until) {
308 return sleep_until(until, contexts::get_global_default_context());
309}
310inline bool Clock::sleep_for(Duration rel_time, Context context) {
311 return sleep_until(now() + rel_time, context);
312}
313inline bool Clock::sleep_for(Duration rel_time) {
314 return sleep_for(rel_time, contexts::get_global_default_context());
315}
316
318template <int Tag> inline Context NodeInterfaceHandle<Tag>::get_context() const {
319 return Context(node_ != nullptr ? node_->executor_handle() : nullptr);
320}
321} // namespace node_interfaces
322
331inline Result spin_in() {
332 if (!::rclcpp::Node::global_initialized()) {
334 }
335 Result last = Result::success();
336 while (ok()) {
337 // phase-521 W6 — the executor's entry budget, not a fixed 10 ms.
338 void* storage = ::rclcpp::Node::global_storage();
339 last = Result(nros_cpp_spin_once(storage, nros_cpp_entry_spin_budget_ms(storage)));
340 if (!last.ok()) return last;
341 }
342 return last;
343}
344
353inline Result spin_in(uint32_t duration_ms, int32_t poll_ms = 10) {
354 if (!::rclcpp::Node::global_initialized()) {
356 }
357 // Issue 0329 — forward to the single budgeted-spin CFFI entry point. This
358 // loop previously budgeted by ITERATION count (`elapsed += timeout`), which
359 // collapsed to milliseconds when `spin_once` returned early on a signaled
360 // wake — the exact bug `Executor::spin` fixed in Phase 118.C. The correct
361 // wall-clock budget now lives once, Rust-side, in `nros_cpp_spin_for`.
362 return Result(nros_cpp_spin_for(::rclcpp::Node::global_storage(), duration_ms, poll_ms));
363}
364
365} // namespace rclcpp
366
367// ============================================================================
368// rclcpp:: — the process-level surface (RFC-0089 stage 6, step A)
369// ============================================================================
370//
371// `rclcpp::init` / `shutdown` / `ok` / `Node` / `spin` / `spin_some` / `Rate` /
372// `spin_until_future_complete` used to live in `nros/rclcpp_compat.hpp`, a
373// separate header a ported file had to be force-included with. RFC-0089
374// §"Naming: replace, with alias as the migration step" makes the ROS 2 spelling
375// a FIRST-CLASS name declared by the API headers themselves; §"End state: no
376// compat layer survives" is what follows from it — with one spelling per
377// concept there is nothing left for a shim to bridge, so it dissolves by
378// construction rather than having to be argued away. Step B — retiring the
379// in-tree `nros::` spelling — landed in phase-483 W1: `rclcpp::` is now the only
380// namespace.
381//
382// They land HERE, in the umbrella, and not in `node.hpp`, because that is where
383// their dependencies are. `rclcpp::Node` reaches `rclcpp::create_subscription_raw`
384// (`component.hpp`), `rclcpp::Timer`, `rclcpp::Service` and `rclcpp::Client`,
385// every one of which includes `node.hpp` itself; `Rate::sleep` and
386// `spin_until_future_complete` drive the `rclcpp::spin` / `rclcpp::spin_once`
387// free functions defined a few lines above. This IS the header they were
388// shimming: the process-level verbs — `rclcpp::init`, `rclcpp::spin`,
389// `rclcpp::spin_once`, `rclcpp::on_shutdown` — are all reached through
390// `<rclcpp/rclcpp.hpp>`.
391//
392// FREESTANDING. Unlike the shim it replaces, this is not hosted-STL by
393// construction. `init` / `shutdown` / `ok` and the `--ros-args` predicate need
394// nothing but `<cstdlib>`, so they reach a `no_std` C++ build. `Node`, the spin
395// forwarders and `Rate` are gated on the standard-library pieces their
396// signatures are spelled in (`__has_include`, never `__STDC_HOSTED__` — issue
397// 0112, rationale in `publisher.hpp`), so on a minimal libcpp they are absent
398// rather than a parse error.
399
400#include <cstdlib> // std::abort -- the runtime half of RFC-0089 W3.b
401#include <type_traits> // the SFINAE guards on create_service / create_client
402
403namespace rclcpp {
404
405// --- Process-level lifecycle -------------------------------------------------
406//
407// `rclcpp::init()` / `rclcpp::shutdown()` / `rclcpp::ok()` are the
408// process-level handshake (nros tracks the shutdown flag).
409//
410// The TWO-ARGUMENT form is REFUSE-LOUD (RFC-0089 W3.b). It used to forward to
411// the one-argument `init()` and discard `argv`, so `--ros-args -r
412// chatter:=/other` — the single most common thing a ported `main` passes —
413// silently became a wrong-topic bug at runtime. Honouring it is remap
414// resolution, RFC-0020 violation class 4, and belongs beside
415// `rclcpp::resolve_name` rather than here; until it lands, the honest answer is a
416// loud abort carrying the migration.
417
418namespace detail {
425constexpr bool is_ros_args_flag(char const* s, int i = 0) {
426 return s == nullptr ? false
427 : "--ros-args"[i] == '\0' ? s[i] == '\0'
428 : s[i] != "--ros-args"[i] ? false
429 : is_ros_args_flag(s, i + 1);
430}
431
432constexpr bool argv_has_ros_args(int argc, char const* const* argv, int i = 0) {
433 return i >= argc ? false
434 : argv == nullptr ? false
435 : is_ros_args_flag(argv[i]) ? true
436 : argv_has_ros_args(argc, argv, i + 1);
437}
438
478[[noreturn]] inline void abort_failed_create(const char* verb, const char* name, int32_t code) {
479 // issue 1303 — two records through `nros_log`, each short enough for its
480 // format buffer: the call (the name bounded here), then the fixed text,
481 // whose length `say_refused` checks at compile time. As one `NROS_ERROR`
482 // record it was over 1180 bytes, which a real sink cannot carry: the body
483 // is dropped and only an ellipsis is printed, so the process aborted
484 // without saying why.
485 RCLC_LOG_ERROR(log_handle(nullptr), "rclcpp::Node::%s(\"%.64s\") failed: rclcpp::ErrorCode %d",
486 verb, name != nullptr ? name : "", static_cast<int>(code));
487 NROS_RCLCPP_SAY_REFUSED(NROS_RCLCPP_ABORT_FAILED_CREATE);
488 ::std::abort();
489}
490
493inline void require_created(::rclcpp::Result r, const char* verb, const char* name) {
494 if (!r.ok()) {
495 abort_failed_create(verb, name, r.raw());
496 }
497}
498} // namespace detail
499
525inline void init(int argc, char const* const* argv) {
526 // Issue 2073 -- the image's baked `ros_args` / `params_files` apply too,
527 // with or without a `--ros-args` on the command line.
528 const bool any = detail::argv_has_ros_args(argc, argv) || ::nros_cpp_baked_ros_args_present();
529 if (any && ::rclcpp::detail::apply_ros_args(nullptr, argc, argv) != 0) {
530 ::std::abort();
531 }
532 (void)::rclcpp::init_in();
533 // An init that failed leaves no executor to install into; nothing here is
534 // dropped that the failed init did not already drop with the session.
535 void* const executor = ::rclcpp::global_handle();
536 if (any && executor != nullptr && ::rclcpp::detail::apply_ros_args(executor, argc, argv) != 0) {
537 ::std::abort();
538 }
539}
540
542inline void init() {
543 (void)::rclcpp::init_in();
544}
545
559inline bool shutdown() {
560 return ::rclcpp::shutdown_in().ok();
561}
562
577using ::rclcpp::spin_once;
578
596
597} // namespace rclcpp
598
599// -- the ported publisher line, UNGATED — phase-456 W5 ------------------------
600//
601// `auto pub = node->create_publisher<M>("chatter", 10);` compiles on every
602// target now, including `-ffreestanding -nostdinc++` against Zephyr's and
603// ThreadX's minimal libcpp. It returns `Publisher<M>::SharedPtr`, which is
604// `rclcpp::Owned<Publisher<M>>` — the publisher by value, `operator->` for
605// `pub->publish(m)`, no allocator, no control block, no `<memory>`.
606//
607// WHAT THE NODE STOPPED DOING, stated because it is a behaviour change.
608// The hosted body used to `make_shared` the publisher and push a second
609// reference into `hosted().owned_entities`, so the node co-owned it and a
610// ported body could drop the returned pointer mid-expression
611// (`node->create_publisher<M>(…)->publish(m);`) and still have a live
612// publisher afterwards. `Owned<T>` is SOLE ownership, so there is no second
613// reference to keep: the entity is destroyed when the holder is. For the
614// expression above that is still correct — the temporary outlives the
615// full-expression — and for the member pattern the corpus actually uses
616// (`publisher_ = this->create_publisher<M>(…)`) it is identical to upstream,
617// where the node is the last owner. What is gone is a publisher that survives
618// its own handle, which upstream does not give either once the node drops it.
619//
620// phase-456 W4 measured that the node was never an ARENA owner here: nothing
621// registers a publisher and `EntryKind` has no publisher kind, so the retention
622// was retention and nothing more. The comment that used to sit on it claimed
623// the arena stored `&entity`, which was false in both halves and is the kind of
624// copied rationale that teaches the next reader the wrong model.
625namespace rclcpp {
626template <typename M>
627inline typename Publisher<M>::SharedPtr Node::create_publisher(const char* topic,
628 const ::rclcpp::QoS& qos) {
629 Publisher<M> p;
630 ::rclcpp::detail::require_created(this->create_publisher<M>(p, topic, qos), "create_publisher",
631 topic);
632 return typename Publisher<M>::SharedPtr(::rclcpp::tr::forward_rvalue(p));
633}
634
635template <typename M>
636inline typename Publisher<M>::SharedPtr Node::create_publisher(const char* topic, ::size_t depth) {
637 return this->create_publisher<M>(topic, ::rclcpp::QoS(static_cast<uint32_t>(depth)));
638}
639} // namespace rclcpp
640
641// The `std::string`-keyed forwarders, then the `Node::SharedPtr`-taking
642// verbs. phase-476 W3 split what was one block behind all four capability
643// probes: each half now names the one capability its signatures use. Every
644// verb here forwards to (or refuses in place of) a freestanding form defined
645// after these blocks.
646#if defined(NROS_CPP_HAS_STD_STRING) // hosted-family: string-interop
647
648namespace rclcpp {
649
650namespace detail {
651
652// phase-426 W4 — ~~`adopt_executor_param_seed`~~ IS GONE, along with the store it
653// existed to reconcile.
654//
655// It re-read every launch-seeded parameter out of the EXECUTOR's store and
656// copied it over the code default, because `rclcpp::Node`'s own
657// `nros::ParameterServer` was a DIFFERENT object and would otherwise have
658// answered 0.15 while launch said 0.03 (issue 0745). `nros::ComponentNode`
659// carried the same dispatch written a second time, in C++17 `if constexpr`,
660// and both headers flagged the duplication against themselves.
661//
662// With one store there is nothing to reconcile: `declare_parameter` declares
663// into the store the seed was written to, finds the name already present, and
664// reads back the seeded value. The adoption is the FFI's ALREADY_EXISTS
665// path, not a helper — and it is now one path rather than two that could
666// disagree.
667
668} // namespace detail
669
670// --- Node ---------------------------------------------------------------------
671//
672// phase-427 W1-W3 — THE NODE TYPES ARE MERGED. `rclcpp::Node` stopped being a
673// separate adapter class here (it became an alias for `nros::Node`, and since
674// phase-483 W1 it IS the one class), and the hosted call shape that adapter
675// carried is now a set of overloads on that one type (declared in `node.hpp`,
676// defined below).
677//
678// What that closes, in order of how much it cost:
679//
680// * THE DUPLICATE PARAMETER FACADE. `nros.hpp` said so about itself ("KNOWN
681// DUPLICATION... There should be ONE helper"): the same rclcpp-shaped
682// `declare_parameter<T>` existed twice, once in C++14 here and once in
683// C++17 `if constexpr` on `ComponentNode`. One node type is what makes one
684// facade possible.
685// * THE `get_logger()` COLLISION. Two accessors with identical signatures
686// and different observable behaviour — a real node logger and the
687// hardcoded `"nros.compat"` sentinel. One type can hold only one, so the
688// merge forced the decision W5 records.
689// * THE TWO-VOCABULARY SPLIT. A ported file wrote `rclcpp::Node` and got a
690// type with no graph queries, no lifecycle, no callback groups, no action
691// entities and no out-ref creators; a native file wrote `nros::Node` and
692// got no `shared_ptr` creators and no parameters. Neither list was a
693// design; both were what the other file happened to have.
694//
695// The class holds no dispatch state and runs no loop — every entity it creates
696// is registered on the executor arena `rclcpp::init()` opened (issue 0465, one
697// session per image), which is all RFC-0019 permits a wrapper to be.
698//
699// Threading: callbacks fire on whatever thread services a spin verb, mirroring
700// the rclcpp default. A node cannot move between executors (RFC-0002, one
701// executor per RTOS task), which is why the executor is decided at construction
702// and never afterwards.
703
704} // namespace rclcpp
705
706namespace rclcpp {
707
708// The HOSTED half of `rclcpp::Node`, defined here rather than in `node.hpp`
709// because every body below needs a complete entity type (`Publisher<M>`,
710// `Subscription<M>`, `Service<S>`, `Client<S>`) or a helper the umbrella pulls
711// in (`create_subscription_raw` from `component.hpp`, the callback cells from
712// `subscription.hpp` / `timer.hpp`). That is the same split Phase 84.G8 already
713// uses for the out-ref `Node::create_X<>` templates, which live in the header
714// that owns each entity.
715//
716// The declarations — with the default arguments — are in `node.hpp` under
717// `NROS_CPP_HAS_STD_STRING`, the one predicate both files share.
718
719// -- publishers ---------------------------------------------------------------
720
721} // namespace rclcpp
722
723namespace rclcpp {
724template <typename M>
725inline typename Publisher<M>::SharedPtr Node::create_publisher(const ::std::string& topic,
726 const ::rclcpp::QoS& qos) {
727 return this->create_publisher<M>(topic.c_str(), qos);
728}
729} // namespace rclcpp
730
731namespace rclcpp {} // namespace rclcpp
732
733namespace rclcpp {
734template <typename M>
735inline typename Publisher<M>::SharedPtr Node::create_publisher(const ::std::string& topic,
736 ::size_t depth) {
737 return this->create_publisher<M>(topic.c_str(), ::rclcpp::QoS(static_cast<uint32_t>(depth)));
738}
739} // namespace rclcpp
740
741namespace rclcpp {
742
743// -- subscriptions ------------------------------------------------------------
744//
745// phase-417 — ONE DISPATCH PATH. This ARENA-REGISTERS the subscription through
746// `rclcpp::create_subscription_raw` (`component.hpp`), the same
747// `nros_cpp_subscription_register` call the native callback-style
748// `create_subscription` makes, so the executor owns the subscriber and
749// dispatches the callback during `spin_once` — whichever spin verb the caller
750// drives. It used to create a POLL-mode subscription and drain it from a
751// node-local `pump()`, which only `rclcpp::spin` / `spin_some` called: a file
752// that spun `rclcpp::spin_once()` instead got zero callbacks and no diagnostic.
753//
754// Why not the out-ref `create_subscription(sub, topic, qos, cb)` overload: that
755// one is SFINAE-restricted to `void(*)(const M&)` — a plain function pointer
756// with NO context slot — and every ported rclcpp callback captures.
757//
758// The receive-buffer hint is `rx_buffer_capacity<M>` — the same number the poll
759// path's `take()` stacks — and NOT the strict `rx_size_bound<M>`, whose
760// unbounded-type arm is a deliberate compile error (issue 0964). A ported file
761// must not stop compiling because its message has an unbounded string.
762//
763// ONE THING THIS LOSES, stated rather than left silent:
764// `create_subscription_raw` hardcodes an EMPTY type hash (it takes no such
765// parameter), which `normalize_type_hash` turns into `"TypeHashNotSupported"`.
766// It does not affect DELIVERY — a subscriber's data keyexpr puts `*` in the
767// hash slot — but under `ros-iron` / `ros-jazzy` the subscription's LIVELINESS
768// token advertises the placeholder instead of the real hash, so
769// `ros2 topic info --verbose` reads differently.
770//
771// WHAT THE RETURNED HANDLE IS: a keep-alive, which is all upstream source does
772// with it (`rclcpp::Subscription<M>::SharedPtr sub_;`). The executor owns the
773// real subscriber. This comment used to say `sub->take(msg)` on it "answers
774// `NotInitialized`"; it did not — `initialized_` was true and the call reached
775// `&mut *(storage as *mut RmwSubscriber)` over bytes the arena never filled.
776// phase-456 W2 made the handle a type with no `operator->`, and W2b moved the
777// taking API to `rclcpp::PollSubscription<M>`, so neither half of that sentence
778// has a subject any more.
779} // namespace rclcpp
780
781namespace rclcpp {
782template <typename M, typename Cb>
783inline typename Subscription<M>::SharedPtr
784Node::create_subscription(const ::std::string& topic, const ::rclcpp::QoS& qos, Cb cb) {
785 // phase-476 W1 — forwards to the freestanding `const char*` overload.
786 return this->create_subscription<M>(topic.c_str(), qos, ::rclcpp::tr::forward_rvalue(cb));
787}
788} // namespace rclcpp
789
790namespace rclcpp {} // namespace rclcpp
791
792namespace rclcpp {
793template <typename M, typename Cb>
794inline typename Subscription<M>::SharedPtr Node::create_subscription(const ::std::string& topic,
795 ::size_t depth, Cb cb) {
796 return this->create_subscription<M>(topic.c_str(), depth, ::rclcpp::tr::forward_rvalue(cb));
797}
798} // namespace rclcpp
799
800namespace rclcpp {
801
802// -- wall timer ---------------------------------------------------------------
803
804// phase-476 W2 — `create_wall_timer` is defined after the hosted block now
805// (`-- timers --`): it returns a `TimerHandle` and allocates nothing, so it is
806// not a hosted capability.
807
808// -- parameters ---------------------------------------------------------------
809//
810// MOVED OUT OF THIS BLOCK (phase-426 W4). The definitions are below, after
811// `#endif // NROS_CPP_HAS_SHARED_PTR && ...`, because a node's parameters are
812// not a hosted capability: the scalar FFI is `<cstdint>` and a `const char*`,
813// and `rclcpp::Seq<T, N>` carries arrays with no STL either. Only the
814// `std::string`-KEYED overloads (declared on the class in `node.hpp`) need the
815// STL, and only those keep the gate.
816//
817// `parameters()` IS GONE (phase-426 W4). It handed out a reference to the node's
818// own `nros::ParameterServer`, described as the escape hatch for "the C-API
819// helpers that take an `rclc_parameter_server_t*`" - and no such helper
820// existed: ~~`nros_executor_register_parameter_services`~~ takes the executor,
821// never a standalone store. That class is deleted now too, so there is nothing
822// to return and nothing that took it. A node's parameters are reached through
823// `declare_parameter` / `get_parameter` / `set_parameter`, which name the store
824// the services actually read.
825
826// -- services and clients -----------------------------------------------------
827//
828// A callback of upstream's `shared_ptr<Request>, shared_ptr<Response>` shape is
829// REFUSE-LOUD rather than "no matching function": that signature needs a
830// per-request heap allocation on the delivery path, which is a second delivery
831// path, not a spelling.
832
833} // namespace rclcpp
834
835namespace rclcpp {
836template <typename S>
837inline ::rclcpp::Owned<::rclcpp::PollService<S>> Node::create_service(const ::std::string& name,
838 const ::rclcpp::QoS& qos) {
839 // phase-476 W1 — forwards to the freestanding `const char*` overload.
840 return this->template create_service<S>(name.c_str(), qos);
841}
842} // namespace rclcpp
843
844namespace rclcpp {} // namespace rclcpp
845
846namespace rclcpp {
847template <typename S, typename F, typename>
848inline typename Service<S>::SharedPtr Node::create_service(const ::std::string& name, F callback,
849 const ::rclcpp::QoS& qos) {
850 return this->template create_service<S>(name.c_str(), callback, qos);
851}
852} // namespace rclcpp
853
854namespace rclcpp {} // namespace rclcpp
855
856namespace rclcpp {
857template <typename S, typename F, typename, typename>
858inline typename Service<S>::SharedPtr Node::create_service(const ::std::string& name, F callback,
859 const ::rclcpp::QoS& qos) {
860 // phase-488 — the `const char*` overload decides: it takes upstream's
861 // shared_ptr handler and refuses the rest.
862 return this->template create_service<S>(name.c_str(), callback, qos);
863}
864} // namespace rclcpp
865
866namespace rclcpp {} // namespace rclcpp
867
868namespace rclcpp {
869template <typename S>
870inline ::rclcpp::Owned<::rclcpp::PollClient<S>> Node::create_client(const ::std::string& name,
871 const ::rclcpp::QoS& qos) {
872 // phase-476 W1 — forwards to the freestanding `const char*` overload.
873 return this->template create_client<S>(name.c_str(), qos);
874}
875} // namespace rclcpp
876
877namespace rclcpp {} // namespace rclcpp
878
879namespace rclcpp {
880template <typename S, typename F, typename>
881inline typename Client<S>::SharedPtr Node::create_client(const ::std::string& name, F callback,
882 const ::rclcpp::QoS& qos) {
883 return this->template create_client<S>(name.c_str(), callback, qos);
884}
885} // namespace rclcpp
886
887namespace rclcpp {} // namespace rclcpp
888
889namespace rclcpp {
890template <typename S, typename F, typename, typename>
891inline typename Client<S>::SharedPtr Node::create_client(const ::std::string&, F,
892 const ::rclcpp::QoS&) {
893 static_assert(::rclcpp::detail::refuse<F>::value,
894 NROS_RCLCPP_REFUSE_SHARED_PTR_SERVICE_CALLBACK);
895 return typename Client<S>::SharedPtr();
896}
897} // namespace rclcpp
898
899namespace rclcpp {} // namespace rclcpp
900
901#endif // NROS_CPP_HAS_STD_STRING
902
903#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
904namespace rclcpp {
905
906// `rclcpp::Node` is defined in `node.hpp`, UNCONDITIONALLY, not here, because
907// a freestanding target gets the class and must get its ROS 2 spelling with it.
908//
909// phase-417 — `pump()` IS GONE and does not come back. It ran a node's wall
910// timers and drained its polling subscriptions, and only `rclcpp::spin` /
911// `spin_some` called it, so a node driven by any other spin verb dispatched
912// nothing. Every entity a node creates is registered on the executor arena, so
913// there is nothing left for a node-local sweep to do and mixing spin spellings
914// is harmless. A second dispatch path here is the RFC-0019 violation the whole
915// item was about, and it cannot be made visible by a diagnostic.
916
917// --- rclcpp::create_timer (phase-430 W6) -------------------------------------
918//
919// Humble has NO `Node::create_timer` member — the clock-taking verb is a FREE
920// function, `rclcpp::create_timer(node, clock, period, callback[, group])`, and
921// that is the only form a Humble-era file can be written in. Ours takes the
922// same arguments in the same order and returns the same cell
923// `create_wall_timer` returns.
924//
925// The clock is `rclcpp::Clock*`, which is exactly what `node->get_clock()` hands
926// back, so the ported line binds with no conversion. Upstream's parameter is
927// `rclcpp::Clock::SharedPtr`; there is no allocator here (RFC-0022) and the
928// clock is a member of the node, so the pointer is the counterpart — the same
929// ADOPT-BOUNDED trade `get_clock()` itself makes.
930//
931// WHAT THE CLOCK DOES, and it is the whole point of the verb: a
932// `RCL_ROS_TIME` clock follows `/clock`, so the timer stops when a bag
933// stops and re-times when the bag is replayed at another rate; a
934// `RCL_STEADY_TIME` one does not. `create_wall_timer` is the steady
935// verb and is unaffected by simulated time. Upstream distinguishes the two
936// cases by TYPE (`WallTimer` vs `GenericTimer<ClockT>`) *and* by VERB; the type
937// distinction is unportable here (the clock is a runtime field on one flat
938// `Timer` — RFC-0089 §"Timer, studied against RTOS semantics"), the verb
939// distinction is portable and is what ported code writes, so that is what we
940// take.
941//
942// NOT ADDED: a clock-less `Node::create_timer(period, callback)`. That member
943// arrives in Iron, and the captured surface here is Humble; adding it now would
944// claim a spelling the recorded upstream does not have.
945
946namespace detail {
947
953template <typename N, typename ::rclcpp::tr::enable_if<
954 ::rclcpp::tr::is_convertible<N*, ::rclcpp::Node*>::value, int>::type = 0>
955inline ::rclcpp::Node& as_node_ref(const ::std::shared_ptr<N>& n) {
956 return *n;
957}
958
959} // namespace detail
960
961// phase-476 W2 — `rclcpp::create_timer` moved after this block (`-- timers --`).
962
963// --- spin / spin_some --------------------------------------------------------
964//
965// phase-417 — FORWARDERS, and nothing else. `rclcpp::spin(node)` is
966// `rclcpp::spin()` (block until `ok()` goes false) and `rclcpp::spin_some(node)`
967// is the process executor's `spin_some` drain (phase-488; it was one 0-timeout
968// `rclcpp::spin_once`). Both used to call a node-local `pump()`
969// first, which was the ONLY thing that ran a ported node's timers and
970// subscriptions; now those live on the executor, so these two dispatch nothing
971// of their own and a file that reaches for `rclcpp::spin_once()` or drives an
972// `rclcpp::Executor` gets exactly the same callbacks. That equivalence is the
973// structural prerequisite for step B: after it both node types share a name,
974// and a mismatch would be invisible.
975//
976// One behaviour change for an existing caller: `rclcpp::spin()` RETURNS on the
977// first failing `spin_once`, where this loop used to discard the result and
978// keep going. A dead session now ends the spin instead of looking alive
979// forever, which is what `rclcpp::spin()` and `Executor::spin` already promise.
980//
981// The `node` argument is still checked, and still otherwise unused: there is
982// one session per image (issue 0465), so every `rclcpp::Node` is on the global
983// executor these verbs drive. Upstream takes the node for the same reason and
984// spins the executor it belongs to.
985
986} // namespace rclcpp
987
988#endif // NROS_CPP_HAS_SHARED_PTR
989
990// --- spin verbs, freestanding (phase-482 W2) ----------------------------------
991//
992// They take `Node::SharedPtr`, which is `rclcpp::Handle<Node>` on every target
993// since phase-482 W2, so they left the `shared_ptr` block: a freestanding port's
994// `main` spells `rclcpp::spin(node)` exactly as a hosted one does.
995namespace rclcpp {
996
997inline void spin(const Node::SharedPtr& node) {
998 if (!node || !node->initialized()) {
999 return;
1000 }
1001 (void)::rclcpp::spin_in();
1002}
1003
1007 spin(Node::SharedPtr(base.node()));
1008}
1009
1010inline void spin_some(const Node::SharedPtr& node) {
1011 if (!node || !node->initialized()) {
1012 return;
1013 }
1014 // phase-488 — the process executor's `spin_some`, the same drain as
1015 // `Executor::spin_some` and Rust's (`nros_cpp_spin_some`). It was one
1016 // `spin_once(0)` pass, which leaves work a callback made ready for the
1017 // next call.
1018 void* const executor = ::rclcpp::global_handle();
1019 if (executor == nullptr) return;
1020 (void)nros_cpp_spin_some(executor, 0);
1021}
1022
1026}
1027
1028// Future type is templated rather than `const auto& future` so the header
1029// stays parseable under `-std=c++14` (the C++20 abbreviated-function-template
1030// syntax breaks `just check cpp`'s freestanding probe).
1031//
1032// issue 0339 — the bounded branch used to call `Executor::spin(timeout_ms)`
1033// and never consult the future, so it BURNED THE WHOLE TIMEOUT even when the
1034// future completed on the first spin: a `wait_for_service` / `send_request`
1035// sequence ported from rclcpp paid the full timeout on every SUCCESSFUL call.
1036// The unbounded branch directly below already had the right shape; both now
1037// share it, differing only in whether a deadline exists.
1052namespace detail {
1058template <typename F> inline bool future_is_ready(F& f) {
1059 return f.is_ready();
1060}
1061#if defined(NROS_CPP_HAS_STD_FUTURE) // hosted-family: future-interop
1062template <typename T> inline bool future_is_ready(const ::std::shared_future<T>& f) {
1063 return f.valid() && f.wait_for(::std::chrono::seconds(0)) == ::std::future_status::ready;
1064}
1065template <typename T> inline bool future_is_ready(::std::shared_future<T>& f) {
1066 return f.valid() && f.wait_for(::std::chrono::seconds(0)) == ::std::future_status::ready;
1067}
1068template <typename T> inline bool future_is_ready(::std::future<T>& f) {
1069 return f.valid() && f.wait_for(::std::chrono::seconds(0)) == ::std::future_status::ready;
1070}
1071#endif
1072} // namespace detail
1073
1074template <typename Future>
1076 int32_t timeout_ms = -1) {
1077 if (!node || !node->initialized()) {
1079 }
1080 // Poll slice: same 10 ms the unbounded loop always used.
1081 constexpr int32_t kPollMs = 10;
1082 const bool bounded = timeout_ms >= 0;
1083 const uint64_t start_ns = nros_cpp_time_ns();
1084 const uint64_t budget_ns = bounded ? static_cast<uint64_t>(timeout_ms) * 1000000ull : 0ull;
1085
1086 while (::rclcpp::ok()) {
1089 }
1090 (void)::rclcpp::spin_once(kPollMs);
1091 // Re-check before the deadline test: a future that became ready on the
1092 // spin just above must report SUCCESS even if the budget expired in
1093 // the same slice.
1096 }
1097 if (bounded && nros_cpp_time_ns() - start_ns >= budget_ns) {
1099 }
1100 }
1102}
1103
1104namespace detail {
1112template <typename Future>
1114 int32_t timeout_ms, bool process) {
1115 if (executor == nullptr) return FutureReturnCode::INTERRUPTED;
1116 constexpr int32_t kPollMs = 10;
1117 const bool bounded = timeout_ms >= 0;
1118 const uint64_t start_ns = nros_cpp_time_ns();
1119 const uint64_t budget_ns = bounded ? static_cast<uint64_t>(timeout_ms) * 1000000ull : 0ull;
1120 while (process ? ::rclcpp::ok() : !::nros_cpp_termination_requested()) {
1122 (void)nros_cpp_spin_once(executor, kPollMs);
1124 if (bounded && nros_cpp_time_ns() - start_ns >= budget_ns) return FutureReturnCode::TIMEOUT;
1125 }
1127}
1128
1130template <typename NodePtr>
1131inline void check_node_on_executor(const NodePtr& node, void* executor, bool process) {
1132 const bool live = node && node->initialized();
1133 const char* name = live ? node->get_name() : "";
1134 if (!live) {
1136 static_cast<int32_t>(ErrorCode::NotInitialized));
1137 }
1138 if (node->executor_handle() != executor) {
1139 // `say_refused` takes a LITERAL (its length is checked against the
1140 // log buffer), so the two refusals are spelled out, not passed in.
1141 if (process) {
1142 NROS_RCLCPP_SAY_REFUSED(NROS_RCLCPP_REFUSE_FOREIGN_EXECUTOR_NODE);
1143 } else {
1144 NROS_RCLCPP_SAY_REFUSED(NROS_RCLCPP_REFUSE_FOREIGN_NODE);
1145 }
1147 static_cast<int32_t>(ErrorCode::Unsupported));
1148 }
1149}
1150} // namespace detail
1151
1152template <typename Future>
1154 if (!initialized_) return FutureReturnCode::INTERRUPTED;
1155 return ::rclcpp::detail::spin_executor_until_future_complete(storage_, future, timeout_ms,
1156 false);
1157}
1158
1159template <typename NodePtr> inline void Executor::add_node(const NodePtr& node, bool notify) {
1160 (void)notify;
1161 ::rclcpp::detail::check_node_on_executor(node, static_cast<void*>(storage_), false);
1162}
1163
1164// --- rclcpp::executors (phase-488) --------------------------------------------
1165namespace executors {
1166
1187 public:
1189
1192 void add_node(const ::rclcpp::Node::SharedPtr& node, bool notify = true) {
1193 (void)notify;
1195 }
1196
1199 template <typename NodePtr, typename T = void>
1200 void remove_node(const NodePtr& node, bool notify = true) {
1201 (void)node;
1202 (void)notify;
1203 static_assert(::rclcpp::detail::refuse<T>::value, NROS_RCLCPP_REFUSE_REMOVE_NODE);
1204 }
1205
1209 template <typename Future>
1210 FutureReturnCode spin_until_future_complete(Future&& future, int32_t timeout_ms = -1) {
1211 return ::rclcpp::detail::spin_executor_until_future_complete(::rclcpp::global_handle(),
1212 future, timeout_ms, true);
1213 }
1214
1217 void spin_all(uint32_t max_duration_ms) {
1218 if (max_duration_ms == 0) {
1219 NROS_RCLCPP_SAY_REFUSED("spin_all(0) REFUSED: max_duration must be positive, as in "
1220 "rclcpp.");
1221 return;
1222 }
1223 spin_some(max_duration_ms);
1224 }
1225
1228 void spin_node_some(const ::rclcpp::Node::SharedPtr& node) {
1229 add_node(node);
1230 spin_some();
1231 }
1232
1236 void spin_node_once(const ::rclcpp::Node::SharedPtr& node, int32_t timeout_ms) {
1237 add_node(node);
1238 (void)::rclcpp::spin_once(timeout_ms);
1239 }
1240
1242 void spin() { (void)::rclcpp::spin_in(); }
1243
1246 void spin_some(uint32_t max_ms = 0) {
1247 void* const executor = ::rclcpp::global_handle();
1248 if (executor == nullptr) return;
1249 (void)nros_cpp_spin_some(executor, max_ms);
1250 }
1251};
1252
1254template <typename T = void> class MultiThreadedExecutorRefused {
1255 public:
1256 template <typename... Args> explicit MultiThreadedExecutorRefused(Args&&...) {
1257 static_assert(::rclcpp::detail::refuse<T>::value,
1258 NROS_RCLCPP_REFUSE_MULTI_THREADED_EXECUTOR);
1259 }
1260};
1261
1271
1272} // namespace executors
1273
1274} // namespace rclcpp
1275
1276#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
1277namespace rclcpp {
1278
1279// phase-482 W2 — the upstream `main` is `rclcpp::spin(std::make_shared<MyNode>())`,
1280// a TEMPORARY `shared_ptr`. Binding it to a function parameter is safe (it lives
1281// for the call), but converting it to a `Node::SharedPtr` handle is refused
1282// because a STORED handle would dangle. These overloads take the `shared_ptr` by
1283// reference instead, so that line compiles unchanged and owns the node for
1284// exactly as long as the spin runs.
1285template <typename N, typename ::rclcpp::tr::enable_if<
1286 ::rclcpp::tr::is_convertible<N*, Node*>::value, int>::type = 0>
1287inline void spin(const ::std::shared_ptr<N>& node) {
1288 spin(Node::SharedPtr(node));
1289}
1290
1291template <typename N, typename ::rclcpp::tr::enable_if<
1292 ::rclcpp::tr::is_convertible<N*, Node*>::value, int>::type = 0>
1293inline void spin_some(const ::std::shared_ptr<N>& node) {
1295}
1296
1297template <
1298 typename N, typename Future,
1299 typename ::rclcpp::tr::enable_if<::rclcpp::tr::is_convertible<N*, Node*>::value, int>::type = 0>
1300inline FutureReturnCode spin_until_future_complete(const ::std::shared_ptr<N>& node,
1301 Future&& future, int32_t timeout_ms = -1) {
1302 return spin_until_future_complete(Node::SharedPtr(node), future, timeout_ms);
1303}
1304
1305} // namespace rclcpp
1306#endif // NROS_CPP_HAS_SHARED_PTR
1307
1308// -- dispatch verbs, freestanding (phase-476 W1) -------------------------------
1309//
1310// The `const char*` overloads of `create_subscription` / `create_service` /
1311// `create_client`. Each result is a handle or an owned poll object, none of them
1312// allocates, and their bodies are the ones the hosted `std::string` overloads
1313// used to carry; those overloads now forward here.
1314
1315namespace rclcpp {
1316
1317namespace detail {
1318
1320template <typename M, typename Cb>
1321inline typename Subscription<M>::SharedPtr
1322create_subscription_dispatch(::rclcpp::Node& node, const char* topic, const ::rclcpp::QoS& qos,
1323 Cb cb, ::rclcpp::tr::false_type /* takes MessageInfo */) {
1324 // phase-456 W2 — the arena owns everything, so this allocates nothing.
1325 //
1326 // This used to `make_shared` a `detail::SubscriptionCallback<M>` cell whose
1327 // only job was to give a capturing lambda a stable address, push it into
1328 // ~~`owned_entities`~~ to keep it alive, and hand back a `shared_ptr` aliasing
1329 // into it. W1 put the capture in the arena entry, so the cell has nothing
1330 // left to hold; W2 stops pretending the result is a pointer to an object.
1331 //
1332 // issue 1995 — the callback's SHAPE decides what the arena holds: a
1333 // `const M&` callable as-is, or (hosted only) an adapter that hands a
1334 // pointer-shaped callback a heap copy of each message. Anything else is
1335 // refused here, naming the shapes, rather than as an `InplaceFn` conversion.
1337 static_assert(shape != ::rclcpp::detail::SUB_SHAPE_NONE || ::rclcpp::detail::refuse<Cb>::value,
1338 NROS_RCLCPP_REFUSE_SUBSCRIPTION_CALLBACK_SHAPE);
1339 using Capture = ::rclcpp::InplaceFn<void(const M&)>;
1341 ::rclcpp::tr::forward_rvalue(cb)));
1342 ::size_t handle_id = 0;
1344 node, topic, qos, captured, &handle_id);
1345 // phase-476 W2 — the arena's copy owns the callable now and destroys it on
1346 // release; ours must not destroy it too.
1347 if (r.ok()) {
1348 captured.relinquish();
1349 }
1350 ::rclcpp::detail::require_created(r, "create_subscription", topic);
1351 return typename Subscription<M>::SharedPtr(node.executor_handle(), handle_id);
1352}
1353
1357template <typename M, typename Cb>
1358inline typename Subscription<M>::SharedPtr
1359create_subscription_dispatch(::rclcpp::Node& node, const char* topic, const ::rclcpp::QoS& qos,
1360 Cb cb, ::rclcpp::tr::true_type /* takes MessageInfo */) {
1361#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
1363 auto fn = ::rclcpp::detail::subscription_info_adapter<M, Cb, shape>::wrap(
1364 ::rclcpp::tr::forward_rvalue(cb));
1365 ::size_t handle_id = 0;
1366 const ::rclcpp::Result r = ::rclcpp::detail::register_subscription_message_info<M>(
1367 node, topic, qos, static_cast<decltype(fn)&&>(fn), &handle_id);
1368 ::rclcpp::detail::require_created(r, "create_subscription", topic);
1369 return typename Subscription<M>::SharedPtr(node.executor_handle(), handle_id);
1370#else
1371 static_assert(::rclcpp::detail::refuse<Cb>::value, NROS_RCLCPP_REFUSE_MESSAGE_INFO_CALLBACK);
1372 (void)topic;
1373 (void)qos;
1374 (void)cb;
1375 return typename Subscription<M>::SharedPtr(node.executor_handle(), 0);
1376#endif // NROS_CPP_HAS_SHARED_PTR
1377}
1378
1379} // namespace detail
1380
1381// phase-515 P4 — the hash-taking generic verbs.
1382inline ::rclcpp::Owned<::rclcpp::GenericPublisher>
1383Node::create_generic_publisher(const char* topic, const char* type, const char* type_hash,
1384 const ::rclcpp::QoS& qos) {
1385 ::rclcpp::GenericPublisher p;
1386 ::rclcpp::detail::require_created(::rclcpp::detail::create_generic_publisher(
1387 this->ffi_handle(), p, topic, type, type_hash, qos),
1388 "create_generic_publisher", topic);
1389 return ::rclcpp::Owned<::rclcpp::GenericPublisher>(::rclcpp::tr::forward_rvalue(p));
1390}
1391
1392template <typename Cb>
1393inline ::rclcpp::SubscriptionHandle<::rclcpp::GenericSubscription>
1394Node::create_generic_subscription(const char* topic, const char* type, const char* type_hash,
1395 const ::rclcpp::QoS& qos, Cb callback) {
1396 ::rclcpp::GenericSubscription::Callback fn(::rclcpp::tr::forward_rvalue(callback));
1397 ::size_t handle_id = 0;
1399 ::rclcpp::detail::register_generic_subscription(this->ffi_handle(), topic, type, type_hash,
1400 qos, fn, &handle_id),
1401 "create_generic_subscription", topic);
1402 return ::rclcpp::SubscriptionHandle<::rclcpp::GenericSubscription>(this->executor_handle(),
1403 handle_id);
1404}
1405
1406template <typename M, typename Cb>
1407inline typename Subscription<M>::SharedPtr
1408Node::create_subscription(const char* topic, const ::rclcpp::QoS& qos, Cb cb) {
1409 // issue 2036 — a callback that takes no one-argument shape but does take
1410 // `(message, const MessageInfo&)` goes down the metadata path; every other
1411 // callback (including one that would take both) keeps the one-argument path.
1416 return ::rclcpp::detail::create_subscription_dispatch<M>(
1417 *this, topic, qos, ::rclcpp::tr::forward_rvalue(cb),
1418 ::rclcpp::tr::integral_constant<bool, takes_info>());
1419}
1420
1421template <typename M, typename Cb>
1423 ::size_t depth, Cb cb) {
1424 return this->create_subscription<M>(topic, ::rclcpp::QoS(static_cast<uint32_t>(depth)),
1425 ::rclcpp::tr::forward_rvalue(cb));
1426}
1427
1428template <typename M, typename Cb>
1429inline typename Subscription<M>::SharedPtr
1430Node::create_subscription(const char* topic, const ::rclcpp::QoS& qos, Cb cb,
1431 const ::rclcpp::SubscriptionOptions& options) {
1432 // phase-488 — `options.callback_group` is upstream's anonymous group, which
1433 // selects the node default (`rclcpp::CallbackGroupToken`), so it needs no
1434 // handling here. A scheduling context is a different matter: this overload
1435 // cannot bind one, and dropping it would schedule the callback somewhere
1436 // the caller did not ask for.
1437 if (options.sched_context != ::rclcpp::SCHED_CONTEXT_UNSET) {
1439 "create_subscription(..., options.sched_context)", topic);
1440 }
1441 // issue 2036 — metadata delivery is chosen by the CALLBACK, as upstream
1442 // chooses it: a callback taking `const rclcpp::MessageInfo&` gets it.
1443 // `options.message_info = true` with a callback that cannot receive it
1444 // would configure nothing, so it is refused rather than ignored.
1445 constexpr bool cb_takes_info =
1450 if (options.message_info && !cb_takes_info) {
1453 "create_subscription(..., options.message_info) with a callback taking no "
1454 "rclcpp::MessageInfo",
1455 topic);
1456 }
1457 return this->create_subscription<M>(topic, qos, ::rclcpp::tr::forward_rvalue(cb));
1458}
1459
1460template <typename M, typename Cb>
1461inline typename Subscription<M>::SharedPtr
1462Node::create_subscription(const char* topic, ::size_t depth, Cb cb,
1463 const ::rclcpp::SubscriptionOptions& options) {
1464 return this->create_subscription<M>(topic, ::rclcpp::QoS(static_cast<uint32_t>(depth)),
1465 ::rclcpp::tr::forward_rvalue(cb), options);
1466}
1467
1468template <typename S>
1469inline ::rclcpp::Owned<::rclcpp::PollService<S>> Node::create_service(const char* name,
1470 const ::rclcpp::QoS& qos) {
1471 // phase-456 W5 — the POLL server, by value. `Service<S>::SharedPtr` is a
1472 // dispatch handle now, and this factory exists precisely to be
1473 // `->take_request()`'d, so it must hand back something dereferenceable.
1474 // `Owned<PollService<S>>` is that and costs no allocator; the node keeps no
1475 // second reference because there is no second reference to keep.
1477 ::rclcpp::detail::require_created(this->template create_service<S>(s, name, qos),
1478 "create_service", name);
1479 return ::rclcpp::Owned<::rclcpp::PollService<S>>(::rclcpp::tr::forward_rvalue(s));
1480}
1481
1482template <typename S, typename F, typename>
1483inline typename Service<S>::SharedPtr Node::create_service(const char* name, F callback,
1484 const ::rclcpp::QoS& qos) {
1485 // phase-456 W5 — the arena owns the server and (W3) the handler, so this
1486 // allocates nothing and there is no cell to retain. The ~~`owned_entities`~~
1487 // push that used to stand here was LOAD-BEARING while the arena held
1488 // `&*s` — dropping the caller's pointer would have dangled it — and became
1489 // pure retention when W3 moved the context to the handler.
1490 Service<S> s;
1491 ::rclcpp::detail::require_created(this->template create_service<S>(s, name, callback, qos),
1492 "create_service", name);
1493 return typename Service<S>::SharedPtr(this->executor_handle(), s.handle_id());
1494}
1495
1496template <typename S, typename F, typename, typename>
1497inline typename Service<S>::SharedPtr Node::create_service(const char* name, F callback,
1498 const ::rclcpp::QoS& qos) {
1499#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
1500 // phase-488 — upstream's `shared_ptr` handler, lent from the dispatch frame
1501 // (`detail::shared_ptr_service_trampoline`, no heap), when it converts to a
1502 // function pointer. A handler with STATE — `std::bind(&X::on_req, this, _1,
1503 // _2)`, a capturing lambda — is copied into the service's arena entry
1504 // instead (`detail::stateful_service_trampoline`), in any of the three
1505 // shapes. Anything invocable as none of them is refused, naming them.
1506 constexpr int shape = ::rclcpp::detail::service_ptr_shape<S, F>::value;
1507 constexpr int stateful = ::rclcpp::detail::stateful_service_shape<S, F>::value;
1508 static_assert(shape != 0 || stateful != 0 || ::rclcpp::detail::refuse<F>::value,
1509 NROS_RCLCPP_REFUSE_SERVICE_CALLBACK_SHAPE);
1510 Service<S> s;
1512 this->template register_hosted_service_<S>(
1513 s, name, ::rclcpp::tr::forward_rvalue(callback), qos,
1514 ::rclcpp::tr::integral_constant<int, (shape != 0 ? 0 : 1)>()),
1515 "create_service", name);
1516 return typename Service<S>::SharedPtr(this->executor_handle(), s.handle_id());
1517#else
1518 (void)name;
1519 (void)callback;
1520 (void)qos;
1521 static_assert(::rclcpp::detail::refuse<F>::value, NROS_RCLCPP_REFUSE_SERVICE_CALLBACK_SHAPE);
1522 return typename Service<S>::SharedPtr();
1523#endif // NROS_CPP_HAS_SHARED_PTR
1524}
1525
1526#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
1527template <typename S, typename F>
1528inline Result Node::register_hosted_service_(Service<S>& out, const char* name, F callback,
1529 const ::rclcpp::QoS& qos,
1530 ::rclcpp::tr::integral_constant<int, 0>) {
1531 constexpr int shape = ::rclcpp::detail::service_ptr_shape<S, F>::value;
1532 return this->template register_service_<S>(
1533 out, name, &::rclcpp::detail::shared_ptr_service_trampoline<S, shape>::call,
1534 ::rclcpp::detail::shared_ptr_service_context<S>(
1535 callback, ::rclcpp::tr::integral_constant<int, (shape == 3 ? 3 : 2)>()),
1536 qos, ::rclcpp::ServiceOptions());
1537}
1538
1539template <typename S, typename F>
1540inline Result Node::register_hosted_service_(Service<S>& out, const char* name, F callback,
1541 const ::rclcpp::QoS& qos,
1542 ::rclcpp::tr::integral_constant<int, 1>) {
1543 constexpr int stateful = ::rclcpp::detail::stateful_service_shape<S, F>::value;
1544 using Fn = ::rclcpp::detail::StatefulServiceFn<S>;
1545 Fn captured(::rclcpp::detail::stateful_service_adapter<S, (stateful != 0 ? stateful : 1)>::wrap(
1546 ::rclcpp::tr::forward_rvalue(callback)));
1547 const Result r = this->template register_service_<S>(
1548 out, name, &::rclcpp::detail::stateful_service_trampoline<S>::call, nullptr, qos,
1549 ::rclcpp::ServiceOptions(), &captured, sizeof(Fn),
1550 &::rclcpp::detail::stateful_service_trampoline<S>::destroy);
1551 // The arena's copy owns the callable now and destroys it on release.
1552 if (r.ok()) {
1553 captured.relinquish();
1554 }
1555 return r;
1556}
1557#endif // NROS_CPP_HAS_SHARED_PTR
1558
1559template <typename S, typename F>
1560inline typename Service<S>::SharedPtr Node::create_service(const char* name, F callback,
1561 const ::rclcpp::QoS& qos,
1562 const ::rclcpp::CallbackGroup* group) {
1563 // phase-488 — see the declaration: an anonymous group is null here.
1564 if (group != nullptr && group->get_name() != nullptr) {
1566 "create_service(..., named callback group)", name);
1567 }
1568 return this->template create_service<S>(name, ::rclcpp::tr::forward_rvalue(callback), qos);
1569}
1570
1571template <typename S>
1572inline ::rclcpp::Owned<::rclcpp::PollClient<S>> Node::create_client(const char* name,
1573 const ::rclcpp::QoS& qos) {
1574 // phase-456 W9 — the FUTURE-style client, by value. `Client<S>::SharedPtr`
1575 // is a dispatch handle now, and this factory exists precisely to be
1576 // `->send_request()`'d, so it must hand back something dereferenceable.
1577 // `Owned<PollClient<S>>` is that and costs no allocator; the node keeps no
1578 // second reference because there is no second reference to keep.
1580 ::rclcpp::detail::require_created(this->template create_client<S>(c, name, qos),
1581 "create_client", name);
1582 return ::rclcpp::Owned<::rclcpp::PollClient<S>>(::rclcpp::tr::forward_rvalue(c));
1583}
1584
1585template <typename S, typename F, typename>
1586inline typename Client<S>::SharedPtr Node::create_client(const char* name, F callback,
1587 const ::rclcpp::QoS& qos) {
1588 // phase-456 W9 — the arena owns the client and (W3) the handler, so this
1589 // allocates nothing and there is no cell to retain. The ~~`owned_entities`~~
1590 // push that used to stand here was LOAD-BEARING while the arena held
1591 // `&*c` — dropping the caller's pointer would have dangled it — and became
1592 // pure retention when W3 moved the context to the handler. Same removal W5
1593 // made one entity over.
1594 Client<S> c;
1595 ::rclcpp::detail::require_created(this->template create_client<S>(c, name, callback, qos),
1596 "create_client", name);
1597 return typename Client<S>::SharedPtr(this->executor_handle(), c.handle_id());
1598}
1599
1600template <typename S, typename F, typename, typename>
1601inline typename Client<S>::SharedPtr Node::create_client(const char*, F, const ::rclcpp::QoS&) {
1602 static_assert(::rclcpp::detail::refuse<F>::value,
1603 NROS_RCLCPP_REFUSE_SHARED_PTR_SERVICE_CALLBACK);
1604 return typename Client<S>::SharedPtr();
1605}
1606
1607} // namespace rclcpp
1608
1609// -- timers -------------------------------------------------------------------
1610//
1611// phase-476 W2 — FREESTANDING. The callable is copied into the executor arena
1612// (`nros_cpp_timer_create_capturing`), which destroys it when the timer is
1613// released, so these allocate nothing and return a two-word `TimerHandle`.
1614// Defined after the hosted block so that, on a hosted target, the
1615// `shared_ptr` member of `as_node_ref`'s overload set is already declared when
1616// `rclcpp::create_timer`'s body names it.
1617
1618namespace rclcpp {
1619namespace detail {
1620
1624template <typename F> class timer_takes_info {
1625 typedef char yes_t[1];
1626 typedef char no_t[2];
1627 template <typename G>
1628 static yes_t& probe(G* g, decltype((*g)(*static_cast<const TimerInfo*>(nullptr)), 0));
1629 template <typename G> static no_t& probe(...);
1630
1631 public:
1632 static const bool value = sizeof(probe<F>(nullptr, 0)) == sizeof(yes_t);
1633};
1634
1638template <typename F, bool TakesInfo = timer_takes_info<F>::value> struct TimerAdapter {
1639 F f;
1640 void operator()(const nros_cpp_timer_call_info_t& raw) {
1641 const rcl_clock_type_t clock = static_cast<rcl_clock_type_t>(raw.clock_type);
1642 const TimerInfo info{Time(raw.expected_call_time, clock),
1643 Time(raw.actual_call_time, clock)};
1644 f(info);
1645 }
1646};
1647template <typename F> struct TimerAdapter<F, false> {
1648 F f;
1649 void operator()(const nros_cpp_timer_call_info_t&) { f(); }
1650};
1651
1657template <typename F> struct timer_capture_capacity {
1658 static constexpr tr::size_type value = NROS_CPP_CALLBACK_CAPACITY;
1659};
1660template <typename Sig, tr::size_type Cap> struct timer_capture_capacity<InplaceFn<Sig, Cap>> {
1661 static constexpr tr::size_type value = sizeof(TimerAdapter<InplaceFn<Sig, Cap>>);
1662};
1663
1679template <typename Cb>
1680inline TimerHandle register_timer_capturing(::rclcpp::Node& node, rcl_clock_type_t clock_type,
1681 Duration period, Cb&& cb, const char* verb,
1682 const CallbackGroup* group = nullptr,
1683 bool autostart = true) {
1684 using F = typename tr::decay<Cb>::type;
1685 using Adapter = TimerAdapter<F>;
1686 using Capture =
1687 InplaceFn<void(const nros_cpp_timer_call_info_t&), timer_capture_capacity<F>::value>;
1688 Capture captured(Adapter{tr::relay<Cb>(cb)});
1689 // Non-capturing lambdas, so each decays to the plain function pointer the
1690 // arena stores. `ctx` is the ARENA's copy of `captured`, never ours.
1691 nros_cpp_timer_info_callback_t invoke = [](const nros_cpp_timer_call_info_t* info, void* ctx) {
1692 (*static_cast<Capture*>(ctx))(*info);
1693 };
1694 nros_cpp_timer_callback_t destroy = [](void* ctx) { static_cast<Capture*>(ctx)->clear(); };
1695 const int64_t ns = period.nanoseconds();
1696 const uint64_t ms = ns > 0 ? static_cast<uint64_t>(ns / 1000000) : uint64_t(0);
1697 size_t handle_id = 0;
1698 const nros_cpp_node_t* h = node.ffi_handle();
1699 const char* group_name = group != nullptr ? group->get_name() : nullptr;
1700 Result r = h == nullptr ? Result(ErrorCode::NotInitialized)
1701 : Result(nros_cpp_timer_create_capturing_with_info(
1702 h, static_cast<uint8_t>(clock_type), ms, invoke,
1703 reinterpret_cast<const uint8_t*>(&captured), sizeof(Capture),
1704 destroy, group_name, autostart, &handle_id));
1705 if (r.ok()) {
1706 captured.relinquish();
1707 }
1708 // A null return would be silent HERE above all: a ported node stores the
1709 // timer handle and never dereferences it, so a dead timer would simply
1710 // never fire.
1712 return TimerHandle(node.executor_handle(), handle_id);
1713}
1714
1715} // namespace detail
1716} // namespace rclcpp
1717
1718namespace rclcpp {
1719
1720template <typename Cb>
1721inline ::rclcpp::TimerHandle Node::create_wall_timer(::rclcpp::Duration period, Cb cb,
1722 const ::rclcpp::CallbackGroup* group,
1723 bool autostart) {
1724 return ::rclcpp::detail::register_timer_capturing(*this, RCL_STEADY_TIME, period,
1725 ::rclcpp::tr::forward_rvalue(cb),
1726 "create_wall_timer", group, autostart);
1727}
1728
1729#ifdef NROS_CPP_HAS_STD_CHRONO // hosted-family: chrono-interop
1730template <typename Rep, typename Period, typename Cb>
1731inline ::rclcpp::TimerHandle Node::create_wall_timer(::std::chrono::duration<Rep, Period> period,
1732 Cb cb, const ::rclcpp::CallbackGroup* group,
1733 bool autostart) {
1734 const auto ns = ::std::chrono::duration_cast<::std::chrono::nanoseconds>(period).count();
1735 return this->create_wall_timer(::rclcpp::Duration::from_nanoseconds(static_cast<int64_t>(ns)),
1736 ::rclcpp::tr::forward_rvalue(cb), group, autostart);
1737}
1738#endif // NROS_CPP_HAS_STD_CHRONO
1739
1740namespace detail {
1745inline ::rclcpp::Node& as_node_ref(const ::rclcpp::Node::SharedPtr& n) {
1746 return *n;
1747}
1748inline ::rclcpp::Node& as_node_ref(::rclcpp::Node& n) {
1749 return n;
1750}
1751inline ::rclcpp::Node& as_node_ref(::rclcpp::Node* n) {
1752 return *n;
1753}
1754} // namespace detail
1755
1769template <typename NodeT, typename CallbackT>
1770inline ::rclcpp::TimerHandle
1771create_timer(NodeT&& node, ::rclcpp::Clock* clock, ::rclcpp::Duration period, CallbackT&& callback,
1772 const ::rclcpp::CallbackGroup* group = nullptr, bool autostart = true) {
1774 const rcl_clock_type_t type =
1775 clock != nullptr ? clock->get_clock_type() : n.get_clock()->get_clock_type();
1776 return ::rclcpp::detail::register_timer_capturing(n, type, period,
1777 ::rclcpp::tr::relay<CallbackT>(callback),
1778 "create_timer", group, autostart);
1779}
1780
1781#ifdef NROS_CPP_HAS_STD_CHRONO // hosted-family: chrono-interop
1787template <typename NodeT, typename Rep, typename Period, typename CallbackT>
1788inline ::rclcpp::TimerHandle
1789create_timer(NodeT&& node, ::rclcpp::Clock* clock, ::std::chrono::duration<Rep, Period> period,
1790 CallbackT&& callback, const ::rclcpp::CallbackGroup* group = nullptr,
1791 bool autostart = true) {
1792 const auto ns = ::std::chrono::duration_cast<::std::chrono::nanoseconds>(period).count();
1793 return create_timer(::rclcpp::tr::relay<NodeT>(node), clock,
1794 ::rclcpp::Duration::from_nanoseconds(static_cast<int64_t>(ns)),
1795 ::rclcpp::tr::relay<CallbackT>(callback), group, autostart);
1796}
1797#endif // NROS_CPP_HAS_STD_CHRONO
1798
1799} // namespace rclcpp
1800
1801// --- node parameters ---------------------------------------------------------
1802//
1803// Out here rather than in the hosted block above: see the note at the
1804// `-- parameters --` marker there, and `node.hpp` for why the gate moved to
1805// the `std::string`-keyed overloads alone (phase-426 W4).
1806
1807// -- parameters ---------------------------------------------------------------
1808//
1809// Forwarders onto THE parameter store — the `nros_params::ParameterServer` the
1810// EXECUTOR owns, reached through `rclcpp/node_parameters.hpp`. There is no other
1811// one any more.
1812//
1813// What changed in phase-426 W4, and why it is not a detail: these used to
1814// forward to an inline `nros::ParameterServer<NROS_RCLCPP_MAX_PARAMS>` member
1815// on the hosted block — a second store, node-local, which the six
1816// `rcl_interfaces/srv/*` servers could not read. So a parameter declared here
1817// was invisible to `ros2 param get`, a sibling node did not share it, and the
1818// launch seed had to be copied across by a helper because the two stores could
1819// not be the same object. The member is deleted; the seam is one FFI call;
1820// `ros2 param get <node> <name>` sees what `declare_parameter` wrote. Issue
1821// 0793 / RFC-0089 §"Parameters".
1822//
1823// ADOPT-BOUNDED still, and the envelope is now about TYPES rather than scope:
1824// bool / int / int64_t / double reach the store, `std::string` and
1825// `std::vector<T>` do under `NROS_CPP_STD`, and rclcpp's `ParameterDescriptor`
1826// / `ignore_override` / callback arguments remain absent (the
1827// compile-time-options rule). The WIRE half of the acceptance — that the six
1828// services answer per node FQN — is phase-426 W3/W6.
1829//
1830// Where the image declares no `param_services` capability there is no store at
1831// all, and every call answers `ErrorCode::Unsupported`. This facade wears
1832// upstream's value-returning signature, which has nowhere to report that, so
1833// `declare_parameter` returns the code default. The loud half USED to be
1834// `nros::ComponentNode`, whose own facade recorded the failure on an `ok()`
1835// flag and made it boot-fatal; phase-427 W4 deleted that type, so no C++ path
1836// is boot-fatal on a missing store any more. `rclcpp::Node` still carries the
1837// flag (`set_error` / `ok()`); routing the parameter path back onto it is a
1838// separate decision, because it changes what an upstream-shaped
1839// `declare_parameter` does.
1840
1849
1850namespace rclcpp {
1851template <typename T> inline T Node::declare_parameter(const char* name, T default_value) {
1852 // phase-446 W6 -- a declaration the contract's `params:` does not make, or
1853 // makes with another type, refuses the boot through `set_error` before
1854 // anything reaches the store (see `Node::check_declared_param`).
1855 if (!this->check_declared_param(name, ::rclcpp::detail::node_param_type<T>::value)) {
1856 return default_value;
1857 }
1858 const ::nros_cpp_node_t* h = this->ffi_handle();
1859 Result r = ::rclcpp::detail::node_param_declare(h, name, default_value);
1860 if (!r.ok() && r.raw() != RCLC_RET_ALREADY_EXISTS) {
1861 return default_value;
1862 }
1863 T out = T();
1864 if (!::rclcpp::detail::node_param_get(h, name, out).ok()) {
1865 return default_value;
1866 }
1867 return out;
1868}
1869} // namespace rclcpp
1870
1871namespace rclcpp {} // namespace rclcpp
1872
1873namespace rclcpp {
1874template <typename T> inline bool Node::get_parameter(const char* name, T& out) const {
1875 return ::rclcpp::detail::node_param_get(this->ffi_handle(), name, out).ok();
1876}
1877} // namespace rclcpp
1878
1879namespace rclcpp {} // namespace rclcpp
1880
1881namespace rclcpp {
1882template <typename T> inline T Node::get_parameter(const char* name) const {
1883 T out = T();
1884 (void)::rclcpp::detail::node_param_get(this->ffi_handle(), name, out);
1885 return out;
1886}
1887} // namespace rclcpp
1888
1889namespace rclcpp {
1890
1895} // namespace rclcpp
1896
1897namespace rclcpp {
1898template <typename T> inline Result Node::set_parameter(const char* name, T value) {
1899 return ::rclcpp::detail::node_param_set(this->ffi_handle(), name, value);
1900}
1901} // namespace rclcpp
1902
1903namespace rclcpp {} // namespace rclcpp
1904
1905namespace rclcpp {
1906inline bool Node::has_parameter(const char* name) const {
1907 return ::rclcpp::detail::node_param_has(this->ffi_handle(), name);
1908}
1909} // namespace rclcpp
1910
1911// --- the rest of rclcpp's parameter surface (phase-417 W4.a) -----------------
1912//
1913// Every one of these is a forwarder onto the executor's ONE store through
1914// `rclcpp/node_parameters.hpp`. None of them holds state; a wrapper that did
1915// would be the second store phase-426 removed, in a smaller spelling.
1916
1917namespace rclcpp {
1918
1919template <typename T>
1920inline T Node::declare_parameter(const char* name, T default_value,
1921 const ParameterDescriptor& descriptor) {
1922 T in_effect = this->template declare_parameter<T>(name, default_value);
1923 // The descriptor is attached AFTER the declaration, which is the store's
1924 // own shape (rclc's `rclc_add_parameter_description` & co.). A failure
1925 // here leaves the parameter declared with its value, which is what
1926 // upstream does when the descriptor is consistent with the default — and
1927 // when it is not, the attach refuses rather than advertising a bound the
1928 // value breaks.
1929 (void)::rclcpp::detail::node_param_apply_descriptor(
1930 this->ffi_handle(), name, descriptor, ::rclcpp::detail::node_param_type<T>::value);
1931 return in_effect;
1932}
1933
1934template <typename T>
1935inline bool Node::get_parameter_or(const char* name, T& out, T fallback) const {
1936 if (::rclcpp::detail::node_param_get(this->ffi_handle(), name, out).ok()) {
1937 return true;
1938 }
1939 out = fallback;
1940 return false;
1941}
1942
1943inline Result Node::undeclare_parameter(const char* name) {
1944 return ::rclcpp::detail::node_param_undeclare(this->ffi_handle(), name);
1945}
1946
1947inline ParameterType Node::get_parameter_type(const char* name) const {
1948 int32_t code = 0;
1949 if (!::rclcpp::detail::node_param_get_type(this->ffi_handle(), name, code).ok()) {
1950 return PARAMETER_NOT_SET;
1951 }
1952 return static_cast<ParameterType>(code);
1953}
1954
1955inline Result Node::get_parameter_types(const char* const* names, ::size_t count,
1956 ParameterType* out) const {
1957 if ((names == nullptr || out == nullptr) && count != 0) {
1958 return Result(RCL_RET_INVALID_ARGUMENT);
1959 }
1960 for (::size_t i = 0; i < count; ++i) {
1961 out[i] = this->get_parameter_type(names[i]);
1962 }
1963 return Result(RCL_RET_OK);
1964}
1965
1966inline Result Node::describe_parameter(const char* name, ParameterDescriptor& out, char* text,
1967 ::size_t text_len) const {
1968 out = parameter_descriptor();
1969 // The buffer is split in half, one NUL-terminated string in each, so ONE
1970 // caller buffer serves both texts and neither can run into the other.
1971 char* description = nullptr;
1972 char* constraints = nullptr;
1973 ::size_t half = 0;
1974 if (text != nullptr && text_len >= 2) {
1975 half = text_len / 2;
1976 description = text;
1977 constraints = text + half;
1978 }
1979 int32_t type = 0;
1980 Result r = ::rclcpp::detail::node_param_describe(this->ffi_handle(), name, description, half,
1981 constraints, half, &out.read_only, &type);
1982 if (r.raw() == RCLC_RET_NOT_FOUND) {
1983 return r;
1984 }
1985 out.description = description;
1986 out.additional_constraints = constraints;
1987
1988 // The range, read through whichever accessor the TYPE picks. NOT_FOUND
1989 // from it means "no range", which is not a failure of `describe`.
1990 if (type == ::rclcpp::param_type::INTEGER) {
1991 out.has_range =
1992 ::rclcpp::detail::node_param_get_range(this->ffi_handle(), name, &out.integer_from,
1993 &out.integer_to, &out.integer_step)
1994 .ok();
1995 } else if (type == ::rclcpp::param_type::DOUBLE) {
1996 out.has_range =
1997 ::rclcpp::detail::node_param_get_range(this->ffi_handle(), name, &out.double_from,
1998 &out.double_to, &out.double_step)
1999 .ok();
2000 }
2001 return r;
2002}
2003
2004inline Result Node::list_parameters(const char* prefix, char* out_names, ::size_t name_stride,
2005 ::size_t max_names, ::size_t& count) const {
2006 return ::rclcpp::detail::node_param_list(this->ffi_handle(), prefix, out_names, name_stride,
2007 max_names, &count);
2008}
2009
2010inline Result Node::set_parameters_atomically(const ParameterWrite* writes, ::size_t count) {
2011 return ::rclcpp::detail::node_params_set_atomically(this->ffi_handle(), writes, count);
2012}
2013
2014inline Result Node::add_on_set_parameters_callback(OnSetParametersCallbackType callback,
2015 void* context,
2016 OnSetParametersCallbackToken& out_handle) {
2017 return ::rclcpp::detail::node_param_add_on_set_callback(this->ffi_handle(), callback, context,
2018 &out_handle);
2019}
2020
2021inline Result Node::remove_on_set_parameters_callback(OnSetParametersCallbackToken handle) {
2022 return ::rclcpp::detail::node_param_remove_on_set_callback(this->ffi_handle(), handle);
2023}
2024
2025} // namespace rclcpp
2026
2027// --- issue 2056: the value-object parameter surface (hosted) -----------------
2028//
2029// Dispatchers from `ParameterValue` / `Parameter` onto the typed methods
2030// above. No state: a `Parameter` read here is built from the store each time.
2031
2032#ifdef NROS_CPP_STD // hosted-family: parameter-interop
2033namespace rclcpp {
2034namespace detail {
2035
2036template <typename T>
2037inline T node_declare_typed(Node& n, const char* name, T v, const ParameterDescriptor* d) {
2038 return d != nullptr ? n.template declare_parameter<T>(name, v, *d)
2039 : n.template declare_parameter<T>(name, v);
2040}
2041
2042inline ParameterValue node_declare_value(Node& n, const char* name, const ParameterValue& v,
2043 const ParameterDescriptor* d) {
2044 switch (v.get_type()) {
2045 case PARAMETER_BOOL:
2046 return ParameterValue(node_declare_typed<bool>(n, name, v.as_bool(), d));
2047 case PARAMETER_INTEGER:
2048 return ParameterValue(
2049 static_cast<long long>(node_declare_typed<int64_t>(n, name, v.as_int(), d)));
2050 case PARAMETER_DOUBLE:
2051 return ParameterValue(node_declare_typed<double>(n, name, v.as_double(), d));
2052 case PARAMETER_STRING:
2053 return ParameterValue(node_declare_typed<::std::string>(n, name, v.as_string(), d));
2054 case PARAMETER_BOOL_ARRAY:
2055 return ParameterValue(
2056 node_declare_typed<::std::vector<bool>>(n, name, v.as_bool_array(), d));
2057 case PARAMETER_INTEGER_ARRAY:
2058 return ParameterValue(
2059 node_declare_typed<::std::vector<int64_t>>(n, name, v.as_integer_array(), d));
2060 case PARAMETER_DOUBLE_ARRAY:
2061 return ParameterValue(
2062 node_declare_typed<::std::vector<double>>(n, name, v.as_double_array(), d));
2063 default:
2064 // NOT_SET, a byte array or a string array: no store form.
2065 return ParameterValue();
2066 }
2067}
2068
2072template <typename D> inline ParameterDescriptor descriptor_from_message(const D& m) {
2073 ParameterDescriptor d = ::rclcpp::parameter_descriptor();
2074 d.description = m.description.c_str();
2075 d.additional_constraints = m.additional_constraints.c_str();
2076 d.read_only = m.read_only;
2077 for (const auto& r : m.floating_point_range) {
2078 d.has_range = true;
2079 d.double_from = r.from_value;
2080 d.double_to = r.to_value;
2081 d.double_step = r.step;
2082 break;
2083 }
2084 for (const auto& r : m.integer_range) {
2085 d.has_range = true;
2086 d.integer_from = r.from_value;
2087 d.integer_to = r.to_value;
2088 d.integer_step = static_cast<int64_t>(r.step);
2089 break;
2090 }
2091 return d;
2092}
2093
2094inline Node::SetParametersOutcome node_set_outcome(Result r) {
2095 Node::SetParametersOutcome o;
2096 o.successful = r.ok();
2097 if (!o.successful) {
2098 o.reason = "set_parameter refused (nano-ros code " + ::std::to_string(r.raw()) + ")";
2099 }
2100 return o;
2101}
2102
2103} // namespace detail
2104
2105inline ParameterValue Node::declare_parameter(const char* name,
2106 const ParameterValue& default_value) {
2107 return ::rclcpp::detail::node_declare_value(*this, name, default_value, nullptr);
2108}
2109
2110inline ParameterValue Node::declare_parameter(const char* name, const ParameterValue& default_value,
2111 const ParameterDescriptor& descriptor) {
2112 return ::rclcpp::detail::node_declare_value(*this, name, default_value, &descriptor);
2113}
2114
2115template <typename D, typename>
2116inline ParameterValue Node::declare_parameter(const char* name, const ParameterValue& default_value,
2117 const D& descriptor) {
2118 const ParameterDescriptor d = ::rclcpp::detail::descriptor_from_message(descriptor);
2119 return ::rclcpp::detail::node_declare_value(*this, name, default_value, &d);
2120}
2121
2122template <typename T, typename D, typename>
2123inline T Node::declare_parameter(const char* name, T default_value, const D& descriptor) {
2124 const ParameterDescriptor d = ::rclcpp::detail::descriptor_from_message(descriptor);
2125 return this->template declare_parameter<T>(name, default_value, d);
2126}
2127
2128inline bool Node::get_parameter(const char* name, Parameter& out) const {
2129 ParameterValue v;
2130 switch (this->get_parameter_type(name)) {
2131 case PARAMETER_BOOL: {
2132 bool x = false;
2133 if (!this->template get_parameter<bool>(name, x)) return false;
2134 v = ParameterValue(x);
2135 break;
2136 }
2137 case PARAMETER_INTEGER: {
2138 int64_t x = 0;
2139 if (!this->template get_parameter<int64_t>(name, x)) return false;
2140 v = ParameterValue(static_cast<long long>(x));
2141 break;
2142 }
2143 case PARAMETER_DOUBLE: {
2144 double x = 0.0;
2145 if (!this->template get_parameter<double>(name, x)) return false;
2146 v = ParameterValue(x);
2147 break;
2148 }
2149 case PARAMETER_STRING: {
2150 ::std::string x;
2151 if (!this->template get_parameter<::std::string>(name, x)) return false;
2152 v = ParameterValue(x);
2153 break;
2154 }
2155 case PARAMETER_BOOL_ARRAY: {
2156 ::std::vector<bool> x;
2157 if (!this->template get_parameter<::std::vector<bool>>(name, x)) return false;
2158 v = ParameterValue(x);
2159 break;
2160 }
2161 case PARAMETER_INTEGER_ARRAY: {
2162 ::std::vector<int64_t> x;
2163 if (!this->template get_parameter<::std::vector<int64_t>>(name, x)) return false;
2164 v = ParameterValue(x);
2165 break;
2166 }
2167 case PARAMETER_DOUBLE_ARRAY: {
2168 ::std::vector<double> x;
2169 if (!this->template get_parameter<::std::vector<double>>(name, x)) return false;
2170 v = ParameterValue(x);
2171 break;
2172 }
2173 default:
2174 return false;
2175 }
2176 out = Parameter(::std::string(name), v);
2177 return true;
2178}
2179
2180inline Parameter Node::get_parameter(const ::std::string& name) const {
2181 Parameter p(name);
2182 (void)this->get_parameter(name.c_str(), p);
2183 return p;
2184}
2185
2186inline ::std::vector<Parameter>
2187Node::get_parameters(const ::std::vector<::std::string>& names) const {
2188 ::std::vector<Parameter> out;
2189 out.reserve(names.size());
2190 for (const auto& n : names) {
2191 out.push_back(this->get_parameter(n));
2192 }
2193 return out;
2194}
2195
2196inline Node::SetParametersOutcome Node::set_parameter(const Parameter& p) {
2197 const char* name = p.get_name().c_str();
2198 Result r(NROS_CPP_RET_UNSUPPORTED);
2199 switch (p.get_type()) {
2200 case PARAMETER_BOOL:
2201 r = this->template set_parameter<bool>(name, p.as_bool());
2202 break;
2203 case PARAMETER_INTEGER:
2204 r = this->template set_parameter<int64_t>(name, p.as_int());
2205 break;
2206 case PARAMETER_DOUBLE:
2207 r = this->template set_parameter<double>(name, p.as_double());
2208 break;
2209 case PARAMETER_STRING:
2210 r = this->template set_parameter<::std::string>(name, p.as_string());
2211 break;
2212 case PARAMETER_BOOL_ARRAY:
2213 r = this->template set_parameter<::std::vector<bool>>(name, p.as_bool_array());
2214 break;
2215 case PARAMETER_INTEGER_ARRAY:
2216 r = this->template set_parameter<::std::vector<int64_t>>(name, p.as_integer_array());
2217 break;
2218 case PARAMETER_DOUBLE_ARRAY:
2219 r = this->template set_parameter<::std::vector<double>>(name, p.as_double_array());
2220 break;
2221 default:
2222 break;
2223 }
2224 return ::rclcpp::detail::node_set_outcome(r);
2225}
2226
2227inline ::std::vector<Node::SetParametersOutcome>
2228Node::set_parameters(const ::std::vector<Parameter>& parameters) {
2229 ::std::vector<SetParametersOutcome> out;
2230 out.reserve(parameters.size());
2231 for (const auto& p : parameters) {
2232 out.push_back(this->set_parameter(p));
2233 }
2234 return out;
2235}
2236
2237template <typename F>
2238inline node_interfaces::OnSetParametersCallbackHandle::SharedPtr
2239Node::add_on_set_parameters_callback(F callback) {
2240 using Handle = node_interfaces::OnSetParametersCallbackHandle;
2241 auto handle = ::std::make_shared<Handle>();
2242 handle->callback = [callback](const ::std::vector<Parameter>& params) mutable -> bool {
2243 return static_cast<bool>(callback(params).successful);
2244 };
2245 handle->node = this->ffi_handle();
2246 Result r = ::rclcpp::detail::node_param_add_on_set_callback(handle->node, &Handle::trampoline,
2247 handle.get(), &handle->token);
2248 if (!r.ok()) {
2249 return nullptr;
2250 }
2251 handle->registered = true;
2252 return handle;
2253}
2254
2255inline void Node::remove_on_set_parameters_callback(
2256 const node_interfaces::OnSetParametersCallbackHandle* handle) {
2257 if (handle != nullptr) {
2258 const_cast<node_interfaces::OnSetParametersCallbackHandle*>(handle)->unregister();
2259 }
2260}
2261
2262namespace node_interfaces {
2263inline bool NodeParametersInterface::has_parameter(const ::std::string& name) const {
2264 return node_ != nullptr && node_->has_parameter(name);
2265}
2266inline ParameterValue NodeParametersInterface::declare_parameter(const ::std::string& name,
2267 const ParameterValue& v) {
2268 return node_ == nullptr ? ParameterValue() : node_->declare_parameter(name, v);
2269}
2270template <typename D>
2271inline ParameterValue NodeParametersInterface::declare_parameter(const ::std::string& name,
2272 const ParameterValue& v,
2273 const D& descriptor) {
2274 return node_ == nullptr ? ParameterValue() : node_->declare_parameter(name, v, descriptor);
2275}
2276inline bool NodeParametersInterface::get_parameter(const ::std::string& name,
2277 Parameter& out) const {
2278 return node_ != nullptr && node_->get_parameter(name, out);
2279}
2280inline Parameter NodeParametersInterface::get_parameter(const ::std::string& name) const {
2281 return node_ == nullptr ? Parameter(name) : node_->get_parameter(name);
2282}
2283inline const ::std::map<::std::string, ParameterValue>&
2284NodeParametersInterface::get_parameter_overrides() const {
2285 using Map = ::std::map<::std::string, ParameterValue>;
2286 // One copy per node, keyed by its FFI handle, so every call (and every
2287 // view of the node) returns the same map. The executor dispatches on one
2288 // task, which is what makes a plain static safe here.
2289 static ::std::map<const void*, Map> copies;
2290 static Map none;
2291 if (node_ == nullptr || node_->ffi_handle() == nullptr) {
2292 return none;
2293 }
2294 struct Sink {
2295 static bool add(const ParameterWrite* write, void* context) {
2296 if (write != nullptr && write->name != nullptr) {
2297 (*static_cast<Map*>(context))[write->name] =
2298 Parameter::from_write(*write).get_parameter_value();
2299 }
2300 return true;
2301 }
2302 };
2303 Map now;
2304 (void)::rclcpp::detail::node_param_list_overrides(node_->ffi_handle(), &Sink::add, &now,
2305 nullptr);
2306 Map& kept = copies[node_->ffi_handle()];
2307 if (kept != now) {
2308 kept = static_cast<Map&&>(now);
2309 }
2310 return kept;
2311}
2312} // namespace node_interfaces
2313
2314} // namespace rclcpp
2315#endif // NROS_CPP_STD
2316
2317// --- Rate / WallRate (phase-417 W2.d) ----------------------------------------
2318//
2319// A FORWARDER onto `rclcpp::spin(remaining_ms, poll_ms)`, which budgets by wall
2320// clock inside `nros_cpp_spin_for` — Rust-side, once.
2321//
2322// The obvious fifteen-line class with its own sleep loop is NOT admissible
2323// here, and the distinction is the whole point of this item: a loop in the
2324// wrapper that spins the executor is RFC-0020 violation class 2, and a wrapper
2325// that BLOCKS without spinning is what RFC-0021 forbids. Same capability, and
2326// only one of the two shapes is allowed. So `sleep()` computes a deadline and
2327// makes exactly one call into the entry point that already exists.
2328//
2329// ADOPT-BOUNDED, and the envelope is load-bearing enough that a ported loop
2330// behaves DIFFERENTLY here even though it compiles unchanged:
2331//
2332// * **Your callbacks RUN during the sleep.** `rclcpp::spin` drives the
2333// executor, so subscriptions and timers registered on the arena fire while
2334// `rate.sleep()` is blocked. Under a single-threaded rclcpp executor they
2335// do not — `Rate::sleep()` there is a pure `sleep_for`, and callbacks only
2336// run when you call `spin_some`. The nano-ros behaviour is the one a
2337// `while (ok()) { work(); rate.sleep(); }` loop usually WANTS (it is why
2338// that loop needs a second thread upstream), but it is not the same
2339// contract, and a callback that races your loop body is the way it shows.
2340// Since phase-417 that includes a `rclcpp::Node`'s OWN timers and
2341// subscriptions: they are executor entities now, so `rate.sleep()` runs
2342// them.
2343// * `Rate` and `WallRate` are the SAME clock. Upstream's `Rate` measures ROS
2344// time and `WallRate` steady time; both here read the monotonic
2345// `nros_cpp_time_ns()`, so a `Rate` does not slow down under a sim clock.
2346// `WallRate` is faithful; `Rate` is `WallRate` under a second name.
2347// * Resolution is ONE MILLISECOND — the FFI budget is `uint32_t` ms — and the
2348// remaining time is rounded UP, so a period below 1 ms cannot be met and a
2349// rate faster than 1 kHz is not expressible.
2350// * Before `rclcpp::init()` there is no executor to spin, so `sleep()` returns
2351// immediately rather than blocking. A rate loop that runs before init busy
2352// spins; upstream would have slept.
2353//
2354// Gated on `<chrono>` alone: the duration constructor and `period()` are spelled
2355// in `std::chrono`, and everything else it touches is an integer.
2356// phase-442 W5 — `Rate` EXISTS ON EVERY TARGET.
2357//
2358// It used to sit inside `#ifdef NROS_CPP_HAS_STD_CHRONO`, which made the whole
2359// type a property of the toolchain: a node written against `rclcpp::Rate`
2360// compiled on a host and vanished on ThreadX. Measured, the gate was paying for
2361// two members out of five — `Rate(double)`, `sleep()` and `reset()` are integer
2362// arithmetic over `nros_cpp_time_ns()` and never needed `<chrono>` at all.
2363//
2364// The two that did are handled differently, and the difference is the rule
2365// RFC-0096 D1 states. `period()` returns `rclcpp::Duration`, which is ours and
2366// exists everywhere, so the type's SHAPE no longer follows a probe. The
2367// `std::chrono` CONSTRUCTOR stays behind the capability macro, which is a gate
2368// on a METHOD — permitted, since `sizeof(Rate)` is `int64_t` + `uint64_t` in
2369// every configuration and two translation units cannot disagree about it.
2370//
2371// That constructor is the one thing here W8 still has to resolve: its parameter
2372// names `std::chrono::duration`, and W8's acceptance is zero `NROS_CPP_HAS_*` in
2373// the tree. Recorded rather than quietly left, because a gated method is exactly
2374// the kind of residue that reads as finished work.
2375
2376namespace rclcpp {
2377
2380class Rate {
2381 public:
2384 explicit Rate(double frequency_hz)
2385 : period_ns_(frequency_hz > 0.0 ? static_cast<int64_t>(1000000000.0 / frequency_hz) : 0) {
2386 reset();
2387 }
2388
2391 explicit Rate(::rclcpp::Duration period) : period_ns_(period.nanoseconds()) {
2392 if (period_ns_ < 0) period_ns_ = 0;
2393 reset();
2394 }
2395
2396#ifdef NROS_CPP_HAS_STD_CHRONO // hosted-family: chrono-interop
2402 template <typename Rep, typename Period>
2403 explicit Rate(std::chrono::duration<Rep, Period> period)
2404 : period_ns_(std::chrono::duration_cast<std::chrono::nanoseconds>(period).count()) {
2405 if (period_ns_ < 0) period_ns_ = 0;
2406 reset();
2407 }
2408#endif
2409
2415 bool sleep() {
2416 if (period_ns_ <= 0) {
2417 return false;
2418 }
2419 const uint64_t now_ns = nros_cpp_time_ns();
2420 if (now_ns >= next_tick_ns_) {
2421 // Overran. Re-anchor on now rather than firing a burst to catch up
2422 // — upstream's `Rate` contract, and the choice the retired
2423 // node-local `pump()` used to make for wall timers before they
2424 // became executor timers (which catch up instead; see the envelope
2425 // on `rclcpp::Timer`).
2426 next_tick_ns_ = now_ns + static_cast<uint64_t>(period_ns_);
2427 return false;
2428 }
2429 const uint64_t remaining_ns = next_tick_ns_ - now_ns;
2430 // Round UP: truncating a 0.4 ms remainder to 0 would turn `sleep()`
2431 // into a busy loop.
2432 uint32_t remaining_ms = static_cast<uint32_t>((remaining_ns + 999999ull) / 1000000ull);
2433 if (remaining_ms == 0) remaining_ms = 1;
2434 next_tick_ns_ += static_cast<uint64_t>(period_ns_);
2435 const int32_t poll_ms = remaining_ms < 10u ? static_cast<int32_t>(remaining_ms) : 10;
2436 (void)::rclcpp::spin_in(remaining_ms, poll_ms);
2437 return true;
2438 }
2439
2441 void reset() { next_tick_ns_ = nros_cpp_time_ns() + static_cast<uint64_t>(period_ns_); }
2442
2449 ::rclcpp::Duration period() const { return ::rclcpp::Duration::from_nanoseconds(period_ns_); }
2450
2451 private:
2452 int64_t period_ns_;
2453 uint64_t next_tick_ns_;
2454};
2455
2459
2460} // namespace rclcpp
2461
2462// issue 2015 — `rclcpp_action::create_client` / `create_server`, at upstream's
2463// paths. Included LAST because they build on everything above
2464// (`detail::require_created`); the umbrella reaches them as it reaches
2465// `rclcpp_action::Client` / `Server`, so `#include <rclcpp/rclcpp.hpp>` alone
2466// is enough for a ported action node, as upstream's transitive includes are.
2467#include "rclcpp_action/create_client.hpp"
2468#include "rclcpp_action/create_server.hpp"
2469// issue 2024 — `rclcpp::ParameterEventHandler`, at upstream's umbrella. LAST for
2470// the same reason: it builds on `Node::create_subscription`.
2471#include "rclcpp/parameter_event_handler.hpp"
2472
2473#endif // NROS_RCLCPP_RCLCPP_HPP
::rclcpp::ClientHandle< S > SharedPtr
Definition client.hpp:142
Definition node.hpp:197
Definition rclcpp.hpp:195
Context()
Definition rclcpp.hpp:198
Context(void *executor)
Definition rclcpp.hpp:199
Context SharedPtr
Definition rclcpp.hpp:197
Result add_pre_shutdown_callback(ShutdownCallback callback, void *context, PreShutdownCallbackHandle *out=nullptr)
The C-callback forms, on every target. See rclcpp::pre_shutdown.
Definition rclcpp.hpp:208
bool remove_pre_shutdown_callback(const PreShutdownCallbackHandle &handle)
true when handle named a live callback.
Definition rclcpp.hpp:226
Context * operator->()
Definition rclcpp.hpp:200
void * executor_handle() const
The executor handle this context names; null for an empty one.
Definition rclcpp.hpp:203
const Context * operator->() const
Definition rclcpp.hpp:201
bool remove_on_shutdown_callback(const OnShutdownCallbackHandle &handle)
Definition rclcpp.hpp:235
bool is_valid() const
Context::is_valid() — named, and not yet shut down.
Definition rclcpp.hpp:205
Result add_on_shutdown_callback(ShutdownCallback callback, void *context, OnShutdownCallbackHandle *out=nullptr)
Definition rclcpp.hpp:217
void add_node(const NodePtr &node, bool notify=true)
Definition rclcpp.hpp:1159
FutureReturnCode spin_until_future_complete(Future &&future, int32_t timeout_ms=-1)
Definition rclcpp.hpp:1153
Definition future.hpp:337
Definition node.hpp:845
const nros_cpp_node_t * ffi_handle() const
Definition node.hpp:1887
Result get_parameter_types(const char *const *names, ::size_t count, ParameterType *out) const
Definition rclcpp.hpp:1955
Result add_on_set_parameters_callback(OnSetParametersCallbackType callback, void *context, OnSetParametersCallbackToken &out_handle)
Definition rclcpp.hpp:2014
::rclcpp::Owned<::rclcpp::PollService< S > > create_service(const char *name, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
bool get_parameter(const char *name, T &out) const
Definition rclcpp.hpp:1874
typename::rclcpp::Subscription< M >::SharedPtr create_subscription(const char *topic, const ::rclcpp::QoS &qos, Cb cb)
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.
::rclcpp::TimerHandle create_wall_timer(::rclcpp::Duration period, Cb cb, const ::rclcpp::CallbackGroup *group=nullptr, bool autostart=true)
bool check_declared_param(const char *name, int passed_type)
Definition node.hpp:2879
Result describe_parameter(const char *name, ParameterDescriptor &out, char *text, ::size_t text_len) const
Definition rclcpp.hpp:1966
T declare_parameter(const char *name, T default_value=T())
rclcpp::Node::declare_parameter<T>(name, default).
Definition rclcpp.hpp:1851
Result remove_on_set_parameters_callback(OnSetParametersCallbackToken handle)
rclcpp::Node::remove_on_set_parameters_callback(handle).
Definition rclcpp.hpp:2021
bool get_parameter_or(const char *name, T &out, T fallback) const
Definition rclcpp.hpp:1935
ParameterType get_parameter_type(const char *name) const
Definition rclcpp.hpp:1947
bool ok() const
Definition node.hpp:969
bool has_parameter(const char *name) const
rclcpp::Node::has_parameter(name).
Definition rclcpp.hpp:1906
void * executor_handle() const
Definition node.hpp:1911
Result list_parameters(const char *prefix, char *out_names, ::size_t name_stride, ::size_t max_names, ::size_t &count) const
Definition rclcpp.hpp:2004
::rclcpp::Handle< Node > SharedPtr
Definition node.hpp:1051
Result set_parameters_atomically(const ParameterWrite *writes, ::size_t count)
Definition rclcpp.hpp:2010
::rclcpp::Owned<::rclcpp::PollClient< S > > create_client(const char *name, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
::rclcpp::SubscriptionHandle<::rclcpp::GenericSubscription > create_generic_subscription(const char *topic, const char *type, const char *type_hash, const ::rclcpp::QoS &qos, Cb callback)
Result set_parameter(const char *name, T value)
Definition rclcpp.hpp:1898
Clock * get_clock()
Definition node.hpp:1859
Result undeclare_parameter(const char *name)
Definition rclcpp.hpp:1943
::rclcpp::Owned<::rclcpp::GenericPublisher > create_generic_publisher(const char *topic, const char *type, const char *type_hash, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
Definition rclcpp.hpp:1383
Definition executor.hpp:91
Definition node.hpp:221
Definition node.hpp:217
Definition executor.hpp:84
Definition publisher.hpp:178
::rclcpp::Owned< Publisher< M > > SharedPtr
Definition publisher.hpp:202
Definition qos.hpp:276
Definition rclcpp.hpp:2380
bool sleep()
Definition rclcpp.hpp:2415
Rate(::rclcpp::Duration period)
Definition rclcpp.hpp:2391
Rate(double frequency_hz)
Definition rclcpp.hpp:2384
void reset()
rclcpp::Rate::reset() — anchor the next tick one period from now.
Definition rclcpp.hpp:2441
::rclcpp::Duration period() const
Definition rclcpp.hpp:2449
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 service.hpp:146
size_t handle_id() const
Executor arena slot for the registration; SIZE_MAX until registered.
Definition service.hpp:282
::rclcpp::ServiceHandle< S > SharedPtr
Definition service.hpp:158
::rclcpp::SubscriptionHandle< M > SharedPtr
Definition subscription.hpp:220
Definition timer.hpp:153
Definition rclcpp.hpp:1624
static const bool value
Definition rclcpp.hpp:1632
The refusal behind MultiThreadedExecutor; see that alias.
Definition rclcpp.hpp:1254
MultiThreadedExecutorRefused(Args &&...)
Definition rclcpp.hpp:1256
void spin_some(uint32_t max_ms=0)
Definition rclcpp.hpp:1246
void spin()
Block until rclcpp::ok() goes false — rclcpp::spin_in().
Definition rclcpp.hpp:1242
void remove_node(const NodePtr &node, bool notify=true)
Definition rclcpp.hpp:1200
FutureReturnCode spin_until_future_complete(Future &&future, int32_t timeout_ms=-1)
Definition rclcpp.hpp:1210
SingleThreadedExecutor()
Definition rclcpp.hpp:1188
void spin_node_once(const ::rclcpp::Node::SharedPtr &node, int32_t timeout_ms)
Definition rclcpp.hpp:1236
void spin_all(uint32_t max_duration_ms)
Definition rclcpp.hpp:1217
void add_node(const ::rclcpp::Node::SharedPtr &node, bool notify=true)
Definition rclcpp.hpp:1192
void spin_node_some(const ::rclcpp::Node::SharedPtr &node)
Definition rclcpp.hpp:1228
Node * node() const
The node this handle names (null for a default-constructed one).
Definition node.hpp:247
Context get_context() const
Definition rclcpp.hpp:318
rclcpp::Future<T> — single-shot deferred result.
int nros_cpp_ret_t
Definition future.hpp:25
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)
Definition rclcpp.hpp:286
Context get_global_default_context()
Definition rclcpp.hpp:289
Definition rclcpp.hpp:418
void check_node_on_executor(const NodePtr &node, void *executor, bool process)
phase-515 P2 — the add_node check, against one executor handle.
Definition rclcpp.hpp:1131
Subscription< M >::SharedPtr create_subscription_dispatch(::rclcpp::Node &node, const char *topic, const ::rclcpp::QoS &qos, Cb cb, ::rclcpp::tr::false_type)
The one-argument callback path (void(const M&), or a hosted pointer shape).
Definition rclcpp.hpp:1322
void abort_failed_create(const char *verb, const char *name, int32_t code)
Definition rclcpp.hpp:478
@ SUB_SHAPE_NONE
Definition subscription.hpp:719
void require_created(::rclcpp::Result r, const char *verb, const char *name)
Definition rclcpp.hpp:493
TimerHandle register_timer_capturing(::rclcpp::Node &node, rcl_clock_type_t clock_type, Duration period, Cb &&cb, const char *verb, const CallbackGroup *group=nullptr, bool autostart=true)
Definition rclcpp.hpp:1680
constexpr bool is_ros_args_flag(char const *s, int i=0)
Definition rclcpp.hpp:425
int32_t apply_ros_args(void *executor, int argc, char const *const *argv)
Definition node.hpp:3495
inline ::rclcpp::Node & as_node_ref(const ::rclcpp::Node::SharedPtr &n)
Definition rclcpp.hpp:1745
Result register_subscription_capturing(::rclcpp::Node &node, const char *topic, const QoS &qos, const Fn &fn, size_t *out_handle_id)
Definition subscription.hpp:928
FutureReturnCode spin_executor_until_future_complete(void *executor, Future &future, int32_t timeout_ms, bool process)
Definition rclcpp.hpp:1113
bool future_is_ready(F &f)
Definition rclcpp.hpp:1058
constexpr bool argv_has_ros_args(int argc, char const *const *argv, int i=0)
Definition rclcpp.hpp:432
Definition rclcpp.hpp:1165
MultiThreadedExecutorRefused<> MultiThreadedExecutor
Definition rclcpp.hpp:1270
Definition rclcpp.hpp:317
NodeInterfaceHandle< 0 > NodeBaseInterface
Definition node.hpp:261
Definition rclcpp.hpp:75
Rate WallRate
Definition rclcpp.hpp:2458
void(*)(void *context) ShutdownCallback
Definition executor.hpp:47
void spin(const Node::SharedPtr &node)
Definition rclcpp.hpp:997
void spin_some(const Node::SharedPtr &node)
Definition rclcpp.hpp:1010
inline ::rclcpp::TimerHandle create_timer(NodeT &&node, ::rclcpp::Clock *clock, ::rclcpp::Duration period, CallbackT &&callback, const ::rclcpp::CallbackGroup *group=nullptr, bool autostart=true)
Definition rclcpp.hpp:1771
Result init_in(const char *locator=nullptr, uint8_t domain_id=0)
Definition node.hpp:3316
Result spin_once(int32_t timeout_ms=10)
Definition rclcpp.hpp:97
FutureReturnCode
Definition rclcpp.hpp:589
@ TIMEOUT
The deadline passed with the future still pending.
Definition rclcpp.hpp:592
@ INTERRUPTED
rclcpp::ok() went false (shutdown) before the future was ready.
Definition rclcpp.hpp:594
@ SUCCESS
Definition rclcpp.hpp:590
void init()
rclcpp::init() — the zero-argument form. ADOPT.
Definition rclcpp.hpp:542
FutureReturnCode spin_until_future_complete(const Node::SharedPtr &node, Future &&future, int32_t timeout_ms=-1)
Definition rclcpp.hpp:1075
bool remove_on_shutdown_callback(OnShutdownCallbackHandle handle)
Definition rclcpp.hpp:178
bool ok()
Definition node.hpp:3564
Result spin_in()
Definition rclcpp.hpp:331
void * global_handle()
Definition node.hpp:3485
Result on_shutdown(ShutdownCallback callback, void *context=nullptr, OnShutdownCallbackHandle *out=nullptr)
Definition rclcpp.hpp:156
bool shutdown()
Definition rclcpp.hpp:559
bool remove_pre_shutdown_callback(PreShutdownCallbackHandle handle)
Definition rclcpp.hpp:170
void arm_monitors()
Definition rclcpp.hpp:113
@ NotInitialized
Definition result.hpp:131
@ Unsupported
Operation not implemented by the active backend.
Definition result.hpp:152
Result pre_shutdown(ShutdownCallback callback, void *context=nullptr, PreShutdownCallbackHandle *out=nullptr)
Definition rclcpp.hpp:137
ResultOf< void > Result
Definition result.hpp:247
rclcpp::Node and global session helpers.
rclcpp::Publisher<M> — typed topic publisher.
rclcpp::QoS — full DDS-shaped QoS settings (Phase 108.B.7).
rclcpp::Client<S> — the arena-registered (callback-style) service client, and Client<S>::SharedPtr = ...
**auto handle
Definition client.hpp:127
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...
rclcpp::Service<S> — the arena-registered (callback-style) service server, and Service<S>::SharedPtr ...
rclcpp::Stream<T> — multi-shot message receiver.
Definition timer.hpp:551
void operator()(const nros_cpp_timer_call_info_t &)
Definition rclcpp.hpp:1649
F f
Definition rclcpp.hpp:1648
Definition rclcpp.hpp:1638
F f
Definition rclcpp.hpp:1639
void operator()(const nros_cpp_timer_call_info_t &raw)
Definition rclcpp.hpp:1640
Definition subscription.hpp:767
Which SubscriptionCallbackShape the callable F is, for message M.
Definition subscription.hpp:761
static constexpr tr::size_type value
Definition rclcpp.hpp:1661
Definition rclcpp.hpp:1657
static constexpr tr::size_type value
Definition rclcpp.hpp:1658
rclcpp::Subscription<M> — the typed DISPATCH subscriber: the arena owns it and the executor calls you...