|
| template<typename NodePtr> |
| void | add_node (const NodePtr &node, bool notify=true) |
| Result | add_on_shutdown_callback (ShutdownCallback callback, void *context=nullptr, OnShutdownCallbackHandle *out=nullptr) |
| Result | add_pre_shutdown_callback (ShutdownCallback callback, void *context=nullptr, PreShutdownCallbackHandle *out=nullptr) |
| Result | cancel () |
| Result | count_clients (const char *service_name, size_t *out_count) |
| Result | count_publishers (const char *topic_name, size_t *out_count) |
| Result | count_services (const char *service_name, size_t *out_count) |
| Result | count_subscribers (const char *topic_name, size_t *out_count) |
| Result | create_node (::rclcpp::Node &out, const char *name, const char *ns=nullptr) |
| | Executor () |
| | Default constructor — creates an uninitialized executor.
|
| | Executor (Executor &&other) |
| 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) |
| | phase-381 W4 — what services one named node CALLS, with the types.
|
| Result | get_node_names (nros_cpp_node_visit_fn visit, void *ctx) |
| 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) |
| | phase-381 W4 — what one named node PUBLISHES, with the types.
|
| Result | get_publishers_info_by_topic (const char *topic_name, nros_cpp_endpoint_info_visit_fn visit, void *ctx) |
| Result | get_service_names_and_types (nros_cpp_names_and_types_visit_fn visit, void *ctx) |
| 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) |
| | phase-381 W4 — what services one named node SERVES, with the types.
|
| 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) |
| Result | get_subscriptions_info_by_topic (const char *topic_name, nros_cpp_endpoint_info_visit_fn visit, void *ctx) |
| | phase-381 W4 — the subscriptions on topic_name, one visit each.
|
| Result | get_topic_names_and_types (nros_cpp_names_and_types_visit_fn visit, void *ctx) |
| void * | handle () |
| bool | is_spinning () const |
| NodeBuilder | node_builder (const char *name) |
| bool | ok () const |
| | Check if the executor is initialized.
|
| Executor & | operator= (Executor &&other) |
| Result | ping (int32_t timeout_ms) |
| template<typename NodePtr, typename T = void> |
| void | remove_node (const NodePtr &node, bool notify=true) |
| bool | remove_on_shutdown_callback (OnShutdownCallbackHandle handle) |
| bool | remove_pre_shutdown_callback (PreShutdownCallbackHandle handle) |
| Result | shutdown () |
| | Shut down the executor and close the middleware connection.
|
| Result | spin (int32_t poll_ms=10) |
| Result | spin_all (uint32_t max_duration_ms) |
| Result | spin_for (uint32_t duration_ms, int32_t poll_ms=10) |
| template<typename NodePtr> |
| Result | spin_node_once (const NodePtr &node, int32_t timeout_ms) |
| template<typename NodePtr> |
| Result | spin_node_some (const NodePtr &node) |
| template<typename T = void> |
| Result | spin_once () |
| Result | spin_once (int32_t timeout_ms) |
| Result | spin_some (uint32_t max_ms=0) |
| template<typename Future> |
| FutureReturnCode | spin_until_future_complete (Future &&future, int32_t timeout_ms=-1) |
| | ~Executor () |
| | Destructor — shuts down if still active.
|
Explicit executor for managing ROS 2 entities and spinning.
Mirrors rclcpp::executors::SingleThreadedExecutor. Provides an explicit alternative to the global rclcpp::init()/rclcpp::spin_once() free functions.
The executor uses inline opaque storage — no heap allocation required.
Usage:
}
Definition executor.hpp:119
Result create_node(::rclcpp::Node &out, const char *name, const char *ns=nullptr)
Definition node.hpp:3617
bool ok() const
Check if the executor is initialized.
Definition executor.hpp:597
static Result create(Executor &out, const char *locator=nullptr, uint8_t domain_id=0)
Definition executor.hpp:133
Result shutdown()
Shut down the executor and close the middleware connection.
Definition executor.hpp:678
Result spin_once(int32_t timeout_ms)
Definition executor.hpp:231
#define RCLCPP_TRY(expr)
Definition result.hpp:259
template<typename NodePtr>
| void rclcpp::Executor::add_node |
( |
const NodePtr & | node, |
|
|
bool | notify = true ) |
|
inline |
add_node(node, notify) — phase-515 P2. ADOPT-BOUNDED.
A node's entities live in the arena of the executor that CREATED it (RFC-0002), so attachment is already done by [create_node]. This CHECKS it: a node created on this executor is a no-op; any other node (uninitialised, or created on another executor) aborts naming it, because spinning this executor would never reach its callbacks. notify is accepted and ignored — there is no waiting executor to wake. Takes rclcpp's Node::SharedPtr (or any pointer-like to a node).
Register a callback to run BEFORE this executor's entities are torn down — rclcpp's Context::add_pre_shutdown_callback. Issue 0790.
This is the load-bearing half and the one with no workaround. The callback runs while every publisher, subscription, service and client still works, so a node can publish a final state, answer a last request, park an actuator or release a bus. After teardown it cannot: on a device there is no OS to reclaim a claimed SPI bus, an armed DMA channel or an actuator holding its last commanded position.
The callbacks run from [shutdown] (and from the destructor, which shuts down if still active). They are a CLEAN-STOP facility: a watchdog reset, a hard fault or an abort does not come through here, and nothing backed by a static table could promise otherwise.
- Parameters
-
| callback | Function to invoke. Must not be null. |
| context | Opaque pointer handed back to callback. Must stay valid until the callback runs or is removed. |
| out | Receives the handle remove_pre_shutdown_callback takes. Optional. |
- Returns
- Success, ErrorCode::Full when the fixed table is exhausted (NROS_EXECUTOR_MAX_SHUTDOWN_CBS, default 2), or ErrorCode::NotInitialized.
| Result rclcpp::Executor::cancel |
( |
| ) |
|
|
inline |
Ask a running [spin] to stop, WITHOUT tearing down the session — rclcpp::Executor::cancel. phase-417 W4.c.
ADOPT-BOUNDED (RFC-0089): cancel sets a flag the spin loop observes at the NEXT POLL BOUNDARY, so it returns BEFORE spinning has actually stopped; [is_spinning] is the observable that tells you when it has. The boundary is one poll_ms wide (the spin() argument), so the delay is a duration you chose rather than an unknown. rclcpp's cancel() makes the same "returns immediately" promise; naming the width is the part we add.
This is the verb a signal handler wants. [shutdown] is the other one and still tears everything down: after cancel() the executor is still ok(), still owns its publishers, subscriptions, services and clients, and can be spun again with no rediscovery. Collapsing the two — which is what a C++ node had to do before this existed — costs a full discovery round on every mode change.
Safe to call from another thread or a signal handler.
| Result rclcpp::Executor::get_node_names |
( |
nros_cpp_node_visit_fn | visit, |
|
|
void * | ctx ) |
|
inline |
phase-381 W4 — every node on the graph, with its namespace.
visit(name, ns, enclave) is called once per node; enclave is nullptr where the backend tracks none, which is what lets one call answer both rmw_get_node_names forms. Return false to stop early.
Takes a plain function pointer plus ctx rather than a std::function: this header is compiled -nostdinc++ against Zephyr's minimal libcpp in embedded builds, where <functional> does not exist (issue 0112). Every string is BORROWED for the duration of the call.
Reports what has been DISCOVERED and never blocks. The first call after startup legitimately sees a partial graph — the backend keeps a standing query fed by the spin loop — so poll rather than calling once and concluding. An empty enumeration means "nobody seen yet", never "nobody exists", and ErrorCode::Unsupported (a backend with no graph) stays distinct from it.
| Result rclcpp::Executor::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 ) |
|
inline |
phase-381 W4 — what one named node SUBSCRIBES to, with the types.
subscription, not subscriber: the C++ surface takes rclcpp's vocabulary (create_subscription, Subscription<T>, get_subscriptions_info_by_topic). rclcpp has no *_by_node form for subscriptions at all, so the WORD comes from its vocabulary rather than from a method it lacks. The C surface says subscriber because rcl does, and the vtable slot because upstream rmw does.
| bool rclcpp::Executor::is_spinning |
( |
| ) |
const |
|
inline |
Is a spin loop running on this executor right now? — rclcpp::Executor::is_spinning. phase-417 W4.c.
A DIFFERENT question from [ok], which answers "is this executor
INITIALISED" — an executor that has never spun is ok() and not spinning, and one that has been cancelled mid-poll is both. Before this existed, ok() was the closest thing on offer and it answered neither half of the cancel envelope.
This is the second half of [cancel]'s contract: poll it to learn when the cancel has been ACTED on.
const because it is a pure read, and a predicate a caller cannot ask of a const Executor& is a predicate with an arbitrary restriction. The FFI slot takes void* because every OTHER nros_cpp_executor_* entry point mutates through it (see [handle]); this one does not, and the const_cast is confined to that mismatch.
| Result rclcpp::Executor::spin |
( |
int32_t | poll_ms = 10 | ) |
|
|
inline |
Spin until this executor is shut down (blocking) — rclcpp::Executor::spin.
Issue 0338 — this verb used to mean the OPPOSITE here: spin was the BOUNDED form and there was no way to say "spin forever" on an executor, while spin blocks until shutdown in rclcpp, in the C API (nros_executor_spin) and in Rust. A user porting rclcpp code wrote exec.spin() and it did not compile; reaching for spin(ms) instead silently returned early. The bounded form is now [spin_for].
Exit condition: [cancel] on THIS executor — typically from a signal handler or another thread, which is rclcpp's clean-shutdown idiom. Returns the first non-success spin_once result, or success after a clean cancel.
phase-417 W4.c — the loop MOVED. It used to be right here, spelled while (initialized_), which made [shutdown] the only way out; and shutdown() calls nros_cpp_fini, so a C++ node could not stop spinning without tearing down the middleware and paying a full discovery round to come back. Two things were wrong with that: the missing verb, and a polling loop living in the wrapper (RFC-0020 violation class 2, the same defect issue 0329 fixed for the BOUNDED form). Both are gone — nros_cpp_spin is the loop, Rust-side, and this is a forwarder.
A concurrent shutdown() is no longer the supported way to end a spin. It never really was: it frees the executor the loop is polling. Say cancel(), then shutdown() once spin() has returned.
- Parameters
-
| poll_ms | Individual spin_once timeout (default: 10ms). |
template<typename T = void>
| Result rclcpp::Executor::spin_once |
( |
| ) |
|
|
inline |
REFUSED — spin_once() with no budget. phase-417 stage 3.
A member template with a defaulted parameter, not = delete: these headers are parsed as C++14, where a deleted function carries no message (= delete("reason") is C++26). The static_assert fires on INSTANTIATION, so the name stays declarable and only USING it fails, with the migration attached.
| Result rclcpp::Executor::spin_once |
( |
int32_t | timeout_ms | ) |
|
|
inline |
Drive transport I/O and dispatch callbacks.
Processes pending subscriptions, timers, services, and guard conditions. Call this periodically in your main loop.
The budget is REQUIRED — phase-417 stage 3. Upstream's spin_once(std::chrono::nanoseconds timeout = -1) blocks indefinitely by default; this one cannot, so there is no default to give that would not be a budget the caller never chose. It used to be = 10, and a ported argument-free spin_once() therefore returned after 10 ms where upstream was still waiting — silently. The no-argument form is now a compile error carrying NROS_RCLCPP_REFUSE_UNBOUNDED_SPIN; a negative timeout_ms is refused at the call, for the same reason under the same message.
ADOPT-BOUNDED envelope (RFC-0089 W3.d), and it is the part no signature carries: this dispatches EVERY ready arena entry, where upstream's spin_once executes the next ONE. RFC-0002 §3 computes the ready bitmap once per cycle, so "one item" is not a shape this executor has. A spin_once(0) here is one pass of upstream's spin_some, which this class now also has under its own name (phase-488, [spin_some]).
- Parameters
-
| timeout_ms | Maximum time to block waiting for I/O. 0 polls; negative is REFUSED (see above). |
- Returns
- Result indicating success or failure.
| Result rclcpp::Executor::spin_some |
( |
uint32_t | max_ms = 0 | ) |
|
|
inline |
spin_some(max_duration = 0) — phase-488, rclcpp's drain verb. ADOPT.
Executes everything already ready and returns without waiting for new work. A callback that makes more work ready (a publish to a topic this executor also subscribes) is drained in the same call, pass after pass, until a pass does nothing — the same drain Rust's Executor::spin_some runs, through one implementation (nros_cpp_spin_some). max_ms bounds that loop and is checked BETWEEN passes, so a single long callback overruns it; 0, the default, is rclcpp's "no limit".
The bound is MILLISECONDS in a uint32_t, not a std::chrono::nanoseconds: this surface cannot require <chrono> (the cpp:Executor::spin_once row). A non-zero bound on an executor with no clock answers ErrorCode::Unsupported instead of draining unbounded.
Where spin_once(timeout_ms) is a blocking wait with a budget, this is a poll: use it in a loop that does its own work between calls.