nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
node.hpp
Go to the documentation of this file.
1// nros-cpp: Node class
2// Freestanding C++ — no exceptions, no STL required
3
9
10#ifndef NROS_RCLCPP_NODE_HPP
11#define NROS_RCLCPP_NODE_HPP
12
13#include <cstdint>
14#include <cstddef>
15#include <type_traits> // Phase 189.M3.3.e — SFINAE on the callback-style create_service
16#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
17#include <cstdlib> // getenv — Phase 123.B.3 env-aware init
18#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
19// <stdio.h> + unqualified `::fprintf` (issue 0942 / phase-472 W5): `<cstdio>` need not put
20// the C names in `std` on a freestanding libstdc++, and `std::fprintf` then fails.
21#include <stdio.h>
22#endif
23#endif
24
25// `<map>` is `NROS_CPP_STD` ONLY, and the `|| __STDC_HOSTED__` arm above is
26// exactly what it must not have (issue 1431).
27//
28// phase-417 W4.a put it in that block beside `<cstdlib>`/`<cstdio>`, which is
29// safe for THOSE: Zephyr's minimal libcpp ships `cstdlib` and `cstdio`, so the
30// hosted arm can be satisfied. It ships no `map`. And `__STDC_HOSTED__` is 1 on
31// Zephyr — the compiler is hosted, the include path is not — so the arm fired
32// and the header failed to open. MEASURED, `g++ -nostdinc++ -I zephyr/cxx-compat`:
33//
34// node.hpp:18:10: fatal error: map: No such file or directory
35//
36// That is issue 0112's half of the both-probes rule, which CLAUDE.md records:
37// `__STDC_HOSTED__` alone is a claim about the COMPILER and the question is
38// about the INCLUDE PATH.
39//
40// The fix is not `__has_include(<map>)`. It is to guard the include with the
41// same condition as the only code that needs it. Both consumers —
42// `Node::declare_parameters` below and `lifecycle.hpp`'s twin, which includes
43// no `<map>` of its own and relies on this one — sit inside
44// `NROS_CPP_HAS_STD_STRING` (phase-476 W3), which implies `NROS_CPP_STD`
45// because every capability probe is `#if defined(NROS_CPP_STD)`. So this is the
46// narrowest guard that still covers every use, and it restores the property
47// the block below states as the rule: a hosted STL include lives inside an
48// `NROS_CPP_STD` region, never one an `||` arm can reach from a freestanding
49// board (issue 0332).
50#if defined(NROS_CPP_STD) // hosted-family: container-interop
51#include <map> // phase-417 W4.a — rclcpp::Node::declare_parameters<T>'s argument
52#endif
53
54// Phase 118.D: ffi.h MUST come before qos.hpp so qos.hpp's
55// `#ifndef NROS_CPP_FFI_H` guard sees the canonical types and skips
56// its local redefinitions.
57#include "rclcpp/detail/nros_cpp_ffi.h"
58
59#include "rclcpp/detail/traits.hpp"
60#include "rclcpp/handle.hpp" // phase-482 W2 — `Node::SharedPtr`
61#include "rclcpp/result.hpp"
62#include "rclcpp/owned.hpp" // phase-456 W5 — the return type of the poll `create_service`
63#include "rclcpp/detail/hosted_block.hpp"
64#include "rclcpp/detail/nros_cpp_config_generated.h"
65// phase-482 W3 — `rclcpp::init` below reads `NROS_ENTRY_LOCATOR` /
66// `NROS_ENTRY_DOMAIN_ID`, so the ladder that derives them from Kconfig must be
67// visible in EVERY TU that calls it, not only in one that also includes
68// `<rclcpp/detail/main.hpp>`. A ported `main.cpp` includes `<rclcpp/rclcpp.hpp>` and
69// nothing else: on a Zephyr guest it compiled with the macro undefined, passed
70// a null locator, and the backend's default dialled the GUEST's own loopback
71// (`ConnectionFailed`, then `create_publisher`'s abort). Where nothing bakes a
72// locator the header yields `""`, which `nros_cpp_init` reads as absent — the
73// same answer a null gave.
74#include "rclc/detail/entry_config.h"
75#include "rclcpp/qos.hpp"
76// Phase 189.M3.1 — rclcpp-style named-options structs
77// (`SubscriptionOptions` / `PublisherOptions`) used by the 4-arg
78// `create_subscription` / `create_publisher` overloads below.
79#include "rclcpp/options.hpp"
80// Phase 84.G8: heavy entity headers (publisher / subscription / service /
81// client / action_server / action_client) are no longer pulled in here.
82// Each entity header provides the out-of-line definition of its
83// corresponding `Node::create_X<T>()` template and includes `node.hpp`
84// itself. Consumers that #include `rclcpp/rclcpp.hpp` (the umbrella) still
85// get every entity + every create method via that path; consumers that
86// only want lightweight Node access can include this header directly
87// and pay for only the light entities (timer, guard_condition,
88// executor) below.
89#include "rclcpp/timer.hpp"
90// Issue 0789 — `Node::get_clock()` / `Node::now()`. `clock.hpp` pulls in
91// `time.hpp` and `duration.hpp`, so a node that stamps a header needs no
92// further include.
93#include "rclcpp/clock.hpp"
94// phase-427 W4 — `ComponentNode` is merged into `Node`, so the two things its
95// header pulled in for the merged members come here with them:
96// * `log.hpp` — `NROS_ERROR`, the OVERRIDABLE sink `report_component_failure`
97// routes through so a freestanding image can be given a boot diagnostic
98// (issue 1015).
99// * `declared_qos.hpp` — `rclcpp::declared_depth` + `DECLARED_DEPTH_UNDECLARED`,
100// read by `Node::check_declared_depth` (phase-403 step 2).
101// Neither includes a nano-ros header of its own, so neither can close a cycle
102// back onto `node.hpp`. `component.hpp` — which DOES include this file — is why
103// the member-pointer `create_subscription_in` / `create_subscription_in_group`
104// family is DECLARED here and DEFINED there.
105#include "rclcpp/detail/declared_qos.hpp"
106#include "rclcpp/detail/declared_params.hpp" // phase-446 W6 -- each node's DECLARED parameters
107// phase-417 W4.a -- `rclcpp::ParameterType`, `ParameterDescriptor`,
108// `ParameterWrite` and the on-set callback types, which the parameter methods
109// below name. Freestanding (`<cstdint>`, `const char*`, the FFI header) and it
110// includes nothing that includes this file. `nros.hpp` included it already;
111// the declarations here are why it moves onto node.hpp's own include list.
112#include "rclcpp/node_parameters.hpp"
113// issue 2056 -- `rclcpp::ParameterValue` / `rclcpp::Parameter`, which the
114// hosted parameter overloads below take and return. It includes
115// `node_parameters.hpp` and the traits header only, so no cycle.
116#include "rclcpp/parameter_value.hpp"
117#include "rclcpp/logging.hpp"
119#include "rclcpp/executor.hpp"
120// phase-417 stage 2b (RFC-0089) — `rclcpp::TopicEndpointInfo` and the visitor
121// typedef used by the graph forwarders below.
122#include "rclcpp/graph.hpp"
123// Phase 273 (RFC-0047) — callback-group token (value type, no heap).
124#include "rclcpp/callback_group.hpp"
125// phase-427 W1 declared the parameter facade here; phase-426 W4 took away the
126// store it read. The hosted block no longer holds a `ParameterServer` — the
127// facade forwards to the EXECUTOR's store — so this header needs
128// `parameter.hpp` for nothing of its own. It is kept because what that header
129// still holds (`rclcpp::Seq<T, N>`) is a public surface a consumer reaches
130// through the node header, and `parameter.hpp` depends on `result.hpp` and
131// `<nros/parameter.h>` only,
132// so there is no cycle.
133#include "rclcpp/parameter.hpp"
134
135// `<chrono>` is the one capability whose predicate is not the uniform one —
136// it needs `<ratio>` as a prerequisite too. The three measured corrections that
137// established that live with the predicate, at the one detection site.
138#include "rclcpp/detail/std_detect.hpp"
139
140// Issue 2081 — named by `Node::register_service_`'s request-id overload;
141// DEFINED in `rclcpp/service.hpp` (under the guard the RMW ABI and
142// `<rclc/detail/request_id.h>` share).
143struct rmw_request_id_t;
144
145// phase-476 W3 — the hosted-node macro (`NROS_CPP_` + ~~`NODE_HOSTED`~~) IS GONE. It was the
146// conjunction of all four capability probes, wrapped around every hosted-shape member at once. Each
147// member now names the ONE capability its signature actually uses: the `std::string`-keyed
148// forwarders `NROS_CPP_HAS_STD_STRING`, the `std::shared_ptr` spellings `NROS_CPP_HAS_SHARED_PTR`,
149// and nothing needs
150// `<vector>` or `<functional>` any more. The rule it served is unchanged: a
151// probe gates METHODS, never a member.
152
153// ~~`NROS_RCLCPP_MAX_PARAMS`~~ IS GONE (phase-426 W4). It sized an inline
154// `nros::ParameterServer` on the merged node's hosted block — a SECOND
155// parameter store, node-local, which `ros2 param get` could not see and a
156// sibling node did not share. The member is deleted and the facade forwards to
157// the executor's store (`rclcpp/node_parameters.hpp`), so there is no per-node
158// arena left for a number to bound.
159//
160// The image's one parameter arena is now `NROS_MAX_PARAMETERS` on
161// `nros-params` (default 32, a build knob), and it is shared across every node
162// on the executor rather than multiplied by the node count. Do not reintroduce
163// a per-facade capacity: a second number is how a second store starts.
164
165// RFC-0071 D9 (phase-506 W4) — no per-backend `nros_rmw_<x>_register()`
166// declarations here. Nothing in these headers called them: registration is the
167// generated strong `nros_app_register_backends()`, and a caller that registers
168// by hand includes its backend's own header.
169
170// Issue #229 pin (cross-space, C++-FFI half): ErrorCode must stay
171// value-identical to the NROS_CPP_RET_* codes (nros_cpp_ffi.h, included
172// above) that every shim below feeds into Result().
173static_assert(NROS_CPP_RET_NOT_FOUND == -4 && NROS_CPP_RET_ALREADY_EXISTS == -5 &&
174 NROS_CPP_RET_FULL == -6 && NROS_CPP_RET_NOT_INIT == -7 &&
175 NROS_CPP_RET_TRY_AGAIN == -14 && NROS_CPP_RET_REENTRANT == -15 &&
176 NROS_CPP_RET_UNSUPPORTED == -16,
177 "nros_cpp_ret_t diverged from the shared numbering (issue #229)");
178
179// Phase 84.G8: forward declarations of the heavy entity class
180// templates. Full definitions live in the corresponding `*.hpp`,
181// which also provide the out-of-line `Node::create_X<>` template
182// bodies — consumers only pay for the entities they #include.
183//
184// phase-428 (RFC-0089): six of them are DECLARED in the upstream namespace,
185// because that is where their definition now lives, and this header names them
186// that way. (Each also had an `nros::` migration alias, declared beside its
187// type in that type's own header, until phase-483 W1 deleted them.)
188namespace rclcpp {
189// phase-427 W7 — the node itself, defined further down in this header. Forward
190// declared up here because the `rclcpp::` free functions BELOW take it by
191// reference and are named by `Node`'s own qualified friend declarations, which
192// require a prior declaration of the function AND of its parameter type.
193class Node;
194template <typename M> class Publisher;
195template <typename M> class Subscription;
196template <typename S> class Service;
197template <typename S> class Client;
198class GenericPublisher; // phase-515 P4 — `rclcpp/generic.hpp`
199class GenericSubscription; // phase-515 P4
200template <typename M> class SubscriptionHandle;
201} // namespace rclcpp
202
203namespace rclcpp_action {
204template <typename A> class Server;
205template <typename A> class Client;
206} // namespace rclcpp_action
207
208namespace rclcpp {
209
210// Phase 122.3.d.b — L1 polling-mode action wrappers.
211template <typename A> class PollingActionServer;
212template <typename A> class PollingActionClient;
213// phase-456 W2b — the two poll-side subscribers (`polling_subscription.hpp`).
214template <typename M> class PollSubscription;
215template <typename M> class PollingSubscription;
216// phase-456 W5 — the poll-side service server (`polling_service.hpp`).
217template <typename S> class PollService;
218// phase-456 W9 — the poll-side (future-style) service client
219// (`polling_client.hpp`), and the dispatch client's handle
220// (`client_handle.hpp`).
221template <typename S> class PollClient;
222template <typename S> class ClientHandle;
223
224class Node;
225class Context;
226
237namespace node_interfaces {
238
240template <int Tag> class NodeInterfaceHandle {
241 public:
244 NodeInterfaceHandle() : node_(nullptr) {}
245 explicit NodeInterfaceHandle(Node* node) : node_(node) {}
247 Node* node() const { return node_; }
248 explicit operator bool() const { return node_ != nullptr; }
251 const NodeInterfaceHandle* operator->() const { return this; }
255 inline Context get_context() const;
256
257 private:
258 Node* node_;
259};
260
266
267#ifdef NROS_CPP_STD // hosted-family: parameter-interop
286class NodeParametersInterface {
287 public:
288 using SharedPtr = NodeParametersInterface;
289 NodeParametersInterface() : node_(nullptr) {}
290 explicit NodeParametersInterface(Node* node) : node_(node) {}
291 NodeParametersInterface* operator->() { return this; }
292 const NodeParametersInterface* operator->() const { return this; }
293 explicit operator bool() const { return node_ != nullptr; }
294 Node* node() const { return node_; }
295
296 inline bool has_parameter(const ::std::string& name) const;
297 inline ParameterValue declare_parameter(const ::std::string& name,
298 const ParameterValue& default_value);
299 template <typename D>
300 inline ParameterValue declare_parameter(const ::std::string& name,
301 const ParameterValue& default_value,
302 const D& descriptor);
303 inline bool get_parameter(const ::std::string& name, Parameter& out) const;
304 inline Parameter get_parameter(const ::std::string& name) const;
305 inline const ::std::map<::std::string, ParameterValue>& get_parameter_overrides() const;
306
307 private:
308 Node* node_;
309};
310
320struct OnSetParametersCallbackHandle {
321 using SharedPtr = ::std::shared_ptr<OnSetParametersCallbackHandle>;
322 using WeakPtr = ::std::weak_ptr<OnSetParametersCallbackHandle>;
326 using OnParametersSetCallbackType = ::std::function<bool(const ::std::vector<Parameter>&)>;
327
328 OnSetParametersCallbackHandle() : node(nullptr), token(0), registered(false) {}
329 OnSetParametersCallbackHandle(const OnSetParametersCallbackHandle&) = delete;
330 OnSetParametersCallbackHandle& operator=(const OnSetParametersCallbackHandle&) = delete;
331 ~OnSetParametersCallbackHandle() { unregister(); }
332
334 void unregister() {
335 if (registered) {
336 (void)::rclcpp::detail::node_param_remove_on_set_callback(node, token);
337 registered = false;
338 }
339 }
340
342 static bool trampoline(const ParameterWrite* write, void* context) {
343 auto* self = static_cast<OnSetParametersCallbackHandle*>(context);
344 if (write == nullptr || self == nullptr || !self->callback) {
345 return true;
346 }
347 ::std::vector<Parameter> params;
348 params.push_back(Parameter::from_write(*write));
349 return self->callback(params);
350 }
351
352 OnParametersSetCallbackType callback;
353 const nros_cpp_node_t* node;
354 OnSetParametersCallbackToken token;
355 bool registered;
356};
357#endif // NROS_CPP_STD
358
359} // namespace node_interfaces
360
401 void* executor;
405 const char* launch_name;
407 const char* launch_namespace;
418 const nros_cpp_qos_override_t* qos_overrides;
421
422 constexpr NodeHandle()
423 : executor(nullptr), launch_name(nullptr), launch_namespace(nullptr),
424 qos_overrides(nullptr), qos_override_count(0) {}
425 explicit constexpr NodeHandle(void* exec)
426 : executor(exec), launch_name(nullptr), launch_namespace(nullptr), qos_overrides(nullptr),
428
430 constexpr NodeHandle(void* exec, const char* name, const char* ns)
431 : executor(exec), launch_name(name), launch_namespace(ns), qos_overrides(nullptr),
433
436 constexpr NodeHandle(void* exec, const char* name, const char* ns,
437 const nros_cpp_qos_override_t* overrides, size_t override_count)
438 : executor(exec), launch_name(name), launch_namespace(ns), qos_overrides(overrides),
439 qos_override_count(override_count) {}
440 constexpr bool valid() const { return executor != nullptr; }
441
444 constexpr const char* resolve_name(const char* fallback) const {
445 return (launch_name != nullptr && launch_name[0] != '\0') ? launch_name : fallback;
446 }
447
448 constexpr const char* resolve_namespace(const char* fallback) const {
449 return (launch_namespace != nullptr && launch_namespace[0] != '\0') ? launch_namespace
450 : fallback;
451 }
452};
453
461// `constexpr` at namespace scope is implicitly const, so it already has internal
462// linkage per TU — `inline` bought nothing and cost C++14 compatibility, which
463// nano-ros otherwise keeps (see `just check cpp`'s freestanding c++14 syntax
464// gate). PX4 builds every module with -std=gnu++14 -Werror, so an inline
465// variable here made <rclcpp/rclcpp.hpp> uncompilable in a PX4 module (phase-325 W2).
466constexpr uint8_t kDomainIdExplicitZero = 255;
467
468namespace detail {
469
480inline void report_component_failure(const char* node_name, const char* what, int32_t code) {
481 // Route through the OVERRIDABLE sink first, so a freestanding image can
482 // see this at all. Issue 1015's bisect ran aground here: on Zephyr both
483 // arms below compile to nothing, so a component that failed to register
484 // halted SILENTLY -- the board printed only the Zephyr banner, identical
485 // to a healthy boot, and "is it broken?" had no answer on the target where
486 // it mattered. Issue 0589 fixed exactly this class for the Rust side; the
487 // C++ side still had it.
488 NROS_ERROR("node \"%s\": FAILED at %s (code=%d)", (node_name != nullptr) ? node_name : "?",
489 (what != nullptr) ? what : "?", static_cast<int>(code));
490#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
491 ::fprintf(stderr, "[nros] FATAL: node \"%s\" failed to construct at %s (code=%d)\n",
492 (node_name != nullptr) ? node_name : "?", (what != nullptr) ? what : "?",
493 static_cast<int>(code));
494#endif
495}
496
504constexpr int32_t DECLARED_DEPTH_MISMATCH = -403;
505
509inline void report_declared_depth_mismatch(const char* node_name, const char* topic, int declared,
510 int passed) {
511 NROS_ERROR("node \"%s\": topic \"%s\" DECLARED depth %d but the QoS "
512 "passed states %d. Depth multiplies the executor arena, so they must agree.",
513 (node_name != nullptr) ? node_name : "?", (topic != nullptr) ? topic : "?", declared,
514 passed);
515#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
516 ::fprintf(stderr,
517 "[nros] FATAL: node \"%s\": topic \"%s\" was DECLARED depth %d in "
518 "the contract sidecar (<stem>.contract.yaml) but the QoS passed to "
519 "create_subscription_in states depth %d. Depth multiplies the executor "
520 "arena (cost is (depth+1)*bound per subscription), so the two must agree. "
521 "Fix the contract row or the call site.\n",
522 (node_name != nullptr) ? node_name : "?", (topic != nullptr) ? topic : "?", declared,
523 passed);
524#else
525 (void)node_name;
526 (void)topic;
527 (void)declared;
528 (void)passed;
529#endif
530}
531
538inline void report_callback_group_mismatch(const char* node_name, const char* how, const char* name,
539 int len, const char* list) {
540 const char* node = (node_name != nullptr) ? node_name : "?";
541 const char* group = (name != nullptr) ? name : "?";
542 const char* declared = (list != nullptr) ? list : "";
543 // "created": the code made a group the keyword never declared.
544 // "declared": the keyword declared a group the constructor never made.
545 const bool created = (how != nullptr && how[0] == 'c');
546 if (created) {
547 NROS_ERROR("node \"%s\": created callback group \"%.*s\", not in CALLBACK_GROUPS \"%s\"",
548 node, len, group, declared);
549 } else {
550 NROS_ERROR("node \"%s\": CALLBACK_GROUPS \"%s\" declares \"%.*s\", never created", node,
551 declared, len, group);
552 }
553#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
554 ::fprintf(stderr,
555 created ? "[nros] FATAL: node \"%s\" created callback group \"%.*s\", which its "
556 "registration's CALLBACK_GROUPS \"%s\" does not declare.\n"
557 : "[nros] FATAL: node \"%s\" never created callback group \"%.*s\", which "
558 "its registration's CALLBACK_GROUPS \"%s\" declares.\n",
559 node, len, group, declared);
560 ::fprintf(stderr,
561 " The tier derivation ranks the declared list and the executor binds what the "
562 "code creates, so the two must name the same groups (ONE declared group and none "
563 "created is node-level placement, and is allowed). Fix the "
564 "nano_ros_node_register(... CALLBACK_GROUPS ...) line or the create_callback_group "
565 "call.\n");
566#endif
567}
568
575
579inline void report_declared_policy_mismatch(const char* node_name, const char* topic,
580 const char* policy, const char* declared,
581 const char* passed) {
582 NROS_ERROR("node \"%s\": topic \"%s\" DECLARED %s %s but the QoS passed states %s. An "
583 "incompatible-QoS match never delivers, so they must agree.",
584 (node_name != nullptr) ? node_name : "?", (topic != nullptr) ? topic : "?", policy,
585 declared, passed);
586#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
587 ::fprintf(stderr,
588 "[nros] FATAL: node \"%s\": topic \"%s\" was DECLARED %s %s in the contract "
589 "sidecar (<stem>.contract.yaml) but the QoS passed to create_subscription_in "
590 "states %s. Fix the contract row or the call site.\n",
591 (node_name != nullptr) ? node_name : "?", (topic != nullptr) ? topic : "?", policy,
592 declared, passed);
593#else
594 (void)node_name;
595 (void)topic;
596 (void)policy;
597 (void)declared;
598 (void)passed;
599#endif
600}
601
603inline const char* declared_reliability_spelling(int ordinal) {
604 return ordinal == static_cast<int>(::rclcpp::ReliabilityPolicy::Reliable) ? "reliable"
605 : ordinal == static_cast<int>(::rclcpp::ReliabilityPolicy::BestEffort)
606 ? "best_effort"
607 : "system_default";
608}
609
611inline const char* declared_durability_spelling(int ordinal) {
612 return ordinal == static_cast<int>(::rclcpp::DurabilityPolicy::Volatile) ? "volatile"
613 : ordinal == static_cast<int>(::rclcpp::DurabilityPolicy::TransientLocal)
614 ? "transient_local"
615 : "system_default";
616}
617
620constexpr int32_t DECLARED_PARAM_MISMATCH = -446;
621
626inline void report_declared_param_mismatch(const char* fqn, const char* param, const char* contract,
627 int declared, int passed) {
628 const char* f = (fqn != nullptr) ? fqn : "?";
629 const char* p = (param != nullptr) ? param : "?";
630 const char* c = (contract != nullptr) ? contract : "?";
631 if (declared == ::rclcpp::DECLARED_PARAM_UNDECLARED) {
632 NROS_ERROR("node \"%s\": parameter \"%s\" is not declared in its contract (%s).", f, p, c);
633 } else {
634 NROS_ERROR("node \"%s\": parameter \"%s\" is declared %s in its contract (%s) but the "
635 "code declares it as %s.",
636 f, p, ::rclcpp::declared_param_type_name(declared), c,
637 ::rclcpp::declared_param_type_name(passed));
638 }
639#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
640 if (declared == ::rclcpp::DECLARED_PARAM_UNDECLARED) {
641 ::fprintf(stderr,
642 "[nros] FATAL: node \"%s\": parameter \"%s\" is not declared in its "
643 "contract (%s, `params:`). The parameter store is sized from that "
644 "declaration, so a parameter it does not name has no slot. Declare it "
645 "there with its type, or stop declaring it in the code.\n",
646 f, p, c);
647 } else {
648 ::fprintf(stderr,
649 "[nros] FATAL: node \"%s\": parameter \"%s\" is declared `%s` in its "
650 "contract (%s) but the code declares it as `%s`. Fix the contract or "
651 "the code so they state one type.\n",
652 f, p, ::rclcpp::declared_param_type_name(declared), c,
653 ::rclcpp::declared_param_type_name(passed));
654 }
655#endif
656}
657
658// phase-476 W3 — ~~`detail::NodeHosted`~~ IS GONE. It was the lazily allocated
659// block behind `Node::hosted_`, and by W3 it held one member:
660// `rclcpp::NodeOptions`, a type with no data members (every accessor is a
661// REFUSE-LOUD template, `options.hpp`). Allocating a heap block to store an
662// empty object bought nothing, so `get_node_options()` answers a constant and
663// the block, the member and the `operator new` go. (issue 1975 later gave
664// `NodeOptions` one real field, `clock_type`, kept inline on the node.)
665
666} // namespace detail
667
678inline Result init_in(const char* locator = nullptr, uint8_t domain_id = 0);
679
699inline Result init_in(const char* locator, uint8_t domain_id, const char* session_name);
700
719inline Result init_in(const char* locator, uint8_t domain_id, const char* session_name,
720 const char* node_namespace);
721
739inline Result init_with_rmw(const char* rmw, const char* locator = nullptr, uint8_t domain_id = 0,
740 const char* session_name = "node");
741
764inline Result init_in(int argc, char const* const* argv, const char* session_name = nullptr);
765
769inline Result shutdown_in();
770
771// phase-427 W7 — the remaining `rclcpp::` free functions `Node` befriends. A
772// QUALIFIED friend declaration (`friend Result(::rclcpp::ok)();`) names an
773// existing entity; it cannot introduce one. These four live in `nros.hpp`,
774// which includes this header, so without these declarations the friendship
775// would be unresolvable and the private `executor_handle_` unreachable from the
776// functions whose whole job is to set it. Default arguments stay on the
777// definitions — a default may be added by a later declaration, never repeated.
778inline bool ok();
779inline Result create_node(::rclcpp::Node& out, const char* name, const char* ns);
780inline Result create_node_on(::rclcpp::Node& out, void* executor_handle, const char* name,
781 const char* ns);
782inline Result spin_once(int32_t timeout_ms);
783inline Result spin_in();
784inline Result spin_in(uint32_t duration_ms, int32_t poll_ms);
785inline void* global_handle();
786
787} // namespace rclcpp
788
789// ============================================================================
790// rclcpp:: — the HOME of the C++ node type (RFC-0089 §"Settled: `rclcpp::` is
791// the HOME, not an alias onto `nros::`"). phase-427 W7 flipped the direction:
792// the class was DEFINED here and `nros::Node` became the migration alias, the
793// same shape the other nine types already carried (`clock.hpp`, `duration.hpp`,
794// `time.hpp`, `publisher.hpp`, `subscription.hpp`, `client.hpp`, `service.hpp`,
795// `action_client.hpp`, `action_server.hpp`). phase-483 W1 deleted the aliases;
796// `rclcpp::` is now the only namespace.
797//
798// `Node` was the TENTH and was held back because PRs #797 and #806 were stacked
799// on this header. Both merged. The measurement that blocked it before — the
800// parity extractor rooting our native surface at `{"nros"}` alone, so a class
801// defined here re-bucketed ~60 members to `theirs-only` — is gone too:
802// `scripts/api-parity.py` roots every C++ TU at `OUR_CPP_ROOTS`, both halves of
803// the vocabulary we ship.
804//
805// The ours-only names the body below reaches (`ErrorCode`, `detail::*`,
806// `NodeHandle`, `SchedContext`, the polling entity templates) are spelled
807// `::rclcpp::`-qualified. Until phase-483 W1 that qualifier was `::nros::` and
808// told an adopted name from an ours-only one; the namespace no longer carries
809// that distinction.
810// ============================================================================
811namespace rclcpp {
845class Node {
846 public:
849 Node() : handle_(), initialized_(false), executor_handle_(nullptr), clock_(RCL_ROS_TIME) {}
850
851 // ==== phase-427 W2 — construction ======================================
852 //
853 // Upstream constructs a node from a name and THROWS on failure. RFC-0018
854 // forbids exceptions here, so the constructor is kept [clause 2] and the
855 // failure channel changes [clause 1]: the node records the failure and
856 // `ok()` reports it.
857
866 explicit Node(const char* name, const char* ns = nullptr)
867 : handle_(), initialized_(false), executor_handle_(nullptr), clock_(RCL_ROS_TIME) {
868 (void)this->init(name, ns);
869#if defined(NROS_AMENT_START_PARAMETER_SERVICES)
870 // phase-488 — upstream starts the six parameter services in every node
871 // (`NodeOptions::start_parameter_services` defaults to true). The ament
872 // verbs define this when the runtime carries `param_services`, so a
873 // ported node gets them with no call of ours; elsewhere the entry or
874 // the user calls `start_parameter_services()`. Idempotent per executor.
875 if (initialized_) (void)this->start_parameter_services();
876#endif
877 }
878
884 Result init(const char* name, const char* ns = nullptr) {
885 if (initialized_) return Result(::rclcpp::ErrorCode::AlreadyExists);
886 if (!Node::global_initialized()) return Result(::rclcpp::ErrorCode::NotInitialized);
887 executor_handle_ = Node::global_storage();
888 return Node::create(*this, name, ns);
889 }
890
895 Result init_on(void* executor_handle, const char* name, const char* ns = nullptr) {
896 if (initialized_) return Result(::rclcpp::ErrorCode::AlreadyExists);
898 executor_handle_ = executor_handle;
899 return Node::create(*this, name, ns);
900 }
901
929 explicit Node(::rclcpp::NodeHandle handle, const char* name, const char* ns = nullptr)
930 : handle_(), initialized_(false), executor_handle_(nullptr), clock_(RCL_ROS_TIME) {
931 if (!handle.valid()) {
932 this->set_error("ctor (null executor handle)", -1);
933 return;
934 }
935 Result r =
936 this->init_on(handle.executor, handle.resolve_name(name), handle.resolve_namespace(ns));
937 if (!r.ok()) {
938 this->set_error("node create", r.raw());
939 return;
940 }
941 // Issue 1854 -- the launch's QoS overrides, installed before the
942 // derived constructor body creates a single entity (a base is fully
943 // constructed before the derived body runs), which is the ordering
944 // the `configure` shape gets from the entry's own call.
945 if (handle.qos_overrides != nullptr && handle.qos_override_count != 0) {
946 nros_cpp_ret_t qrc = ::nros_cpp_node_set_qos_overrides(&handle_, handle.qos_overrides,
947 handle.qos_override_count);
948 if (qrc != NROS_CPP_RET_OK) {
949 this->set_error("set_qos_overrides", static_cast<int32_t>(qrc));
950 }
951 }
952 }
953
969 bool ok() const { return initialized_ && !__atomic_load_n(&has_error_, __ATOMIC_ACQUIRE); }
970
973 const char* error_what() const {
974 // Acquire-fence on the flag so a standalone call (not preceded by
975 // ok()) still sees the released `error_what_` write (issue #230).
976 (void)__atomic_load_n(&has_error_, __ATOMIC_ACQUIRE);
977 return error_what_;
978 }
979
980 int32_t error_code() const {
981 (void)__atomic_load_n(&has_error_, __ATOMIC_ACQUIRE);
982 return error_code_;
983 }
984
985 // ==== phase-427 W1/W3 — the HOSTED shape ================================
986 //
987 // Upstream's own signatures, verbatim, on the same type. Everything below
988 // is spelled in `std::shared_ptr` / `std::string` / `std::vector` /
989 // `std::function`, so where those are absent the SIGNATURES are absent with
990 // them — a gate on METHODS, never on a member.
991 //
992 // ONE NAME, TWO SIGNATURES (RFC-0089 §"Entity creation"). The out-ref
993 // family above and the `shared_ptr` family below are OVERLOADS, and the
994 // compiler separates them without ambiguity because the hosted forms put
995 // `M` only in the return type — so a call that passes an out-param deduces
996 // nothing for them and they drop out, while an explicit
997 // `create_publisher<M>("chatter", 10)` cannot bind an out-ref parameter to
998 // a string literal. On a FREESTANDING target the hosted overload does not
999 // exist at all, so that same ported line fails to compile naming the
1000 // out-ref overload — mechanical, which is what clause 2 asks for.
1001 //
1002 // phase-476 W3 — each member below is gated on the ONE capability its
1003 // signature uses, not on a conjunction of all four.
1004
1008 Node(const char* name, const ::rclcpp::NodeOptions& options) : Node(name, nullptr) {
1009 apply_options_(options);
1010 }
1011
1015 Node(const char* name, const char* ns, const ::rclcpp::NodeOptions& options) : Node(name, ns) {
1016 apply_options_(options);
1017 }
1018
1022 const ::rclcpp::NodeOptions& get_node_options() const { return options_; }
1023
1024#ifdef NROS_CPP_HAS_STD_STRING // hosted-family: string-interop
1026 explicit Node(const ::std::string& name) : Node(name.c_str(), nullptr) {}
1027
1029 Node(const ::std::string& name, const ::rclcpp::NodeOptions& options)
1030 : Node(name.c_str(), options) {}
1031
1033 Node(const ::std::string& name, const ::std::string& ns,
1034 const ::rclcpp::NodeOptions& options = ::rclcpp::NodeOptions())
1035 : Node(name.c_str(), ns.c_str(), options) {}
1036#endif // NROS_CPP_HAS_STD_STRING
1037
1051 using SharedPtr = ::rclcpp::Handle<Node>;
1053 using ConstSharedPtr = ::rclcpp::Handle<const Node>;
1056 using WeakPtr = ::rclcpp::Handle<Node>;
1058 using ConstWeakPtr = ::rclcpp::Handle<const Node>;
1059
1074
1080 return ::rclcpp::node_interfaces::NodeBaseInterface(this);
1081 }
1082
1084 return ::rclcpp::node_interfaces::NodeClockInterface(this);
1085 }
1086
1088 return ::rclcpp::node_interfaces::NodeGraphInterface(this);
1089 }
1090
1092 return ::rclcpp::node_interfaces::NodeLoggingInterface(this);
1093 }
1094
1096 return ::rclcpp::node_interfaces::NodeWaitablesInterface(this);
1097 }
1098
1099#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
1114 template <typename... Args> static ::std::shared_ptr<Node> make_shared(Args&&... args) {
1115 return ::std::make_shared<Node>(static_cast<Args&&>(args)...);
1116 }
1117
1119 template <typename... Args> static ::std::unique_ptr<Node> make_unique(Args&&... args) {
1120 return ::std::unique_ptr<Node>(new Node(static_cast<Args&&>(args)...));
1121 }
1122#endif // NROS_CPP_HAS_SHARED_PTR
1123
1127 bool initialized() const { return initialized_; }
1128
1131 Node& nros_node() { return *this; }
1133 const Node& nros_node() const { return *this; }
1134
1135#ifdef NROS_CPP_HAS_STD_STRING // hosted-family: string-interop
1136 // -- entity creation, upstream's signatures (bodies in `nros.hpp`) -------
1137
1143 template <typename M>
1144 typename ::rclcpp::Publisher<M>::SharedPtr create_publisher(const ::std::string& topic,
1145 const ::rclcpp::QoS& qos);
1146
1148 template <typename M>
1149 typename ::rclcpp::Publisher<M>::SharedPtr create_publisher(const ::std::string& topic,
1150 ::size_t depth);
1151
1154 template <typename M, typename Cb>
1155 typename ::rclcpp::Subscription<M>::SharedPtr
1156 create_subscription(const ::std::string& topic, const ::rclcpp::QoS& qos, Cb cb);
1157
1159 template <typename M, typename Cb>
1160 typename ::rclcpp::Subscription<M>::SharedPtr create_subscription(const ::std::string& topic,
1161 ::size_t depth, Cb cb);
1162
1164 template <typename M, typename Cb>
1165 typename ::rclcpp::Subscription<M>::SharedPtr
1166 create_subscription(const ::std::string& topic, const ::rclcpp::QoS& qos, Cb cb,
1167 const ::rclcpp::SubscriptionOptions& options) {
1168 return this->create_subscription<M>(topic.c_str(), qos, ::rclcpp::tr::forward_rvalue(cb),
1169 options);
1170 }
1171
1173 template <typename M, typename Cb>
1174 typename ::rclcpp::Subscription<M>::SharedPtr
1175 create_subscription(const ::std::string& topic, ::size_t depth, Cb cb,
1176 const ::rclcpp::SubscriptionOptions& options) {
1177 return this->create_subscription<M>(topic.c_str(), depth, ::rclcpp::tr::forward_rvalue(cb),
1178 options);
1179 }
1180
1191 template <typename S>
1192 ::rclcpp::Owned<::rclcpp::PollService<S>>
1193 create_service(const ::std::string& name, const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1194
1196 template <typename S, typename F,
1197 typename = typename std::enable_if<std::is_convertible<
1198 F, void (*)(const typename S::Request&, typename S::Response&)>::value>::type>
1199 typename ::rclcpp::Service<S>::SharedPtr
1200 create_service(const ::std::string& name, F callback,
1201 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1202
1208 template <typename S, typename F,
1209 typename = typename std::enable_if<
1211 !std::is_convertible<F, void (*)(const typename S::Request&,
1212 typename S::Response&)>::value>::type,
1213 typename = void>
1214 typename ::rclcpp::Service<S>::SharedPtr
1215 create_service(const ::std::string&, F, const ::rclcpp::QoS& = ::rclcpp::QoS::services());
1216
1218 template <typename S, typename F>
1219 typename ::rclcpp::Service<S>::SharedPtr create_service(const ::std::string& name, F callback,
1220 const ::rclcpp::QoS& qos,
1221 const ::rclcpp::CallbackGroup* group) {
1222 return this->template create_service<S>(name.c_str(),
1223 ::rclcpp::tr::forward_rvalue(callback), qos, group);
1224 }
1225
1242 template <typename S>
1243 ::rclcpp::Owned<::rclcpp::PollClient<S>>
1244 create_client(const ::std::string& name, const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1245
1247 template <typename S, typename F,
1248 typename = typename std::enable_if<
1249 std::is_convertible<F, void (*)(const typename S::Response&)>::value>::type>
1250 typename ::rclcpp::Client<S>::SharedPtr
1251 create_client(const ::std::string& name, F callback,
1252 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1253
1255 template <typename S, typename F,
1256 typename = typename std::enable_if<
1258 !std::is_convertible<F, void (*)(const typename S::Response&)>::value>::type,
1259 typename = void>
1260 typename ::rclcpp::Client<S>::SharedPtr
1261 create_client(const ::std::string&, F, const ::rclcpp::QoS& = ::rclcpp::QoS::services());
1262
1263#endif // NROS_CPP_HAS_STD_STRING
1264
1265 // -- the ported publisher line, on EVERY target — phase-456 W5 ----------
1266 //
1267 // OUTSIDE the hosted gate (phase-476 W3 deleted it), and that is the point
1268 // rather than a placement detail. `create_publisher<M>("chatter", 10)` is the single most
1269 // copied line in the porting corpus, and until W5 it was gated: the return
1270 // type was `std::shared_ptr<Publisher<M>>`, so on a minimal freestanding
1271 // libcpp the overload did not exist and the line did not compile.
1272 // `ported_create_publisher_freestanding_probe.cpp` asserted exactly that,
1273 // and the lane's own error text named the reason — "the hosted overload
1274 // returns std::shared_ptr and there is no allocator here".
1275 //
1276 // The return type is `Publisher<M>::SharedPtr` = `rclcpp::Owned<Publisher<M>>`
1277 // now, which needs no allocator and exists everywhere, so the reason is
1278 // gone and the gate goes with it. RFC-0096 D1: ONE API, byte-for-byte the
1279 // same shape on every target, reached by removing what made it differ.
1280 //
1281 // The `const char*` key is what makes it freestanding — a `std::string`
1282 // parameter would have re-imposed the gate through the argument instead of
1283 // the return. A string literal binds here exactly (array-to-pointer), so a
1284 // ported call reaches this overload on a hosted target too; the
1285 // `std::string` forwarders above stay for a caller that already holds one.
1286
1288 template <typename M>
1289 typename ::rclcpp::Publisher<M>::SharedPtr
1290 create_publisher(const char* topic,
1291 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile());
1292
1294 template <typename M>
1295 typename ::rclcpp::Publisher<M>::SharedPtr create_publisher(const char* topic, ::size_t depth);
1296
1297 // phase-476 W1 — the DISPATCH verbs join it, for the same reason. Their
1298 // results have been freestanding handles since phase-456 (the arena holds
1299 // the subscription, the server, the client and the callable), so the
1300 // `std::string` key was the only thing still keeping them hosted. The
1301 // `std::string` overloads in the hosted block forward here.
1302
1305 template <typename M, typename Cb>
1306 typename ::rclcpp::Subscription<M>::SharedPtr
1307 create_subscription(const char* topic, const ::rclcpp::QoS& qos, Cb cb);
1308
1310 template <typename M, typename Cb>
1311 typename ::rclcpp::Subscription<M>::SharedPtr create_subscription(const char* topic,
1312 ::size_t depth, Cb cb);
1313
1320 template <typename M, typename Cb>
1321 typename ::rclcpp::Subscription<M>::SharedPtr
1322 create_subscription(const char* topic, const ::rclcpp::QoS& qos, Cb cb,
1323 const ::rclcpp::SubscriptionOptions& options);
1324
1326 template <typename M, typename Cb>
1327 typename ::rclcpp::Subscription<M>::SharedPtr
1328 create_subscription(const char* topic, ::size_t depth, Cb cb,
1329 const ::rclcpp::SubscriptionOptions& options);
1330
1331 // -- generic (type-erased) pub/sub — phase-515 P4 -------------------------
1332 //
1333 // Bodies in `rclcpp.hpp`, types in `rclcpp/generic.hpp`. rclcpp's name-only
1334 // spellings are REFUSE-LOUD; the hash-taking ones are the nano-ros form.
1335
1340 ::rclcpp::Owned<::rclcpp::GenericPublisher>
1341 create_generic_publisher(const char* topic, const char* type, const char* type_hash,
1342 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile());
1343
1347 template <typename Cb>
1349 create_generic_subscription(const char* topic, const char* type, const char* type_hash,
1350 const ::rclcpp::QoS& qos, Cb callback);
1351
1354 template <typename A, typename B, typename... R>
1355 void create_generic_publisher(const A& topic, const B& type, const ::rclcpp::QoS& qos,
1356 const R&...) {
1357 (void)topic;
1358 (void)type;
1359 (void)qos;
1360 static_assert(::rclcpp::detail::refuse<A>::value, NROS_RCLCPP_REFUSE_GENERIC_BY_NAME);
1361 }
1362
1365 template <typename A, typename B, typename Cb, typename... R>
1366 void create_generic_subscription(const A& topic, const B& type, const ::rclcpp::QoS& qos,
1367 Cb&& callback, const R&...) {
1368 (void)topic;
1369 (void)type;
1370 (void)qos;
1371 (void)callback;
1372 static_assert(::rclcpp::detail::refuse<A>::value, NROS_RCLCPP_REFUSE_GENERIC_BY_NAME);
1373 }
1374
1377 template <typename S>
1378 ::rclcpp::Owned<::rclcpp::PollService<S>>
1379 create_service(const char* name, const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1380
1382 template <typename S, typename F,
1383 typename = typename ::rclcpp::tr::enable_if<::rclcpp::tr::is_convertible<
1384 F, void (*)(const typename S::Request&, typename S::Response&)>::value>::type>
1385 typename ::rclcpp::Service<S>::SharedPtr
1386 create_service(const char* name, F callback,
1387 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1388
1394 template <typename S, typename F,
1395 typename = typename ::rclcpp::tr::enable_if<
1397 !::rclcpp::tr::is_convertible<F, void (*)(const typename S::Request&,
1398 typename S::Response&)>::value>::type,
1399 typename = void>
1400 typename ::rclcpp::Service<S>::SharedPtr
1401 create_service(const char*, F, const ::rclcpp::QoS& = ::rclcpp::QoS::services());
1402
1409 template <typename S, typename F>
1410 typename ::rclcpp::Service<S>::SharedPtr create_service(const char* name, F callback,
1411 const ::rclcpp::QoS& qos,
1412 const ::rclcpp::CallbackGroup* group);
1413
1416 template <typename S>
1417 ::rclcpp::Owned<::rclcpp::PollClient<S>>
1418 create_client(const char* name, const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1419
1421 template <typename S, typename F,
1422 typename = typename ::rclcpp::tr::enable_if<::rclcpp::tr::is_convertible<
1423 F, void (*)(const typename S::Response&)>::value>::type>
1424 typename ::rclcpp::Client<S>::SharedPtr
1425 create_client(const char* name, F callback,
1426 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
1427
1429 template <
1430 typename S, typename F,
1431 typename = typename ::rclcpp::tr::enable_if<
1433 !::rclcpp::tr::is_convertible<F, void (*)(const typename S::Response&)>::value>::type,
1434 typename = void>
1435 typename ::rclcpp::Client<S>::SharedPtr
1436 create_client(const char*, F, const ::rclcpp::QoS& = ::rclcpp::QoS::services());
1437
1438 // -- parameters (bodies in `nros.hpp`) ----------------------------------
1439 //
1440 // Forwarders onto THE parameter store - the `nros_params::ParameterServer`
1441 // the EXECUTOR owns, reached across the FFI by `rclcpp/node_parameters.hpp`.
1442 // Declared here with the class and DEFINED in `nros.hpp` beside the other
1443 // out-of-line members, which is where the reasoning lives.
1444 //
1445 // Until phase-426 W4 these read an inline `ParameterServer` member on the
1446 // hosted block instead: a second store, node-local, that the six
1447 // `rcl_interfaces/srv/*` servers could not read - so a parameter declared
1448 // through this facade was invisible to `ros2 param get` and a sibling node
1449 // did not share it.
1450 //
1451 // OUTSIDE the hosted gate (phase-476 W3 deleted it), and that is the rest of
1452 // W4 rather than a tidy-up. The member leaving was "the last thing that made
1453 // the hosted gate decide whether a node HAS parameters rather than
1454 // whether it can spell them" - but the declarations stayed inside the gate,
1455 // so a freestanding node still had no parameter method, and
1456 // `nros::ParameterServer<Cap>` (deleted with this wave) was the only C++
1457 // parameter surface it had. Deleting that class while the gate stood would
1458 // have removed the capability from freestanding C++ instead of moving it,
1459 // which is what W4's ordering rule exists to prevent. The scalar FFI is
1460 // `<cstdint>` and a `const char*`; nothing about it needs the STL, and
1461 // `rclcpp::Seq<T, N>` gives the array form the same reach. Only the
1462 // `std::string`-KEYED overloads below need it, and they keep the gate - a
1463 // gate on METHODS, never on a member, which is what
1464 // `check-cpp-capability-layout` asks.
1465
1467 template <typename T> T declare_parameter(const char* name, T default_value = T());
1470 template <typename T> bool get_parameter(const char* name, T& out) const;
1472 template <typename T> T get_parameter(const char* name) const;
1476 template <typename T> Result set_parameter(const char* name, T value);
1478 bool has_parameter(const char* name) const;
1479
1480 // ---- phase-417 W4.a: the rest of rclcpp's parameter surface ------------
1481 //
1482 // All of it forwards to the executor's ONE store through
1483 // `rclcpp/node_parameters.hpp`. OUTSIDE any capability gate for the same
1484 // reason the four above are: the FFI is `<cstdint>` and `const char*`, so
1485 // a freestanding node can spell every one of them, and a gate here would
1486 // decide whether a node HAS parameters rather than whether it can name
1487 // them.
1488
1497 template <typename T>
1498 T declare_parameter(const char* name, T default_value, const ParameterDescriptor& descriptor);
1499
1503 template <typename T> bool get_parameter_or(const char* name, T& out, T fallback) const;
1504
1508 Result undeclare_parameter(const char* name);
1509
1512 ParameterType get_parameter_type(const char* name) const;
1513
1520 Result get_parameter_types(const char* const* names, ::size_t count, ParameterType* out) const;
1521
1529 Result describe_parameter(const char* name, ParameterDescriptor& out, char* text,
1530 ::size_t text_len) const;
1531
1539 Result list_parameters(const char* prefix, char* out_names, ::size_t name_stride,
1540 ::size_t max_names, ::size_t& count) const;
1541
1546 Result set_parameters_atomically(const ParameterWrite* writes, ::size_t count);
1547
1560 Result add_on_set_parameters_callback(OnSetParametersCallbackType callback, void* context,
1561 OnSetParametersCallbackToken& out_handle);
1562
1564 Result remove_on_set_parameters_callback(OnSetParametersCallbackToken handle);
1565
1566#ifdef NROS_CPP_STD // hosted-family: parameter-interop
1567 // ---- issue 2056: upstream's value-object parameter surface --------------
1568 //
1569 // Each of these dispatches on a `ParameterValue`'s tag to the typed form
1570 // above, so the store, the declared-parameter contract check and the
1571 // per-node keying are still reached through one implementation. Methods
1572 // only; nothing here is a member.
1573
1578 ParameterValue declare_parameter(const char* name, const ParameterValue& default_value);
1580 ParameterValue declare_parameter(const char* name, const ParameterValue& default_value,
1581 const ParameterDescriptor& descriptor);
1586 template <typename D, typename = decltype(&D::floating_point_range)>
1587 ParameterValue declare_parameter(const char* name, const ParameterValue& default_value,
1588 const D& descriptor);
1590 template <typename T, typename D, typename = decltype(&D::floating_point_range)>
1591 T declare_parameter(const char* name, T default_value, const D& descriptor);
1592 ParameterValue declare_parameter(const ::std::string& name,
1593 const ParameterValue& default_value) {
1594 return this->declare_parameter(name.c_str(), default_value);
1595 }
1596 ParameterValue declare_parameter(const ::std::string& name, const ParameterValue& default_value,
1597 const ParameterDescriptor& descriptor) {
1598 return this->declare_parameter(name.c_str(), default_value, descriptor);
1599 }
1600 template <typename D, typename = decltype(&D::floating_point_range)>
1601 ParameterValue declare_parameter(const ::std::string& name, const ParameterValue& default_value,
1602 const D& descriptor) {
1603 return this->declare_parameter(name.c_str(), default_value, descriptor);
1604 }
1605 template <typename T, typename D, typename = decltype(&D::floating_point_range)>
1606 T declare_parameter(const ::std::string& name, T default_value, const D& descriptor) {
1607 return this->declare_parameter(name.c_str(), default_value, descriptor);
1608 }
1609
1612 Parameter get_parameter(const ::std::string& name) const;
1614 bool get_parameter(const char* name, Parameter& out) const;
1615 bool get_parameter(const ::std::string& name, Parameter& out) const {
1616 return this->get_parameter(name.c_str(), out);
1617 }
1619 ::std::vector<Parameter> get_parameters(const ::std::vector<::std::string>& names) const;
1620
1625 struct SetParametersOutcome {
1626 bool successful;
1627 ::std::string reason;
1628 };
1631 SetParametersOutcome set_parameter(const Parameter& parameter);
1633 ::std::vector<SetParametersOutcome> set_parameters(const ::std::vector<Parameter>& parameters);
1634
1643 template <typename F>
1644 node_interfaces::OnSetParametersCallbackHandle::SharedPtr
1645 add_on_set_parameters_callback(F callback);
1647 void
1648 remove_on_set_parameters_callback(const node_interfaces::OnSetParametersCallbackHandle* handle);
1649
1652 node_interfaces::NodeParametersInterface::SharedPtr get_node_parameters_interface() {
1653 return node_interfaces::NodeParametersInterface(this);
1654 }
1655#endif // NROS_CPP_STD
1656
1657#ifdef NROS_CPP_HAS_STD_STRING // hosted-family: string-interop
1661 template <typename T> T declare_parameter(const ::std::string& name, T default_value = T()) {
1662 return this->template declare_parameter<T>(name.c_str(), default_value);
1663 }
1664 template <typename T> bool get_parameter(const ::std::string& name, T& out) const {
1665 return this->template get_parameter<T>(name.c_str(), out);
1666 }
1667 template <typename T> T get_parameter(const ::std::string& name) const {
1668 return this->template get_parameter<T>(name.c_str());
1669 }
1670 template <typename T> Result set_parameter(const ::std::string& name, T value) {
1671 return this->template set_parameter<T>(name.c_str(), value);
1672 }
1673 bool has_parameter(const ::std::string& name) const {
1674 return this->has_parameter(name.c_str());
1675 }
1676 Result undeclare_parameter(const ::std::string& name) {
1677 return this->undeclare_parameter(name.c_str());
1678 }
1679 template <typename T>
1680 T declare_parameter(const ::std::string& name, T default_value,
1681 const ParameterDescriptor& descriptor) {
1682 return this->template declare_parameter<T>(name.c_str(), default_value, descriptor);
1683 }
1684 template <typename T>
1685 bool get_parameter_or(const ::std::string& name, T& out, T fallback) const {
1686 return this->template get_parameter_or<T>(name.c_str(), out, fallback);
1687 }
1688 ParameterType get_parameter_type(const ::std::string& name) const {
1689 return this->get_parameter_type(name.c_str());
1690 }
1691
1703 template <typename T>
1704 Result declare_parameters(const ::std::string& prefix, const ::std::map<::std::string, T>& m) {
1705 for (typename ::std::map<::std::string, T>::const_iterator it = m.begin(); it != m.end();
1706 ++it) {
1707 const ::std::string full = prefix.empty() ? it->first : (prefix + "." + it->first);
1708 const ::nros_cpp_node_t* h = this->ffi_handle();
1709 Result r = ::rclcpp::detail::node_param_declare(h, full.c_str(), it->second);
1710 if (!r.ok() && r.raw() != RCLC_RET_ALREADY_EXISTS) {
1711 return r;
1712 }
1713 }
1714 return Result(RCL_RET_OK);
1715 }
1716#endif // NROS_CPP_HAS_STD_STRING
1717
1737 static Result create(Node& out, const char* name, const char* ns = nullptr) {
1738 if (!out.executor_handle_) {
1740 }
1741
1742 nros_cpp_ret_t ret = nros_cpp_node_create(out.executor_handle_, name, ns, &out.handle_);
1743
1744 if (ret == 0) {
1745 out.initialized_ = true;
1746 }
1747 return Result(ret);
1748 }
1749
1751 const char* get_name() const {
1752 if (!initialized_) return "";
1753 return nros_cpp_node_get_name(&handle_);
1754 }
1755
1757 const char* get_namespace() const {
1758 if (!initialized_) return "";
1759 return nros_cpp_node_get_namespace(&handle_);
1760 }
1761
1776 Result get_fully_qualified_name(char* buf, size_t buf_len, size_t* out_len = nullptr) const {
1777 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
1778 return Result(nros_cpp_node_get_fully_qualified_name(&handle_, buf, buf_len, out_len));
1779 }
1780
1798 const char* serialization_format() const {
1799 if (!initialized_) return nullptr;
1800 return nros_cpp_node_get_serialization_format(&handle_);
1801 }
1802
1815 const char* rmw_implementation_identifier() const {
1816 if (!initialized_) return nullptr;
1817 return nros_cpp_node_get_rmw_implementation_identifier(&handle_);
1818 }
1819
1843 ::rclcpp::Logger get_logger() const {
1844 if (!initialized_) return ::rclcpp::Logger("", nullptr);
1845 return ::rclcpp::Logger(nros_cpp_node_get_name(&handle_),
1846 nros_cpp_node_get_logger(&handle_));
1847 }
1848
1859 Clock* get_clock() { return &clock_; }
1861 const Clock* get_clock() const { return &clock_; }
1862
1870 Time now() const { return clock_.now(); }
1871
1873 bool is_valid() const { return initialized_; }
1874
1887 const nros_cpp_node_t* ffi_handle() const { return initialized_ ? &handle_ : nullptr; }
1888
1899 void set_qos_overrides(const nros_cpp_qos_override_t* overrides, size_t len) {
1900 if (initialized_) {
1901 ::nros_cpp_node_set_qos_overrides(&handle_, overrides, len);
1902 }
1903 }
1904
1911 void* executor_handle() const { return initialized_ ? executor_handle_ : nullptr; }
1912
1920 return Result(nros_cpp_register_parameter_services(executor_handle()));
1921 }
1922
1923 // ---- Graph queries — phase-417 stage 2b (RFC-0089) --------------------
1924 //
1925 // rclcpp puts these on the node. `rclcpp::Executor` owns them here because
1926 // one session per image makes the executor the graph's receiver
1927 // (RFC-0002), so each method below FORWARDS to the executor this node was
1928 // opened against and nothing else: no state, no loop, no caching, no name
1929 // construction. RFC-0019 — the behaviour is Rust's and stays there.
1930 //
1931 // The envelope every one of them shares (ADOPT-BOUNDED, RFC-0089): they
1932 // report what has been DISCOVERED and never block, so an empty result
1933 // means "nobody seen yet" and never "nobody exists" — poll rather than
1934 // calling once and concluding. `ErrorCode::Unsupported`, which is what a
1935 // backend with no graph at all returns, is a DIFFERENT answer from an
1936 // empty one and must not be collapsed into zero.
1937
1956 Result get_node_names(nros_cpp_node_visit_fn visit, void* ctx) const {
1957 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
1958 return Result(nros_cpp_executor_get_node_names(executor_handle_, visit, ctx));
1959 }
1960
1968 Result get_topic_names_and_types(nros_cpp_names_and_types_visit_fn visit, void* ctx) const {
1969 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
1970 return Result(nros_cpp_executor_get_topic_names_and_types(executor_handle_, visit, ctx));
1971 }
1972
1976 Result get_service_names_and_types(nros_cpp_names_and_types_visit_fn visit, void* ctx) const {
1977 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
1978 return Result(nros_cpp_executor_get_service_names_and_types(executor_handle_, visit, ctx));
1979 }
1980
1989 Result count_publishers(const char* topic_name, size_t* out_count) const {
1990 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
1991 return Result(nros_cpp_executor_count_publishers(executor_handle_, topic_name, out_count));
1992 }
1993
1996 Result count_subscribers(const char* topic_name, size_t* out_count) const {
1997 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
1998 return Result(nros_cpp_executor_count_subscribers(executor_handle_, topic_name, out_count));
1999 }
2000
2005 Result count_clients(const char* service_name, size_t* out_count) const {
2006 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2007 return Result(nros_cpp_executor_count_clients(executor_handle_, service_name, out_count));
2008 }
2009
2012 Result count_services(const char* service_name, size_t* out_count) const {
2013 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2014 return Result(nros_cpp_executor_count_services(executor_handle_, service_name, out_count));
2015 }
2016
2020 Result get_publisher_names_and_types_by_node(const char* node_name, const char* node_namespace,
2021 nros_cpp_names_and_types_visit_fn visit,
2022 void* ctx) const {
2023 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2024 return Result(nros_cpp_executor_get_publisher_names_and_types_by_node(
2025 executor_handle_, node_name, node_namespace, visit, ctx));
2026 }
2027
2039 const char* node_namespace,
2040 nros_cpp_names_and_types_visit_fn visit,
2041 void* ctx) const {
2042 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2043 return Result(nros_cpp_executor_get_subscription_names_and_types_by_node(
2044 executor_handle_, node_name, node_namespace, visit, ctx));
2045 }
2046
2050 Result get_service_names_and_types_by_node(const char* node_name, const char* node_namespace,
2051 nros_cpp_names_and_types_visit_fn visit,
2052 void* ctx) const {
2053 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2054 return Result(nros_cpp_executor_get_service_names_and_types_by_node(
2055 executor_handle_, node_name, node_namespace, visit, ctx));
2056 }
2057
2061 Result get_client_names_and_types_by_node(const char* node_name, const char* node_namespace,
2062 nros_cpp_names_and_types_visit_fn visit,
2063 void* ctx) const {
2064 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2065 return Result(nros_cpp_executor_get_client_names_and_types_by_node(
2066 executor_handle_, node_name, node_namespace, visit, ctx));
2067 }
2068
2083 Result get_publishers_info_by_topic(const char* topic_name,
2084 nros_cpp_endpoint_info_visit_fn visit, void* ctx) const {
2085 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2086 return Result(nros_cpp_executor_get_publishers_info_by_topic(executor_handle_, topic_name,
2087 visit, ctx));
2088 }
2089
2098 Result get_publishers_info_by_topic(const char* topic_name,
2099 ::rclcpp::TopicEndpointInfoVisitFn visit, void* ctx) const {
2100 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2101 ::rclcpp::detail::EndpointInfoTrampoline tramp{visit, ctx};
2102 return Result(nros_cpp_executor_get_publishers_info_by_topic(
2103 executor_handle_, topic_name, &::rclcpp::detail::EndpointInfoTrampoline::thunk,
2104 &tramp));
2105 }
2106
2112 nros_cpp_endpoint_info_visit_fn visit, void* ctx) const {
2113 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2114 return Result(nros_cpp_executor_get_subscriptions_info_by_topic(executor_handle_,
2115 topic_name, visit, ctx));
2116 }
2117
2122 ::rclcpp::TopicEndpointInfoVisitFn visit,
2123 void* ctx) const {
2124 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2125 ::rclcpp::detail::EndpointInfoTrampoline tramp{visit, ctx};
2126 return Result(nros_cpp_executor_get_subscriptions_info_by_topic(
2127 executor_handle_, topic_name, &::rclcpp::detail::EndpointInfoTrampoline::thunk,
2128 &tramp));
2129 }
2130
2137 template <typename M>
2139 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile());
2140
2153 template <typename M>
2155 const ::rclcpp::QoS& qos, const ::rclcpp::PublisherOptions& options);
2156
2168 template <typename M>
2170 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile());
2171
2187 template <typename M>
2189 const ::rclcpp::QoS& qos,
2190 const ::rclcpp::SubscriptionOptions& options);
2191
2234 template <
2235 typename M, typename F,
2236 typename = typename std::enable_if<std::is_convertible<F, void (*)(const M&)>::value>::type>
2238 const ::rclcpp::QoS& qos, F callback,
2239 const ::rclcpp::SubscriptionOptions& options = {});
2240
2252 template <typename M, typename F,
2253 typename = typename std::enable_if<
2254 std::is_convertible<F, void (*)(const M&, const uint8_t*, size_t)>::value>::type>
2256 const ::rclcpp::QoS& qos, F callback,
2257 const ::rclcpp::SubscriptionOptions& options = {});
2258
2259#if defined(NROS_SAFETY_E2E)
2278 template <typename M, typename F,
2279 typename = typename std::enable_if<std::is_convertible<
2280 F, void (*)(const M&, const ::nros_cpp_integrity_status_t&)>::value>::type>
2281 Result create_subscription_with_safety(::rclcpp::Subscription<M>& out, const char* topic,
2282 const ::rclcpp::QoS& qos, F callback,
2283 const ::rclcpp::SubscriptionOptions& options = {});
2284#endif // NROS_SAFETY_E2E
2285
2300 template <typename S>
2301 Result create_service(::rclcpp::PollService<S>& out, const char* service_name,
2302 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
2303
2317 template <typename S, typename F,
2318 typename = typename std::enable_if<std::is_convertible<
2319 F, void (*)(const typename S::Request&, typename S::Response&)>::value>::type>
2320 Result create_service(::rclcpp::Service<S>& out, const char* service_name, F callback,
2321 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services(),
2322 const ::rclcpp::ServiceOptions& options = {});
2323
2339 template <typename S>
2340 Result create_client(::rclcpp::PollClient<S>& out, const char* service_name,
2341 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
2342
2354 template <typename S, typename F,
2355 typename = typename std::enable_if<
2356 std::is_convertible<F, void (*)(const typename S::Response&)>::value>::type>
2357 Result create_client(::rclcpp::Client<S>& out, const char* service_name, F callback,
2358 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services(),
2359 const ::rclcpp::ClientOptions& options = {});
2360
2371 template <typename A>
2372 Result create_action_server(::rclcpp_action::Server<A>& out, const char* action_name,
2373 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services(),
2374 const ::rclcpp::ActionServerOptions& options = {});
2375
2382 template <typename A>
2383 Result create_action_client(::rclcpp_action::Client<A>& out, const char* action_name,
2384 const ::rclcpp::QoS& qos = ::rclcpp::QoS::services());
2385
2389 template <typename A>
2391 const char* action_name);
2392
2394 template <typename A>
2396 const char* action_name);
2397
2403 template <typename M>
2405 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile());
2406
2430 Result create_timer(Timer& out, const Clock& clock, uint64_t period_ms,
2431 nros_cpp_timer_callback_t callback, void* context = nullptr) {
2432 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2433 size_t handle_id = 0;
2434 nros_cpp_ret_t ret = nros_cpp_timer_create_on_clock(
2435 executor_handle_, static_cast<uint8_t>(clock.get_clock_type()), period_ms, callback,
2436 context, &handle_id);
2437 if (ret == 0) {
2438 out.executor_ = executor_handle_;
2439 out.handle_id_ = handle_id;
2440 out.initialized_ = true;
2441 }
2442 return Result(ret);
2443 }
2444
2463 template <typename Cb>
2464 ::rclcpp::TimerHandle create_wall_timer(::rclcpp::Duration period, Cb cb,
2465 const ::rclcpp::CallbackGroup* group = nullptr,
2466 bool autostart = true);
2467
2468#ifdef NROS_CPP_HAS_STD_CHRONO // hosted-family: chrono-interop
2472 template <typename Rep, typename Period, typename Cb>
2473 ::rclcpp::TimerHandle create_wall_timer(::std::chrono::duration<Rep, Period> period, Cb cb,
2474 const ::rclcpp::CallbackGroup* group = nullptr,
2475 bool autostart = true);
2476#endif
2477
2488 Result create_wall_timer(Timer& out, uint64_t period_ms, nros_cpp_timer_callback_t callback,
2489 void* context = nullptr) {
2490 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2491 size_t handle_id = 0;
2492 nros_cpp_ret_t ret =
2493 nros_cpp_timer_create(executor_handle_, period_ms, callback, context, &handle_id);
2494 if (ret == 0) {
2495 out.executor_ = executor_handle_;
2496 out.handle_id_ = handle_id;
2497 out.initialized_ = true;
2498 }
2499 return Result(ret);
2500 }
2501
2502 // ==== phase-427 W3 — member-function binding, on the upstream name ======
2503 //
2504 // `nros::bind_timer<C, &C::m>(node, out, ms, self)` was a FREE function
2505 // with an invented name doing what `create_wall_timer` does. Folding it in
2506 // removes an invention by reusing an upstream name, which is what clause 2
2507 // asks for — and it is the shape a component actually writes, so the verb a
2508 // reader meets first is the one they need.
2509 //
2510 // NO ALLOCATION and NO `std::function`: the member pointer is a TEMPLATE
2511 // PARAMETER, so the trampoline is a capture-less lambda that converts to
2512 // the executor's raw `void(*)(void*)` and `self` is the context. That is
2513 // why this overload, unlike the `std::chrono` one, reaches every target.
2514
2520 template <class C, void (C::*Method)()>
2521 Result create_wall_timer(Timer& out, uint64_t period_ms, C* self) {
2522 return this->create_wall_timer(
2523 out, period_ms, [](void* ctx) { (static_cast<C*>(ctx)->*Method)(); }, self);
2524 }
2525
2532 template <class C, void (C::*Method)()>
2533 Result create_timer(Timer& out, const Clock& clock, uint64_t period_ms, C* self) {
2534 return this->create_timer(
2535 out, clock, period_ms, [](void* ctx) { (static_cast<C*>(ctx)->*Method)(); }, self);
2536 }
2537
2540 template <class C, void (C::*Method)()>
2541 Result create_timer_in_group(const ::rclcpp::CallbackGroup& group, Timer& out,
2542 uint64_t period_ms, C* self) {
2543 return this->create_timer_in_group(
2544 group, out, period_ms, [](void* ctx) { (static_cast<C*>(ctx)->*Method)(); }, self);
2545 }
2546
2555 Result create_timer_oneshot(Timer& out, uint64_t delay_ms, nros_cpp_timer_callback_t callback,
2556 void* context = nullptr) {
2557 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2558 size_t handle_id = 0;
2559 nros_cpp_ret_t ret = nros_cpp_timer_create_oneshot(executor_handle_, delay_ms, callback,
2560 context, &handle_id);
2561 if (ret == 0) {
2562 out.executor_ = executor_handle_;
2563 out.handle_id_ = handle_id;
2564 out.initialized_ = true;
2565 }
2566 return Result(ret);
2567 }
2568
2569 // -- Phase 273 (RFC-0047) — Callback-group API -------------------------
2570
2586 ::rclcpp::CallbackGroup create_callback_group(const char* name) {
2587 if (!::rclcpp::detail::admit_callback_group(name)) {
2588 const ::rclcpp::detail::DeclaredCallbackGroups* g =
2589 ::rclcpp::detail::declared_callback_groups_scope();
2590 const char* list = (g != nullptr) ? g->list : nullptr;
2591 int len = 0;
2592 while (name[len] != '\0')
2593 ++len;
2594 ::rclcpp::detail::report_callback_group_mismatch(this->get_name(), "created", name, len,
2595 list);
2596 this->set_error("create_callback_group: the group is not in this registration's "
2597 "CALLBACK_GROUPS",
2598 ::rclcpp::detail::DECLARED_CALLBACK_GROUP_MISMATCH);
2599 }
2600 return ::rclcpp::CallbackGroup{name};
2601 }
2602
2610 ::rclcpp::CallbackGroup::SharedPtr
2611 create_callback_group(::rclcpp::CallbackGroupType type,
2612 bool automatically_add_to_executor_with_node = true) {
2613 (void)automatically_add_to_executor_with_node;
2614 return ::rclcpp::CallbackGroup::SharedPtr(type);
2615 }
2616
2628 Result create_timer_in_group(const ::rclcpp::CallbackGroup& group, Timer& out,
2629 uint64_t period_ms, nros_cpp_timer_callback_t callback,
2630 void* context = nullptr) {
2631 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2632 size_t handle_id = 0;
2633 nros_cpp_ret_t ret = nros_cpp_timer_create_in_group(
2634 executor_handle_, &handle_, period_ms, callback, context, group.get_name(), &handle_id);
2635 if (ret == 0) {
2636 out.executor_ = executor_handle_;
2637 out.handle_id_ = handle_id;
2638 out.initialized_ = true;
2639 }
2640 return Result(ret);
2641 }
2642
2662 template <
2663 typename M, typename F,
2664 typename = typename std::enable_if<std::is_convertible<F, void (*)(const M&)>::value>::type>
2665 Result create_subscription_in_group(const ::rclcpp::CallbackGroup& group,
2666 ::rclcpp::Subscription<M>& out, const char* topic,
2667 const ::rclcpp::QoS& qos, F callback,
2668 const ::rclcpp::SubscriptionOptions& options = {});
2669
2681 template <typename M>
2682 Result create_publisher_in_group(const ::rclcpp::CallbackGroup& /* group */,
2683 ::rclcpp::Publisher<M>& out, const char* topic,
2684 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile()) {
2685 return create_publisher<M>(out, topic, qos);
2686 }
2687
2695 Result create_guard_condition(::rclcpp::GuardCondition& out, nros_cpp_guard_callback_t callback,
2696 void* context = nullptr) {
2697 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
2698 size_t handle_id = 0;
2699 nros_cpp_ret_t ret = nros_cpp_guard_condition_create(executor_handle_, callback, context,
2700 out.storage_, &handle_id);
2701 if (ret == 0) {
2702 out.initialized_ = true;
2703 out.executor_ = executor_handle_;
2704 out.handle_id_ = handle_id;
2705 }
2706 return Result(ret);
2707 }
2708
2709 // ==== phase-427 W4 — the ours-only family, under `_in` names =============
2710 //
2711 // These came off `nros::ComponentNode`. Every one of them is spelled with
2712 // an `_in` SUFFIX rather than upstream's bare verb, and the rename is the
2713 // whole point of the item.
2714 //
2715 // `ComponentNode::create_publisher<M>(const char*, const QoS&)` returned by
2716 // value and reported failure through the `ok()` latch; upstream's
2717 // `create_publisher<M>(const std::string&, …)` returns a `shared_ptr` and
2718 // throws. As OVERLOADS on one type those differ only in SIGNATURE, and C++
2719 // resolves a signature-only difference SILENTLY. Measured on the merged
2720 // shape before the rename, with gcc 13:
2721 //
2722 // create_publisher<M>("chatter", 10) -> UPSTREAM (shared_ptr)
2723 // create_publisher<M>("chatter", rclcpp::QoS(10)) -> OURS (by value),
2724 // accepted with only
2725 // a -Wextra-ish note
2726 // that ISO calls it
2727 // ambiguous
2728 //
2729 // So a ported file compiles either way and gets a different type, lifetime
2730 // and failure channel depending on which spelling of the depth it used.
2731 // That is exactly the compile-and-differ RFC-0089 forbids, and the merge is
2732 // what would have manufactured it. The fix is a different NAME, not a
2733 // cleverer signature — the third application of that rule in this campaign,
2734 // after the reordered C node initialiser and the clock-taking C timer verb.
2735 //
2736 // `tests/compile/one_node_type_ours_only_names.cpp` pins both halves: that
2737 // the ported spellings reach upstream's overload on the real type, and
2738 // (the negative control) that the pre-rename shape bound the wrong one.
2739
2745 template <typename M>
2747 create_publisher_in(const char* topic,
2748 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile()) {
2750 Result r = this->create_publisher(pub, topic, qos);
2751 if (!r.ok()) {
2752 this->set_error("create_publisher_in", r.raw());
2753 }
2754 return pub;
2755 }
2756
2770 template <typename M, class C, void (C::*Method)(const M& msg)>
2771 void create_subscription_in(const char* topic,
2772 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile());
2773
2776 template <typename M, class C, void (C::*Method)(const M& msg)>
2777 void create_subscription_in_group(const ::rclcpp::CallbackGroup& group, const char* topic,
2778 const ::rclcpp::QoS& qos = ::rclcpp::QoS::default_profile());
2779
2796 bool check_declared_depth(const char* type_name, const char* topic, const ::rclcpp::QoS& qos) {
2797 const int declared = ::rclcpp::declared_depth(type_name, topic);
2798 // Nobody declared this endpoint. Not an error: the image has not opted
2799 // in, and anything sizing from depth refuses rather than defaulting.
2800 if (declared == ::rclcpp::DECLARED_DEPTH_UNDECLARED) {
2801 return true;
2802 }
2803 if (declared == qos.depth()) {
2804 return true;
2805 }
2806 // The two NUMBERS and the TOPIC go out first: `set_error` records one
2807 // `const char*`, which cannot carry them.
2809 qos.depth());
2810 this->set_error(
2811 "create_subscription_in: ::rclcpp::QoS depth disagrees with the depth declared for "
2812 "this topic in the contract sidecar. Depth multiplies the arena, so the "
2813 "declaration and the code must state one number, not two.",
2815 return false;
2816 }
2817
2829 bool check_declared_qos(const char* type_name, const char* topic, const ::rclcpp::QoS& qos) {
2830 if (!this->check_declared_depth(type_name, topic, qos)) {
2831 return false;
2832 }
2833 const int rel = ::rclcpp::declared_reliability(type_name, topic);
2834 if (rel != ::rclcpp::DECLARED_POLICY_UNDECLARED &&
2835 rel != static_cast<int>(qos.reliability())) {
2837 this->get_name(), topic, "reliability",
2840 static_cast<int>(qos.reliability())));
2841 this->set_error("create_subscription_in: ::rclcpp::QoS reliability disagrees with the "
2842 "reliability declared for this topic in the contract sidecar.",
2844 return false;
2845 }
2846 const int dur = ::rclcpp::declared_durability(type_name, topic);
2847 if (dur != ::rclcpp::DECLARED_POLICY_UNDECLARED &&
2848 dur != static_cast<int>(qos.durability())) {
2850 this->get_name(), topic, "durability",
2852 ::rclcpp::detail::declared_durability_spelling(static_cast<int>(qos.durability())));
2853 this->set_error("create_subscription_in: ::rclcpp::QoS durability disagrees with the "
2854 "durability declared for this topic in the contract sidecar.",
2856 return false;
2857 }
2858 return true;
2859 }
2860
2879 bool check_declared_param(const char* name, int passed_type) {
2880 const char* ns = this->get_namespace();
2881 const char* nn = this->get_name();
2882 if (name == nullptr || nn == nullptr) {
2883 return true;
2884 }
2885 const ::rclcpp::declared_params::Node* declaring =
2886 ::rclcpp::declared_param_node((ns != nullptr) ? ns : "", nn);
2887 if (declaring == nullptr || ::rclcpp::declared_param_exempt(name)) {
2888 return true;
2889 }
2890 const int declared = ::rclcpp::declared_param_type(declaring, name);
2891 if (declared == passed_type) {
2892 return true;
2893 }
2894 ::rclcpp::detail::report_declared_param_mismatch(declaring->fqn, name, declaring->contract,
2895 declared, passed_type);
2896 this->set_error("declare_parameter: the code does not declare this parameter the way "
2897 "its contract does (see the line above for the node, the parameter and "
2898 "the contract). The parameter store is sized from the contract's "
2899 "`params:`, so the two must state one name and one type.",
2901 return false;
2902 }
2903
2911 void set_error(const char* what, int32_t code) {
2912 // Relaxed read for the first-failure guard: set_error runs during
2913 // construction, on the constructing thread, never concurrently with a
2914 // reader (issue #230).
2915 if (!__atomic_load_n(&has_error_, __ATOMIC_RELAXED)) {
2916 error_what_ = what;
2917 error_code_ = code;
2918 // RELEASE publishes the two plain writes above: a reader that
2919 // acquire-observes has_error_ == true is guaranteed to see them.
2920 __atomic_store_n(&has_error_, true, __ATOMIC_RELEASE);
2922 }
2923 }
2924
2933 if (initialized_) {
2934 nros_cpp_node_destroy(&handle_);
2935 initialized_ = false;
2936 }
2937 }
2938
2939 // Move semantics (non-copyable)
2940 Node(Node&& other)
2941 : handle_(other.handle_), initialized_(other.initialized_),
2942 executor_handle_(other.executor_handle_), clock_(other.clock_), options_(other.options_),
2943 // The latch travels with the node: a moved-from node's failure is
2944 // still this node's failure, and the entry checks `ok()` on whichever
2945 // object it ended up holding.
2946 has_error_(other.has_error_), error_what_(other.error_what_),
2947 error_code_(other.error_code_) {
2948 other.initialized_ = false;
2949 other.executor_handle_ = nullptr;
2950 }
2951
2952 Node& operator=(Node&& other) {
2953 if (this != &other) {
2954 if (initialized_) {
2955 nros_cpp_node_destroy(&handle_);
2956 }
2957 handle_ = other.handle_;
2958 initialized_ = other.initialized_;
2959 executor_handle_ = other.executor_handle_;
2960 clock_ = other.clock_;
2961 options_ = other.options_;
2962 has_error_ = other.has_error_;
2963 error_what_ = other.error_what_;
2964 error_code_ = other.error_code_;
2965 other.initialized_ = false;
2966 other.executor_handle_ = nullptr;
2967 }
2968 return *this;
2969 }
2970
2971 private:
2972 Node(const Node&) = delete;
2973 Node& operator=(const Node&) = delete;
2974
2985 template <typename S>
2986 Result register_service_(::rclcpp::Service<S>& out, const char* service_name,
2987 bool (*trampoline)(const uint8_t*, size_t, uint8_t*, size_t, size_t*,
2988 void*),
2989 void* context, const ::rclcpp::QoS& qos,
2990 const ::rclcpp::ServiceOptions& options, const void* capture = nullptr,
2991 size_t capture_len = 0, void (*capture_drop)(void*) = nullptr);
2992
2997 template <typename S>
2998 Result register_service_(::rclcpp::Service<S>& out, const char* service_name,
2999 bool (*trampoline)(const uint8_t*, size_t, ::rmw_request_id_t*,
3000 uint8_t*, size_t, size_t*, void*),
3001 void* context, const ::rclcpp::QoS& qos,
3002 const ::rclcpp::ServiceOptions& options, const void* capture = nullptr,
3003 size_t capture_len = 0, void (*capture_drop)(void*) = nullptr);
3004
3007 template <typename S>
3008 Result finish_register_service_(::rclcpp::Service<S>& out, const char* service_name,
3009 nros_cpp_ret_t ret, size_t handle);
3010
3016 template <typename S, typename F>
3017 Result register_hosted_service_(::rclcpp::Service<S>& out, const char* name, F callback,
3018 const ::rclcpp::QoS& qos,
3019 ::rclcpp::tr::integral_constant<int, 0>);
3020 template <typename S, typename F>
3021 Result register_hosted_service_(::rclcpp::Service<S>& out, const char* name, F callback,
3022 const ::rclcpp::QoS& qos,
3023 ::rclcpp::tr::integral_constant<int, 1>);
3024
3030 void apply_options_(const ::rclcpp::NodeOptions& options) {
3031 options_ = options;
3032 // issue 2035 -- the per-node opt-out C's `notify_changed_over_dds =
3033 // false` reaches. UNSUPPORTED on a build with no parameter services,
3034 // where no node publishes the topic: nothing to switch off.
3035 if (!options.start_parameter_event_publisher() && initialized_) {
3036 (void)nros_cpp_node_disable_parameter_events(&handle_);
3037 }
3038 Clock chosen(options.clock_type());
3039 if (!chosen.is_valid()) {
3040 this->set_error("NodeOptions::clock_type (not a clock)",
3041 static_cast<int32_t>(::rclcpp::ErrorCode::InvalidArgument));
3042 return;
3043 }
3044 clock_ = chosen;
3045 }
3046
3047 nros_cpp_node_t handle_;
3048 bool initialized_;
3049 void* executor_handle_; // Set by rclcpp::init() via friendship
3050 // Issue 0789 — the node's own clock, ROS time as in rclcpp. Constructing
3051 // it touches no platform service (only a steady clock records an epoch),
3052 // so a Node in static storage stays as cheap to create as it was.
3053 Clock clock_;
3054 // issue 1975 -- the options the node was constructed with, for
3055 // `get_node_options()`; `clock_` above is built from `clock_type()`.
3056 // Unconditional (one enum), so the layout stays probe-independent.
3057 ::rclcpp::NodeOptions options_;
3058 // phase-476 W3 — `void* hosted_` and its lazy `hosted()` accessor are gone
3059 // with ~~`detail::NodeHosted`~~ (see there): the block's last member was an
3060 // empty `NodeOptions`. `sizeof(Node)` loses that pointer on EVERY target,
3061 // so the layout stays probe-independent.
3062
3063 // phase-427 W4 — the error latch, 24 UNCONDITIONAL bytes on every node.
3064 //
3065 // These did NOT go behind the old `hosted_` block, and the decision was deliberate rather
3066 // than an oversight the layout rule missed. This is the error channel a
3067 // `-fno-exceptions` target has INSTEAD of a throwing constructor: the
3068 // boot-halt mechanism (`NanoRosEntityInventory.cmake`, RFC-0044 Q2) is
3069 // exactly what a freestanding image needs, and a latch reachable only on a
3070 // hosted target would leave the firmware case — the one that cannot throw —
3071 // with no channel at all. So every node pays 24 bytes, which is the price
3072 // of one node type whose failure story works on both.
3073 //
3074 // Issue #230 — inverted flag: the HEALTHY value is the zero-init default,
3075 // so a cross-core reader sees "ok" without waiting for any store. Accessed
3076 // via __atomic builtins (release in set_error, acquire in ok()/error_*) —
3077 // NOT `<atomic>`, which the Zephyr `-nostdinc++` minimal libcpp may lack
3078 // (issue 0112 class). All three fields are zero-init-safe.
3079 bool has_error_ = false;
3080 const char* error_what_ = nullptr;
3081 int32_t error_code_ = 0;
3082
3083 // phase-427 W7 — the class moved to `rclcpp::` while its friends were still
3084 // `nros::` free functions, so each friend is QUALIFIED. phase-483 W1 moved
3085 // the friends into `rclcpp::` too; the qualified form still names exactly
3086 // the declarations at the top of this header.
3087 //
3088 // The declarator-id is PARENTHESIZED, and that is not style. A
3089 // nested-name-specifier is parsed greedily, so `friend Result ::rclcpp::init`
3090 // reads as `Result::rclcpp::init` and fails with `'rclcpp' in 'using Result ='
3091 // does not name a type` — a diagnostic that names neither the friend nor
3092 // the namespace. `(::rclcpp::init)` closes the return type first.
3093 friend class ::rclcpp::Executor;
3094 friend class ::rclcpp::NodeBuilder;
3095 friend Result(::rclcpp::init_in)(const char* locator, uint8_t domain_id);
3096 friend Result(::rclcpp::init_in)(const char* locator, uint8_t domain_id,
3097 const char* session_name);
3098 // Issue 1434 — the namespaced overload. A friend declaration names ONE
3099 // signature, so an overload that reaches `global_storage()` needs its own
3100 // line; without it the new body compiles everywhere the class is not
3101 // instantiated and fails only where a TU actually calls it.
3102 friend Result(::rclcpp::init_in)(const char* locator, uint8_t domain_id,
3103 const char* session_name, const char* node_namespace);
3104 friend Result(::rclcpp::init_with_rmw)(const char* rmw, const char* locator, uint8_t domain_id,
3105 const char* session_name);
3107 friend bool(::rclcpp::ok)();
3108 friend Result(::rclcpp::create_node)(Node& out, const char* name, const char* ns);
3109 friend Result(::rclcpp::create_node_on)(Node& out, void* executor_handle, const char* name,
3110 const char* ns);
3111 friend Result(::rclcpp::spin_once)(int32_t timeout_ms);
3113 friend Result(::rclcpp::spin_in)(uint32_t duration_ms, int32_t poll_ms);
3115
3116 // Global executor inline storage for init/shutdown free functions.
3117 //
3118 // Use a template-static-member trick instead of a function-local static.
3119 // Function-local statics need __cxa_guard_acquire/release on first-call
3120 // initialisation; on NuttX the resulting guard logic returns NULL for
3121 // the storage pointer (observed empirically with LTO on armv7a-nuttx-eabihf,
3122 // even with constant-initialisation `= {}`). A template static member is
3123 // emitted into .bss like a file-scope variable and gets COMDAT-folded by
3124 // the linker, sidestepping the guarded-init path entirely.
3125 template <int = 0> struct GlobalStorageHolder {
3126 alignas(8) static uint8_t storage[NROS_CPP_EXECUTOR_STORAGE_SIZE];
3127 };
3128 static uint8_t* global_storage() { return GlobalStorageHolder<>::storage; }
3129
3149 static bool global_initialized() {
3150 return nros_cpp_context_is_live(GlobalStorageHolder<>::storage);
3151 }
3152};
3153
3154// Out-of-class definitions for Node::GlobalStorageHolder<> — the template
3155// machinery means these get emitted as COMDAT symbols, so multiple TUs
3156// including this header all collapse to a single .bss allocation.
3157template <int N>
3158alignas(8) uint8_t Node::GlobalStorageHolder<N>::storage[NROS_CPP_EXECUTOR_STORAGE_SIZE] = {};
3159
3160} // namespace rclcpp
3161
3162namespace rclcpp {
3163
3164// ==== phase-427 W4 — the timer pool, as a TEMPLATE PARAMETER ================
3165//
3166// `ComponentNode` carried `Timer timers_[NROS_COMPONENT_MAX_TIMERS]` — 192
3167// unconditional bytes at the default depth of 8. Merging that onto `Node` would
3168// have charged every node in the tree for a pool most of them never touch, on
3169// targets chosen for having no memory to spare.
3170//
3171// So the depth is a template parameter and the DEFAULT IS ZERO: `Node` itself
3172// carries no pool and is unchanged in size by this half of the merge. A node
3173// that wants the storage-free timer verbs derives `NodeWithTimers<N>` and names
3174// the depth it needs.
3175//
3176// Why a derived template and not `template <size_t N> class Node` — measured,
3177// not preferred. `Node` has to stay ONE non-template type
3178// (`tests/compile/one_node_type.cpp`); 218 in-tree sites spell `Node` with
3179// no argument list, which a class template with a defaulted parameter does not
3180// permit; and every `Node&` parameter in the tree — `create_node(Node&)`,
3181// `Executor`, `spin` — would otherwise accept only ONE depth, so a component
3182// with a pool would not be a node the executor could take. Deriving keeps the
3183// IS-A that `ComponentNode` never had (it WRAPPED a node, which is the reason
3184// this merge exists at all) while keeping the bytes opt-in.
3185//
3186// `NodeWithTimers` is NOT a second node type. It adds storage and two verbs;
3187// it declares no identity, and every one of its instances IS-A `Node`.
3188
3192#ifndef NROS_COMPONENT_MAX_TIMERS
3193#define NROS_COMPONENT_MAX_TIMERS 8
3194#endif
3202#if NROS_COMPONENT_MAX_TIMERS < 1
3203#error "NROS_COMPONENT_MAX_TIMERS must be >= 1: it sizes a C array (issue 1015)"
3204#endif
3205
3225template <::size_t MaxTimers = NROS_COMPONENT_MAX_TIMERS>
3226class NodeWithTimers : public ::rclcpp::Node {
3227 static_assert(MaxTimers >= 1,
3228 "NodeWithTimers<0> has no pool -- derive ::rclcpp::Node directly, or use "
3229 "the out-ref create_wall_timer(Timer&, ...) family");
3230
3231 public:
3232 using ::rclcpp::Node::Node;
3233
3240 template <class C, void (C::*Method)()> void create_wall_timer_in(uint64_t period_ms) {
3241 Timer* slot = this->next_timer_slot("create_wall_timer_in");
3242 if (slot == nullptr) return;
3243 Result r = this->Node::template create_wall_timer<C, Method>(*slot, period_ms,
3244 static_cast<C*>(this));
3245 if (!r.ok()) {
3246 this->set_error("create_wall_timer_in", r.raw());
3247 return;
3248 }
3249 ++timer_count_;
3250 }
3251
3253 void create_wall_timer_in(uint64_t period_ms, nros_cpp_timer_callback_t callback,
3254 void* context = nullptr) {
3255 Timer* slot = this->next_timer_slot("create_wall_timer_in");
3256 if (slot == nullptr) return;
3257 Result r = this->Node::create_wall_timer(*slot, period_ms, callback, context);
3258 if (!r.ok()) {
3259 this->set_error("create_wall_timer_in", r.raw());
3260 return;
3261 }
3262 ++timer_count_;
3263 }
3264
3267 template <class C, void (C::*Method)()>
3268 void create_timer_in_group(const CallbackGroup& group, uint64_t period_ms) {
3269 Timer* slot = this->next_timer_slot("create_timer_in_group");
3270 if (slot == nullptr) return;
3271 Result r = this->Node::template create_timer_in_group<C, Method>(group, *slot, period_ms,
3272 static_cast<C*>(this));
3273 if (!r.ok()) {
3274 this->set_error("create_timer_in_group", r.raw());
3275 return;
3276 }
3277 ++timer_count_;
3278 }
3279
3281 void create_timer_in_group(const CallbackGroup& group, uint64_t period_ms,
3282 nros_cpp_timer_callback_t callback, void* context = nullptr) {
3283 Timer* slot = this->next_timer_slot("create_timer_in_group");
3284 if (slot == nullptr) return;
3285 Result r = this->Node::create_timer_in_group(group, *slot, period_ms, callback, context);
3286 if (!r.ok()) {
3287 this->set_error("create_timer_in_group", r.raw());
3288 return;
3289 }
3290 ++timer_count_;
3291 }
3292
3293 // The out-ref forms on `Node` share these names; bring them in so a derived
3294 // node can still spell `create_timer_in_group(group, my_timer_, 100, cb, ctx)`
3295 // without the pool overloads hiding the base by name lookup.
3298
3299 private:
3302 Timer* next_timer_slot(const char* what) {
3303 if (timer_count_ >= MaxTimers) {
3304 this->set_error(what, -1);
3305 return nullptr;
3306 }
3307 return &timers_[timer_count_];
3308 }
3309
3310 Timer timers_[MaxTimers];
3311 ::size_t timer_count_ = 0;
3312};
3313
3314// -- Free function implementations --
3315
3316inline Result init_in(const char* locator, uint8_t domain_id) {
3317 // Issue 0329 — forward RAW to the 3-arg overload, which resolves the whole
3318 // ladder (baked `NROS_ENTRY_LOCATOR`/`NROS_ENTRY_DOMAIN_ID` rungs, hosted
3319 // default, then the env overlay in the Rust resolver behind `nros_cpp_init`).
3320 // This overload previously re-resolved the locator rung itself — a second,
3321 // partial copy of the same ladder (it never applied `NROS_ENTRY_DOMAIN_ID`),
3322 // duplicating what the 3-arg already does. Phase 266: unified default session
3323 // name "node".
3324 return init_in(locator, domain_id, "node");
3325}
3326
3327inline Result init_in(const char* locator, uint8_t domain_id, const char* session_name) {
3328 // Issue 1434 — forward RAW to the 4-arg overload, exactly as the 2-arg
3329 // forwards here (issue 0329): one ladder, one body. `nullptr` is "this
3330 // image declares no namespace", which is what every caller of this
3331 // overload has always meant.
3332 return init_in(locator, domain_id, session_name, nullptr);
3333}
3334
3335inline Result init_in(const char* locator, uint8_t domain_id, const char* session_name,
3336 const char* node_namespace) {
3337 // NROS_CPP_RET_INVALID_ARGUMENT = -3 (defined in nros_cpp_ffi.h
3338 // which isn't included from this header — duplicate the value
3339 // inline; generated header is the source of truth).
3340 if (session_name == nullptr) {
3341 return Result(-3);
3342 }
3343#if defined(NROS_CPP_STD) || (__STDC_HOSTED__ + 0) // hosted-family: hosted-console
3344 // Issue #39 — apply the same `$NROS_LOCATOR` / `$ROS_DOMAIN_ID` env
3345 // fallback as the 2-arg `init()` when `locator` is null / `domain_id` is
3346 // 0. This makes `init_in(argc, argv)` (which delegates here with a
3347 // null locator) honor the env overlay instead of passing a null locator
3348 // to the backend → TransportError / degraded session.
3349 // Issue 0330 — the hard local default is GONE entirely (it was a zenoh
3350 // fact in an RMW-blind header); the backend now supplies it.
3351 // RFC-0045 / issue #206 — the env overlay (NROS_LOCATOR / ROS_DOMAIN_ID /
3352 // NROS_NODE_NAME) moved into the shared Rust resolver behind
3353 // nros_cpp_init (precedence model A: hosted env > this baked chain >
3354 // compiled default; malformed or >232 ROS_DOMAIN_ID is an init ERROR,
3355 // never a silent domain 0). This header only assembles the baked rung.
3356#endif
3357 // Phase-287 W6 — compile-time connect defaults, so ONE portable source
3358 // works native + embedded. `NROS_ENTRY_LOCATOR` / `NROS_ENTRY_DOMAIN_ID`
3359 // are target compile definitions the embedded board gate bakes
3360 // (NanoRosEntry.cmake; Kconfig on Zephyr via <rclcpp/detail/main.hpp>); on native
3361 // they are undefined. Precedence (model A, RFC-0045/#206): env (hosted,
3362 // applied in the Rust resolver) > explicit arg > baked macro > default.
3363#ifdef NROS_ENTRY_LOCATOR
3364 if (locator == nullptr) {
3365 locator = NROS_ENTRY_LOCATOR;
3366 }
3367#endif
3368#ifdef NROS_ENTRY_DOMAIN_ID
3369 // Only the UNSET sentinel (0) folds the baked macro in; an explicit
3370 // argument — including kDomainIdExplicitZero (255) for a literal
3371 // domain 0 (issue #227) — passes through untouched.
3372 if (domain_id == 0) {
3373 domain_id = static_cast<uint8_t>(NROS_ENTRY_DOMAIN_ID);
3374 }
3375#endif
3376 // Issue 0330 — there is deliberately NO hosted "tcp/127.0.0.1:7447"
3377 // fallback here. That value is a *zenoh* fact and this header is
3378 // RMW-blind; a cyclonedds or xrce build must not carry it. A null
3379 // locator flows through `nros_cpp_init` (→ `None` → the RFC-0045
3380 // resolver's empty bottom rung) to whichever backend is linked, and
3381 // that backend applies its own default (zenoh:
3382 // `nros_rmw_zenoh::DEFAULT_LOCATOR`; xrce: its agent default;
3383 // cyclonedds: ignores the locator). Precedence is otherwise unchanged:
3384 // hosted env > explicit arg > baked macro > backend default.
3385
3386 // Phase 128.C.1 / phase-241.D3-rev — RMW-blind init. The selected
3387 // backend registers itself before `main` via its `.init_array` ctor
3388 // (RFC-0042 §D3.3), and `nros_cpp_init` additionally calls the weak
3389 // `nros_app_register_backends()` board-override hook for RTOS targets
3390 // where `.init_array` ctors do not run. No `#ifdef NROS_RMW_*` chain
3391 // here, no CMake-driven fan-out — the user's
3392 // `target_link_libraries(... NanoRos::Rmw::<name>)` is the only
3393 // selector.
3394 // Issue 1050 defect (3) — the RMW selector's BAKED rung. `NROS_ENTRY_RMW`
3395 // is a target compile definition the entry gate bakes, exactly like
3396 // `NROS_ENTRY_LOCATOR` / `NROS_ENTRY_DOMAIN_ID` above; undefined means the
3397 // image names no backend and the registry must contain exactly one.
3398 //
3399 // This is what the paragraph above could not express. "The user's
3400 // `target_link_libraries(... NanoRos::Rmw::<name>)` is the only selector"
3401 // is true of the LINK and false of the REGISTRY: a hosted archive
3402 // registers whatever it carries, from `.init_array`, before `main`.
3403#ifdef NROS_ENTRY_RMW
3404 const char* rmw = NROS_ENTRY_RMW;
3405#else
3406 const char* rmw = nullptr;
3407#endif
3408 // Issue 1434 — the namespace travels as the BAKED rung, like `rmw` above
3409 // and `session_name` beside it. `nullptr` reaches `nros_cpp_init_rmw`'s
3410 // own "unset" arm, which is the root; an EMPTY string is normalised here
3411 // rather than there, because a bake macro that did not resolve expands to
3412 // `""` and treating that as "the empty namespace" would be a configured
3413 // value where the image stated nothing (the same reasoning 1050 applied to
3414 // an empty `rmw` selector).
3415 if (node_namespace != nullptr && node_namespace[0] == '\0') {
3416 node_namespace = nullptr;
3417 }
3418 // Issue 1568 — the one refusal every executor-storage path takes: the
3419 // linked library says whether this TU's `NROS_CPP_EXECUTOR_STORAGE_SIZE`
3420 // (the header it was compiled against — on NuttX the committed snapshot)
3421 // is enough for the executor it builds. Refused rather than overrun.
3422 {
3423 nros_cpp_ret_t chk =
3424 nros_cpp_executor_storage_check(::rclcpp::Node::global_storage(),
3425 sizeof(::rclcpp::Node::GlobalStorageHolder<>::storage));
3426 if (chk != 0) {
3427 return Result(chk);
3428 }
3429 }
3430 nros_cpp_ret_t ret = nros_cpp_init_rmw(rmw, locator, domain_id, session_name, node_namespace,
3431 ::rclcpp::Node::global_storage());
3432 // No flag to set: `nros_cpp_init_rmw` stamps the context tag, and
3433 // `global_initialized()` reads it.
3434 return Result(ret);
3435}
3436
3437inline Result init_with_rmw(const char* rmw, const char* locator, uint8_t domain_id,
3438 const char* session_name) {
3439 // NROS_CPP_RET_INVALID_ARGUMENT = -3; see the 3-arg overload for why the
3440 // value is duplicated here rather than included.
3441 if (session_name == nullptr) {
3442 return Result(-3);
3443 }
3444#ifdef NROS_ENTRY_LOCATOR
3445 if (locator == nullptr) {
3446 locator = NROS_ENTRY_LOCATOR;
3447 }
3448#endif
3449#ifdef NROS_ENTRY_DOMAIN_ID
3450 if (domain_id == 0) {
3451 domain_id = static_cast<uint8_t>(NROS_ENTRY_DOMAIN_ID);
3452 }
3453#endif
3454 // No `NROS_ENTRY_RMW` fallback here: an explicit argument that resolves to
3455 // nullptr is the caller saying "no selector", and quietly substituting the
3456 // bake would make this overload unable to express that.
3457 // Issue 1568 — the one refusal every executor-storage path takes: the
3458 // linked library says whether this TU's `NROS_CPP_EXECUTOR_STORAGE_SIZE`
3459 // (the header it was compiled against — on NuttX the committed snapshot)
3460 // is enough for the executor it builds. Refused rather than overrun.
3461 {
3462 nros_cpp_ret_t chk =
3463 nros_cpp_executor_storage_check(::rclcpp::Node::global_storage(),
3464 sizeof(::rclcpp::Node::GlobalStorageHolder<>::storage));
3465 if (chk != 0) {
3466 return Result(chk);
3467 }
3468 }
3469 nros_cpp_ret_t ret = nros_cpp_init_rmw(rmw, locator, domain_id, session_name, nullptr,
3470 ::rclcpp::Node::global_storage());
3471 // No flag to set: `nros_cpp_init_rmw` stamps the context tag, and
3472 // `global_initialized()` reads it.
3473 return Result(ret);
3474}
3475
3477 if (!::rclcpp::Node::global_initialized()) {
3478 return Result::success();
3479 }
3480 // No flag to clear: `nros_cpp_fini` unstamps the tag, which is what
3481 // `global_initialized()` reads. One owner of the fact.
3482 return Result(nros_cpp_fini(::rclcpp::Node::global_storage()));
3483}
3484
3485inline void* global_handle() {
3486 if (!::rclcpp::Node::global_initialized()) return nullptr;
3487 return ::rclcpp::Node::global_storage();
3488}
3489
3490namespace detail {
3495inline int32_t apply_ros_args(void* executor, int argc, char const* const* argv) {
3496 // No `--ros-args` scope means nothing for the parser to read; skipping the
3497 // call keeps a plain `main(argc, argv)` off the FFI entirely.
3498 bool scoped = false;
3499 for (int i = 0; argv != nullptr && i < argc && !scoped; ++i) {
3500 const char* a = argv[i];
3501 const char* want = "--ros-args";
3502 while (a != nullptr && *a != '\0' && *a == *want) {
3503 ++a;
3504 ++want;
3505 }
3506 scoped = a != nullptr && *a == '\0' && *want == '\0';
3507 }
3508 // Issue 2073 -- an image that baked arguments of its own has them to
3509 // apply whatever the command line holds (a board's is empty).
3510 if (!scoped && !nros_cpp_baked_ros_args_present()) {
3511 return 0;
3512 }
3513 char why[256] = {0};
3514 const int32_t rc = nros_cpp_install_argv_remaps(executor, argc, argv, why, sizeof(why));
3515 if (rc != 0) {
3516 // issue 1303 — two records through `nros_log`, each short enough for
3517 // its format buffer: the refused token (bounded here), then the fixed
3518 // text, whose length `say_refused` checks at compile time. As one
3519 // `NROS_ERROR` record it was over 870 bytes, which a real sink cannot
3520 // carry: the body is dropped and only an ellipsis is printed.
3521 RCLC_LOG_ERROR(::rclcpp::detail::log_handle(nullptr), "rclcpp::init: refused: %.120s",
3522 why[0] != '\0' ? why : "invalid executor handle or argv");
3523 NROS_RCLCPP_SAY_REFUSED(NROS_RCLCPP_REFUSE_INIT_ARGV);
3524 }
3525 return rc;
3526}
3527
3531inline Result init_honouring_ros_args(int argc, char const* const* argv, const char* name) {
3532 const int32_t checked = apply_ros_args(nullptr, argc, argv);
3533 if (checked != 0) {
3534 return Result(checked);
3535 }
3536 Result r = init_in(nullptr, 0, name);
3537 if (!r.ok()) {
3538 return r;
3539 }
3540 const int32_t installed = apply_ros_args(::rclcpp::global_handle(), argc, argv);
3541 if (installed != 0) {
3542 (void)shutdown_in();
3543 return Result(installed);
3544 }
3545 return r;
3546}
3547} // namespace detail
3548
3549// -- init honouring --ros-args --
3550//
3551// `init_in(argc, argv)` delegates to the 3-arg `init_in`, which reads the
3552// environment. The session name falls back to `"nros_cpp"`.
3553
3554inline Result init_in(int argc, char const* const* argv, const char* session_name) {
3555 const char* name = (session_name != nullptr) ? session_name : "nros_cpp";
3556 return detail::init_honouring_ros_args(argc, argv, name);
3557}
3558
3564inline bool ok() {
3565 return ::rclcpp::Node::global_initialized() && !::nros_cpp_termination_requested();
3566}
3567
3576inline Result create_node(::rclcpp::Node& out, const char* name, const char* ns = nullptr) {
3577 if (!::rclcpp::Node::global_initialized()) {
3579 }
3580 out.executor_handle_ = ::rclcpp::Node::global_storage();
3581 return ::rclcpp::Node::create(out, name, ns);
3582}
3583
3596inline Result create_node_on(::rclcpp::Node& out, void* executor_handle, const char* name,
3597 const char* ns = nullptr) {
3598 if (executor_handle == nullptr) {
3600 }
3601 out.executor_handle_ = executor_handle;
3602 return ::rclcpp::Node::create(out, name, ns);
3603}
3604
3608inline ResultOf<::rclcpp::Node> make_node(const char* name, const char* ns = nullptr) {
3610 Result r = create_node(n, name, ns);
3611 if (!r.ok()) return ResultOf<::rclcpp::Node>::error(r);
3612 return ResultOf<::rclcpp::Node>::ok(::rclcpp::tr::forward_rvalue(n));
3613}
3614
3615// -- Executor::create_node implementation (requires full Node definition) --
3616
3617inline Result Executor::create_node(::rclcpp::Node& out, const char* name, const char* ns) {
3618 if (!initialized_) return Result(ErrorCode::NotInitialized);
3619 out.executor_handle_ = storage_;
3620 return ::rclcpp::Node::create(out, name, ns);
3621}
3622
3623// -- Phase 104.C.9 — NodeBuilder ----------------------------------------
3624//
3625// Mirrors Rust's `Executor::node_builder(name).rmw(...).locator(...).
3626// domain_id(...).namespace(...).sched(...).build()` chain. The C++
3627// wrapper is value-typed and stack-allocated; it accumulates options
3628// into an inline `nros_cpp_node_options_t` and ships it to
3629// `nros_cpp_node_create_ex` on `.build()`.
3630//
3631// Usage:
3632// ```cpp
3633// rclcpp::Node node;
3634// RCLCPP_TRY(executor.node_builder("egress")
3635// .rmw("cyclonedds")
3636// .domain_id(0)
3637// .build(node));
3638// ```
3639
3641 public:
3642 NodeBuilder(void* executor_handle, const char* name)
3643 : executor_handle_(executor_handle), name_(name),
3644 options_(nros_cpp_node_get_default_options()) {}
3645
3650 NodeBuilder& rmw(const char* name) {
3651 copy_bounded(name, options_.rmw_name, &options_.rmw_name_len, NROS_CPP_RMW_NAME_LEN);
3652 return *this;
3653 }
3654
3657 NodeBuilder& locator(const char* loc) {
3658 copy_bounded(loc, options_.locator, &options_.locator_len, NROS_CPP_LOCATOR_LEN);
3659 return *this;
3660 }
3661
3664 NodeBuilder& domain_id(uint32_t id) {
3665 options_.domain_id_override = id;
3666 return *this;
3667 }
3668
3674 NodeBuilder& namespace_(const char* ns) {
3675 copy_bounded(ns, options_.namespace_, &options_.namespace_len, NROS_CPP_NAMESPACE_LEN);
3676 return *this;
3677 }
3678
3681 NodeBuilder& sched(uint8_t sc_id) {
3682 options_.sched_context_id = sc_id;
3683 return *this;
3684 }
3685
3688 if (!executor_handle_) return Result(ErrorCode::NotInitialized);
3689 out.executor_handle_ = executor_handle_;
3690 nros_cpp_ret_t ret =
3691 nros_cpp_node_create_ex(executor_handle_, name_, &options_, &out.handle_);
3692 if (ret == 0) {
3693 out.initialized_ = true;
3694 }
3695 return Result(ret);
3696 }
3697
3698 private:
3699 static void copy_bounded(const char* src, uint8_t* dst, size_t* dst_len, size_t cap) {
3700 size_t n = 0;
3701 if (src != nullptr) {
3702 while (src[n] != '\0' && n < cap) {
3703 dst[n] = static_cast<uint8_t>(src[n]);
3704 ++n;
3705 }
3706 }
3707 // Zero out the tail so stale bytes don't leak across reuses.
3708 for (size_t i = n; i < cap; ++i) {
3709 dst[i] = 0;
3710 }
3711 *dst_len = n;
3712 }
3713
3714 void* executor_handle_;
3715 const char* name_;
3716 nros_cpp_node_options_t options_;
3717};
3718
3719inline NodeBuilder Executor::node_builder(const char* name) {
3720 return NodeBuilder(initialized_ ? handle() : nullptr, name);
3721}
3722
3723namespace detail {
3730 const DeclaredCallbackGroups& groups) {
3731 const char* missing = nullptr;
3732 int len = 0;
3733 if (first_uncreated_callback_group(groups, &missing, &len)) {
3734 report_callback_group_mismatch(node.get_name(), "declared", missing, len, groups.list);
3735 node.set_error("a CALLBACK_GROUPS group was never created",
3736 DECLARED_CALLBACK_GROUP_MISMATCH);
3737 }
3738}
3739} // namespace detail
3740
3741} // namespace rclcpp
3742
3743// The process-level verbs — `rclcpp::init()`, `rclcpp::ok()`,
3744// `rclcpp::shutdown()`, `rclcpp::spin_once()` — are declared in `nros.hpp`,
3745// which is where the free functions they name live. This header carries only
3746// what the node itself needs.
3747
3748#endif // NROS_RCLCPP_NODE_HPP
Definition timer.hpp:225
Definition node.hpp:222
Definition node.hpp:197
Definition rclcpp.hpp:195
Result create_node(::rclcpp::Node &out, const char *name, const char *ns=nullptr)
Definition node.hpp:3617
NodeBuilder node_builder(const char *name)
Definition node.hpp:3719
Definition node.hpp:3640
NodeBuilder & locator(const char *loc)
Definition node.hpp:3657
NodeBuilder & sched(uint8_t sc_id)
Definition node.hpp:3681
Result build(::rclcpp::Node &out) const
Materialize the Node.
Definition node.hpp:3687
NodeBuilder(void *executor_handle, const char *name)
Definition node.hpp:3642
NodeBuilder & rmw(const char *name)
Definition node.hpp:3650
NodeBuilder & domain_id(uint32_t id)
Definition node.hpp:3664
NodeBuilder & namespace_(const char *ns)
Definition node.hpp:3674
Definition node.hpp:3226
::rclcpp::TimerHandle create_wall_timer(::rclcpp::Duration period, Cb cb, const ::rclcpp::CallbackGroup *group=nullptr, bool autostart=true)
void create_timer_in_group(const CallbackGroup &group, uint64_t period_ms)
Definition node.hpp:3268
void create_timer_in_group(const CallbackGroup &group, uint64_t period_ms, nros_cpp_timer_callback_t callback, void *context=nullptr)
The plain C callback + ctx form, in a group, parked in the pool.
Definition node.hpp:3281
void create_wall_timer_in(uint64_t period_ms)
Definition node.hpp:3240
void create_wall_timer_in(uint64_t period_ms, nros_cpp_timer_callback_t callback, void *context=nullptr)
The plain C callback + ctx escape hatch, parked in the pool.
Definition node.hpp:3253
Definition node.hpp:845
Node & operator=(Node &&other)
Definition node.hpp:2952
bool check_declared_depth(const char *type_name, const char *topic, const ::rclcpp::QoS &qos)
Definition node.hpp:2796
Result create_subscription(::rclcpp::PollSubscription< M > &out, const char *topic, const ::rclcpp::QoS &qos, const ::rclcpp::SubscriptionOptions &options)
Result create_client(::rclcpp::PollClient< S > &out, const char *service_name, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
void create_subscription_in(const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
Result create_timer(Timer &out, const Clock &clock, uint64_t period_ms, C *self)
Definition node.hpp:2533
Result create_subscription_in_group(const ::rclcpp::CallbackGroup &group, ::rclcpp::Subscription< M > &out, const char *topic, const ::rclcpp::QoS &qos, F callback, const ::rclcpp::SubscriptionOptions &options={})
const nros_cpp_node_t * ffi_handle() const
Definition node.hpp:1887
Result count_subscribers(const char *topic_name, size_t *out_count) const
Definition node.hpp:1996
SharedPtr shared_from_this()
Definition node.hpp:1071
Result create_timer(Timer &out, const Clock &clock, uint64_t period_ms, nros_cpp_timer_callback_t callback, void *context=nullptr)
Definition node.hpp:2430
Time now() const
Definition node.hpp:1870
Result create_subscription(::rclcpp::PollSubscription< M > &out, const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
Result create_service(::rclcpp::Service< S > &out, const char *service_name, F callback, const ::rclcpp::QoS &qos=::rclcpp::QoS::services(), const ::rclcpp::ServiceOptions &options={})
const char * error_what() const
Definition node.hpp:973
Result get_parameter_types(const char *const *names, ::size_t count, ParameterType *out) const
Definition rclcpp.hpp:1955
Result create_publisher(::rclcpp::Publisher< M > &out, const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
typename::rclcpp::Service< S >::SharedPtr create_service(const char *, F, const ::rclcpp::QoS &=::rclcpp::QoS::services())
Result create_polling_action_client(::rclcpp::PollingActionClient< A > &out, const char *action_name)
Phase 122.3.d.b — Create an L1 polling-mode action client.
typename::rclcpp::Subscription< M >::SharedPtr create_subscription(const char *topic, ::size_t depth, Cb cb)
create_subscription<M>(topic, depth, callback).
::rclcpp::Handle< const Node > ConstWeakPtr
Const spelling of WeakPtr.
Definition node.hpp:1058
Result add_on_set_parameters_callback(OnSetParametersCallbackType callback, void *context, OnSetParametersCallbackToken &out_handle)
Definition rclcpp.hpp:2014
::rclcpp::Handle< const Node > ConstSharedPtr
rclcpp::Node::ConstSharedPtr.
Definition node.hpp:1053
Result create_timer_in_group(const ::rclcpp::CallbackGroup &group, Timer &out, uint64_t period_ms, C *self)
Definition node.hpp:2541
Node(const char *name, const char *ns, const ::rclcpp::NodeOptions &options)
Definition node.hpp:1015
Result create_client(::rclcpp::Client< S > &out, const char *service_name, F callback, const ::rclcpp::QoS &qos=::rclcpp::QoS::services(), const ::rclcpp::ClientOptions &options={})
::rclcpp::Logger get_logger() const
Definition node.hpp:1843
Result create_service(::rclcpp::PollService< S > &out, const char *service_name, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
typename::rclcpp::Subscription< M >::SharedPtr create_subscription(const char *topic, const ::rclcpp::QoS &qos, Cb cb, const ::rclcpp::SubscriptionOptions &options)
typename::rclcpp::Subscription< M >::SharedPtr create_subscription(const char *topic, ::size_t depth, Cb cb, const ::rclcpp::SubscriptionOptions &options)
create_subscription<M>(topic, depth, callback, options).
::rclcpp::Handle< Node > WeakPtr
Definition node.hpp:1056
Result create_subscription(::rclcpp::Subscription< M > &out, const char *topic, const ::rclcpp::QoS &qos, F callback, const ::rclcpp::SubscriptionOptions &options={})
::rclcpp::Owned<::rclcpp::PollService< S > > create_service(const char *name, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
Result create_subscription_with_info(::rclcpp::Subscription< M > &out, const char *topic, const ::rclcpp::QoS &qos, F callback, const ::rclcpp::SubscriptionOptions &options={})
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)
void create_generic_publisher(const A &topic, const B &type, const ::rclcpp::QoS &qos, const R &...)
Definition node.hpp:1355
Result count_clients(const char *service_name, size_t *out_count) const
Definition node.hpp:2005
Result create_wall_timer(Timer &out, uint64_t period_ms, C *self)
Definition node.hpp:2521
Result get_node_names(nros_cpp_node_visit_fn visit, void *ctx) const
Definition node.hpp:1956
Result init(const char *name, const char *ns=nullptr)
Definition node.hpp:884
::rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr get_node_waitables_interface()
See get_node_base_interface.
Definition node.hpp:1095
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
const char * serialization_format() const
Definition node.hpp:1798
~Node()
Definition node.hpp:2932
static Result create(Node &out, const char *name, const char *ns=nullptr)
Definition node.hpp:1737
bool get_parameter_or(const char *name, T &out, T fallback) const
Definition rclcpp.hpp:1935
typename::rclcpp::Service< S >::SharedPtr create_service(const char *name, F callback, const ::rclcpp::QoS &qos, const ::rclcpp::CallbackGroup *group)
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
::rclcpp::CallbackGroup::SharedPtr create_callback_group(::rclcpp::CallbackGroupType type, bool automatically_add_to_executor_with_node=true)
Definition node.hpp:2611
Result count_services(const char *service_name, size_t *out_count) const
Definition node.hpp:2012
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::Publisher< M > create_publisher_in(const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
Definition node.hpp:2747
Result get_publishers_info_by_topic(const char *topic_name, ::rclcpp::TopicEndpointInfoVisitFn visit, void *ctx) const
Definition node.hpp:2098
::rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr get_node_logging_interface()
See get_node_base_interface.
Definition node.hpp:1091
::rclcpp::Handle< Node > SharedPtr
Definition node.hpp:1051
typename::rclcpp::Service< S >::SharedPtr create_service(const char *name, F callback, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
Callback-style service server (void(const S::Request&, S::Response&)).
Node(Node &&other)
Definition node.hpp:2940
Node(const char *name, const ::rclcpp::NodeOptions &options)
Definition node.hpp:1008
const char * rmw_implementation_identifier() const
Definition node.hpp:1815
Result create_guard_condition(::rclcpp::GuardCondition &out, nros_cpp_guard_callback_t callback, void *context=nullptr)
Definition node.hpp:2695
const char * get_namespace() const
Get the node namespace.
Definition node.hpp:1757
bool initialized() const
Definition node.hpp:1127
void set_error(const char *what, int32_t code)
Definition node.hpp:2911
Node & nros_node()
Definition node.hpp:1131
Result get_client_names_and_types_by_node(const char *node_name, const char *node_namespace, nros_cpp_names_and_types_visit_fn visit, void *ctx) const
Definition node.hpp:2061
Result count_publishers(const char *topic_name, size_t *out_count) const
Definition node.hpp:1989
Result start_parameter_services()
Definition node.hpp:1919
int32_t error_code() const
The raw error code of the first latched failure, or 0.
Definition node.hpp:980
const char * get_name() const
Get the node name.
Definition node.hpp:1751
bool is_valid() const
Check if the node is initialized and valid.
Definition node.hpp:1873
::rclcpp::node_interfaces::NodeGraphInterface::SharedPtr get_node_graph_interface()
See get_node_base_interface.
Definition node.hpp:1087
Result create_wall_timer(Timer &out, uint64_t period_ms, nros_cpp_timer_callback_t callback, void *context=nullptr)
Definition node.hpp:2488
Result create_timer_in_group(const ::rclcpp::CallbackGroup &group, Timer &out, uint64_t period_ms, nros_cpp_timer_callback_t callback, void *context=nullptr)
Definition node.hpp:2628
::rclcpp::CallbackGroup create_callback_group(const char *name)
Definition node.hpp:2586
Node(::rclcpp::NodeHandle handle, const char *name, const char *ns=nullptr)
Definition node.hpp:929
Node(const char *name, const char *ns=nullptr)
Definition node.hpp:866
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)
typename::rclcpp::Publisher< M >::SharedPtr create_publisher(const char *topic, ::size_t depth)
create_publisher<M>(topic, depth) — the integer-depth spelling.
Result get_subscription_names_and_types_by_node(const char *node_name, const char *node_namespace, nros_cpp_names_and_types_visit_fn visit, void *ctx) const
Definition node.hpp:2038
bool check_declared_qos(const char *type_name, const char *topic, const ::rclcpp::QoS &qos)
Definition node.hpp:2829
Result create_timer_oneshot(Timer &out, uint64_t delay_ms, nros_cpp_timer_callback_t callback, void *context=nullptr)
Definition node.hpp:2555
Result get_service_names_and_types(nros_cpp_names_and_types_visit_fn visit, void *ctx) const
Definition node.hpp:1976
Result get_topic_names_and_types(nros_cpp_names_and_types_visit_fn visit, void *ctx) const
Definition node.hpp:1968
Result set_parameter(const char *name, T value)
Definition rclcpp.hpp:1898
const Clock * get_clock() const
Const overload of get_clock().
Definition node.hpp:1861
Result get_fully_qualified_name(char *buf, size_t buf_len, size_t *out_len=nullptr) const
Definition node.hpp:1776
Result get_subscriptions_info_by_topic(const char *topic_name, ::rclcpp::TopicEndpointInfoVisitFn visit, void *ctx) const
Definition node.hpp:2121
typename::rclcpp::Client< S >::SharedPtr create_client(const char *, F, const ::rclcpp::QoS &=::rclcpp::QoS::services())
REFUSED — upstream's shared_ptr handler shape.
Result create_polling_action_server(::rclcpp::PollingActionServer< A > &out, const char *action_name)
Clock * get_clock()
Definition node.hpp:1859
void create_generic_subscription(const A &topic, const B &type, const ::rclcpp::QoS &qos, Cb &&callback, const R &...)
Definition node.hpp:1366
const Node & nros_node() const
Const overload of [nros_node].
Definition node.hpp:1133
Result get_publisher_names_and_types_by_node(const char *node_name, const char *node_namespace, nros_cpp_names_and_types_visit_fn visit, void *ctx) const
Definition node.hpp:2020
Result get_publishers_info_by_topic(const char *topic_name, nros_cpp_endpoint_info_visit_fn visit, void *ctx) const
Definition node.hpp:2083
void create_subscription_in_group(const ::rclcpp::CallbackGroup &group, const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
::rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface()
Definition node.hpp:1079
Node()
Definition node.hpp:849
Result create_publisher_in_group(const ::rclcpp::CallbackGroup &, ::rclcpp::Publisher< M > &out, const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
Definition node.hpp:2682
Result undeclare_parameter(const char *name)
Definition rclcpp.hpp:1943
const ::rclcpp::NodeOptions & get_node_options() const
Definition node.hpp:1022
::rclcpp::node_interfaces::NodeClockInterface::SharedPtr get_node_clock_interface()
See get_node_base_interface.
Definition node.hpp:1083
Result create_publisher(::rclcpp::Publisher< M > &out, const char *topic, const ::rclcpp::QoS &qos, const ::rclcpp::PublisherOptions &options)
Result create_polling_subscription(::rclcpp::PollingSubscription< M > &out, const char *topic, const ::rclcpp::QoS &qos=::rclcpp::QoS::default_profile())
ConstSharedPtr shared_from_this() const
Const overload of [shared_from_this].
Definition node.hpp:1073
void set_qos_overrides(const nros_cpp_qos_override_t *overrides, size_t len)
Definition node.hpp:1899
Result init_on(void *executor_handle, const char *name, const char *ns=nullptr)
Definition node.hpp:895
Result get_subscriptions_info_by_topic(const char *topic_name, nros_cpp_endpoint_info_visit_fn visit, void *ctx) const
Definition node.hpp:2111
::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
Result get_service_names_and_types_by_node(const char *node_name, const char *node_namespace, nros_cpp_names_and_types_visit_fn visit, void *ctx) const
Definition node.hpp:2050
typename::rclcpp::Client< S >::SharedPtr create_client(const char *name, F callback, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
Callback-style service client (void(const S::Response&)).
Definition node.hpp:221
Definition node.hpp:217
Definition node.hpp:214
Definition node.hpp:212
Definition node.hpp:211
Definition node.hpp:215
Definition publisher.hpp:178
static constexpr QoS services()
Services profile: RELIABLE + VOLATILE + KEEP_LAST(10).
Definition qos.hpp:805
static constexpr QoS default_profile()
Default profile: RELIABLE + VOLATILE + KEEP_LAST(10).
Definition qos.hpp:799
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 result.hpp:332
static ResultOf ok(T value)
Definition result.hpp:334
static ResultOf error(ErrorCode code)
Definition result.hpp:340
Definition service.hpp:146
Definition node.hpp:200
Definition subscription.hpp:197
Definition timer.hpp:153
static const bool value
Definition qos.hpp:889
One slice's handle; Tag only keeps the five spellings distinct types.
Definition node.hpp:240
Node * node() const
Definition node.hpp:247
Context get_context() const
Definition rclcpp.hpp:318
const NodeInterfaceHandle * operator->() const
Definition node.hpp:251
NodeInterfaceHandle()
Definition node.hpp:244
NodeInterfaceHandle SharedPtr
Upstream's Node*InterfaceSharedPtr — the handle itself (no allocator).
Definition node.hpp:243
NodeInterfaceHandle(Node *node)
Definition node.hpp:245
Definition client.hpp:256
Definition server.hpp:351
rclcpp::Executor — drives transport I/O and dispatches callbacks.
int nros_cpp_ret_t
Definition future.hpp:25
rclcpp::GuardCondition — cross-thread wake source.
Definition rclcpp.hpp:418
void report_declared_depth_mismatch(const char *node_name, const char *topic, int declared, int passed)
Definition node.hpp:509
const char * declared_reliability_spelling(int ordinal)
The contract spelling of a reliability ordinal, for the report above.
Definition node.hpp:603
void report_declared_policy_mismatch(const char *node_name, const char *topic, const char *policy, const char *declared, const char *passed)
Definition node.hpp:579
void report_component_failure(const char *node_name, const char *what, int32_t code)
Definition node.hpp:480
constexpr int32_t DECLARED_PARAM_MISMATCH
Definition node.hpp:620
int32_t apply_ros_args(void *executor, int argc, char const *const *argv)
Definition node.hpp:3495
Result init_honouring_ros_args(int argc, char const *const *argv, const char *name)
Definition node.hpp:3531
const char * declared_durability_spelling(int ordinal)
The contract spelling of a durability ordinal, for the report above.
Definition node.hpp:611
constexpr int32_t DECLARED_DEPTH_MISMATCH
Definition node.hpp:504
void finish_declared_callback_groups(::rclcpp::Node &node, const DeclaredCallbackGroups &groups)
Definition node.hpp:3729
void report_callback_group_mismatch(const char *node_name, const char *how, const char *name, int len, const char *list)
Definition node.hpp:538
void report_declared_param_mismatch(const char *fqn, const char *param, const char *contract, int declared, int passed)
Definition node.hpp:626
constexpr int32_t DECLARED_QOS_MISMATCH
Definition node.hpp:574
Definition rclcpp.hpp:317
NodeInterfaceHandle< 3 > NodeLoggingInterface
Definition node.hpp:264
NodeInterfaceHandle< 0 > NodeBaseInterface
Definition node.hpp:261
NodeInterfaceHandle< 1 > NodeClockInterface
Definition node.hpp:262
NodeInterfaceHandle< 2 > NodeGraphInterface
Definition node.hpp:263
NodeInterfaceHandle< 4 > NodeWaitablesInterface
Definition node.hpp:265
Definition node.hpp:203
Client & operator=(Client &&other)
Definition client.hpp:1002
Definition rclcpp.hpp:75
constexpr uint8_t kDomainIdExplicitZero
Definition node.hpp:466
Result shutdown_in()
Definition node.hpp:3476
Result create_node(::rclcpp::Node &out, const char *name, const char *ns)
Definition node.hpp:3576
@ Reliable
Definition qos.hpp:149
@ BestEffort
Definition qos.hpp:150
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
void init()
rclcpp::init() — the zero-argument form. ADOPT.
Definition rclcpp.hpp:542
bool ok()
Definition node.hpp:3564
Result spin_in()
Definition rclcpp.hpp:331
Result create_node_on(::rclcpp::Node &out, void *executor_handle, const char *name, const char *ns)
Definition node.hpp:3596
ResultOf<::rclcpp::Node > make_node(const char *name, const char *ns=nullptr)
Definition node.hpp:3608
ResultOf< Publisher< M > > create_publisher(::rclcpp::Node &node, const char *topic, const QoS &qos=QoS::default_profile())
Definition publisher.hpp:705
void * global_handle()
Definition node.hpp:3485
@ InvalidArgument
Null pointer, empty topic name, or out-of-range value.
Definition result.hpp:122
@ AlreadyExists
Already exists (duplicate declare/register).
Definition result.hpp:126
@ NotInitialized
Definition result.hpp:131
Result init_with_rmw(const char *rmw, const char *locator=nullptr, uint8_t domain_id=0, const char *session_name="node")
Definition node.hpp:3437
@ Volatile
Definition qos.hpp:164
@ TransientLocal
Definition qos.hpp:165
ResultOf< void > Result
Definition result.hpp:247
rclcpp::QoS — full DDS-shaped QoS settings (Phase 108.B.7).
**auto handle
Definition client.hpp:127
::rclcpp::Owned< Client > SharedPtr
Definition client.hpp:487
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.
Definition node.hpp:400
constexpr const char * resolve_namespace(const char *fallback) const
The launch namespace if one was declared, else fallback.
Definition node.hpp:448
constexpr bool valid() const
Definition node.hpp:440
constexpr NodeHandle()
Definition node.hpp:422
const char * launch_name
Definition node.hpp:405
void * executor
Definition node.hpp:401
constexpr NodeHandle(void *exec)
Definition node.hpp:425
const char * launch_namespace
Launch-declared namespace, same contract.
Definition node.hpp:407
const nros_cpp_qos_override_t * qos_overrides
Definition node.hpp:418
constexpr const char * resolve_name(const char *fallback) const
Definition node.hpp:444
constexpr NodeHandle(void *exec, const char *name, const char *ns, const nros_cpp_qos_override_t *overrides, size_t override_count)
Definition node.hpp:436
constexpr NodeHandle(void *exec, const char *name, const char *ns)
Definition node.hpp:430
size_t qos_override_count
Entries in qos_overrides; 0 = none.
Definition node.hpp:420
rclcpp::Timer — periodic callback driven by the executor.