nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
executor.hpp
Go to the documentation of this file.
1// nros-cpp: Executor class
2// Freestanding C++ — no exceptions, no STL required
3
9
10#ifndef NROS_RCLCPP_EXECUTOR_HPP
11#define NROS_RCLCPP_EXECUTOR_HPP
12
13#include <cstdint>
14#include <cstddef>
15
16#include "rclcpp/result.hpp"
17// phase-417 stage 3 — the refusal vocabulary (`NROS_RCLCPP_REFUSE_*`,
18// `rclcpp::detail::refuse`, `rclcpp::detail::log_handle`) and the routed
19// `NROS_LOG_*` family both live here. `log.hpp` includes nothing of ours but
20// `std_detect.hpp`, so this cannot cycle.
21#include "rclcpp/logging.hpp"
22#include "rclcpp/detail/nros_cpp_config_generated.h"
23
24#include "rclcpp/detail/nros_cpp_ffi.h"
25
26// phase-427 W7 — `Node` is DEFINED in `rclcpp::` (RFC-0089: that namespace is
27// the home), so the forward declaration has to be there too.
28namespace rclcpp {
29class Node;
30// phase-515 P1 -- defined in `rclcpp.hpp`; `Executor::spin_until_future_complete`
31// returns it. Opaque scoped-enum declaration, underlying type `int` as there.
32enum class FutureReturnCode;
33} // namespace rclcpp
34
35namespace rclcpp {
36
37// Forward declarations
38class NodeBuilder;
39
47using ShutdownCallback = void (*)(void* context);
48
57 public:
60 ShutdownCallbackHandle() : value_(NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID) {}
61
64 explicit ShutdownCallbackHandle(nros_cpp_shutdown_callback_handle_t value) : value_(value) {}
65
67 bool valid() const { return value_ != NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID; }
68
70 nros_cpp_shutdown_callback_handle_t value() const { return value_; }
71
72 private:
73 nros_cpp_shutdown_callback_handle_t value_;
74};
75
88
95
119class Executor {
120 public:
122 Executor() : storage_(), initialized_(false) {}
123
133 static Result create(Executor& out, const char* locator = nullptr, uint8_t domain_id = 0) {
134 return create(out, locator, domain_id, "nros_cpp");
135 }
136
143 static Result create(Executor& out, const char* locator, uint8_t domain_id,
144 const char* session_name) {
145 // -3 = NROS_CPP_RET_INVALID_ARGUMENT (generated header).
146 if (session_name == nullptr) {
147 return Result(-3);
148 }
149 // Issue 1050 defect (3) — the same baked RMW selector `rclcpp::init()`
150 // reads. Both spellings of "open the one session" must apply it, or the
151 // bake means one thing on one path and nothing on the other — which is
152 // how `BACKENDS` came to read as a declaration that decided nothing.
153#ifdef NROS_ENTRY_RMW
154 const char* rmw = NROS_ENTRY_RMW;
155#else
156 const char* rmw = nullptr;
157#endif
158 nros_cpp_ret_t ret =
159 nros_cpp_init_rmw(rmw, locator, domain_id, session_name, nullptr, out.storage_);
160 if (ret == 0) {
161 out.initialized_ = true;
162 }
163 return Result(ret);
164 }
165
174 static Result create_with_rmw(Executor& out, const char* rmw, const char* locator = nullptr,
175 uint8_t domain_id = 0, const char* session_name = "nros_cpp") {
176 if (session_name == nullptr) {
177 return Result(-3);
178 }
179 nros_cpp_ret_t ret =
180 nros_cpp_init_rmw(rmw, locator, domain_id, session_name, nullptr, out.storage_);
181 if (ret == 0) {
182 out.initialized_ = true;
183 }
184 return Result(ret);
185 }
186
195 Result create_node(::rclcpp::Node& out, const char* name, const char* ns = nullptr);
196
204 NodeBuilder node_builder(const char* name);
205
231 Result spin_once(int32_t timeout_ms) {
232 // FIRST, before the `initialized_` check: the defect is in the VALUE,
233 // so it is knowable without a session and a caller must not have to
234 // stand one up to be told (RFC-0089 §"Where the refusal fires"). It
235 // used to be `timeout_ms.max(0)` Rust-side, which turned upstream's
236 // "block forever" into a 0 ms poll with no diagnostic at all.
237 //
238 // `NROS_RCLCPP_SAY_REFUSED`, not the legacy `NROS_ERROR`: the legacy
239 // sink is a no-op on every freestanding target, and a refusal nobody
240 // can read on the targets nano-ros exists for is issue 1019 again. It
241 // is also not the `"%s"` printf path, whose 256-byte buffer would cut
242 // the message off before the alternative.
243 if (timeout_ms < 0) {
244 NROS_RCLCPP_SAY_REFUSED(NROS_RCLCPP_REFUSE_UNBOUNDED_SPIN_RUNTIME);
246 }
247 if (!initialized_) return Result(ErrorCode::NotInitialized);
248 return Result(nros_cpp_spin_once(storage_, timeout_ms));
249 }
250
258 template <typename T = void> Result spin_once() {
259 static_assert(::rclcpp::detail::refuse<T>::value, NROS_RCLCPP_REFUSE_UNBOUNDED_SPIN);
261 }
262
282 Result spin_some(uint32_t max_ms = 0) {
283 if (!initialized_) return Result(ErrorCode::NotInitialized);
284 return Result(nros_cpp_spin_some(storage_, max_ms));
285 }
286
296 Result spin_all(uint32_t max_duration_ms) {
297 if (max_duration_ms == 0) return Result(ErrorCode::InvalidArgument);
298 return spin_some(max_duration_ms);
299 }
300
307 template <typename Future>
308 FutureReturnCode spin_until_future_complete(Future&& future, int32_t timeout_ms = -1);
309
319 template <typename NodePtr> void add_node(const NodePtr& node, bool notify = true);
320
325 template <typename NodePtr, typename T = void>
326 void remove_node(const NodePtr& node, bool notify = true) {
327 (void)node;
328 (void)notify;
329 static_assert(::rclcpp::detail::refuse<T>::value, NROS_RCLCPP_REFUSE_REMOVE_NODE);
330 }
331
336 template <typename NodePtr> Result spin_node_some(const NodePtr& node) {
337 add_node(node);
338 return spin_some();
339 }
340
345 template <typename NodePtr> Result spin_node_once(const NodePtr& node, int32_t timeout_ms) {
346 add_node(node);
347 return spin_once(timeout_ms);
348 }
349
361 Result ping(int32_t timeout_ms) {
362 if (!initialized_) return Result(ErrorCode::NotInitialized);
363 return Result(nros_cpp_executor_ping(storage_, timeout_ms));
364 }
365
383 Result get_node_names(nros_cpp_node_visit_fn visit, void* ctx) {
384 if (!initialized_) return Result(ErrorCode::NotInitialized);
385 return Result(nros_cpp_executor_get_node_names(storage_, visit, ctx));
386 }
387
394 Result get_topic_names_and_types(nros_cpp_names_and_types_visit_fn visit, void* ctx) {
395 if (!initialized_) return Result(ErrorCode::NotInitialized);
396 return Result(nros_cpp_executor_get_topic_names_and_types(storage_, visit, ctx));
397 }
398
401 Result get_service_names_and_types(nros_cpp_names_and_types_visit_fn visit, void* ctx) {
402 if (!initialized_) return Result(ErrorCode::NotInitialized);
403 return Result(nros_cpp_executor_get_service_names_and_types(storage_, visit, ctx));
404 }
405
411 Result count_publishers(const char* topic_name, size_t* out_count) {
412 if (!initialized_) return Result(ErrorCode::NotInitialized);
413 return Result(nros_cpp_executor_count_publishers(storage_, topic_name, out_count));
414 }
415
418 Result count_subscribers(const char* topic_name, size_t* out_count) {
419 if (!initialized_) return Result(ErrorCode::NotInitialized);
420 return Result(nros_cpp_executor_count_subscribers(storage_, topic_name, out_count));
421 }
422
426 Result count_clients(const char* service_name, size_t* out_count) {
427 if (!initialized_) return Result(ErrorCode::NotInitialized);
428 return Result(nros_cpp_executor_count_clients(storage_, service_name, out_count));
429 }
430
433 Result count_services(const char* service_name, size_t* out_count) {
434 if (!initialized_) return Result(ErrorCode::NotInitialized);
435 return Result(nros_cpp_executor_count_services(storage_, service_name, out_count));
436 }
437
439 Result get_publisher_names_and_types_by_node(const char* node_name, const char* node_namespace,
440 nros_cpp_names_and_types_visit_fn visit,
441 void* ctx) {
442 if (!initialized_) return Result(ErrorCode::NotInitialized);
443 return Result(nros_cpp_executor_get_publisher_names_and_types_by_node(
444 storage_, node_name, node_namespace, visit, ctx));
445 }
446
456 const char* node_namespace,
457 nros_cpp_names_and_types_visit_fn visit,
458 void* ctx) {
459 if (!initialized_) return Result(ErrorCode::NotInitialized);
460 return Result(nros_cpp_executor_get_subscription_names_and_types_by_node(
461 storage_, node_name, node_namespace, visit, ctx));
462 }
463
465 Result get_service_names_and_types_by_node(const char* node_name, const char* node_namespace,
466 nros_cpp_names_and_types_visit_fn visit, void* ctx) {
467 if (!initialized_) return Result(ErrorCode::NotInitialized);
468 return Result(nros_cpp_executor_get_service_names_and_types_by_node(
469 storage_, node_name, node_namespace, visit, ctx));
470 }
471
473 Result get_client_names_and_types_by_node(const char* node_name, const char* node_namespace,
474 nros_cpp_names_and_types_visit_fn visit, void* ctx) {
475 if (!initialized_) return Result(ErrorCode::NotInitialized);
476 return Result(nros_cpp_executor_get_client_names_and_types_by_node(
477 storage_, node_name, node_namespace, visit, ctx));
478 }
479
486 Result get_publishers_info_by_topic(const char* topic_name,
487 nros_cpp_endpoint_info_visit_fn visit, void* ctx) {
488 if (!initialized_) return Result(ErrorCode::NotInitialized);
489 return Result(
490 nros_cpp_executor_get_publishers_info_by_topic(storage_, topic_name, visit, ctx));
491 }
492
495 nros_cpp_endpoint_info_visit_fn visit, void* ctx) {
496 if (!initialized_) return Result(ErrorCode::NotInitialized);
497 return Result(
498 nros_cpp_executor_get_subscriptions_info_by_topic(storage_, topic_name, visit, ctx));
499 }
500
529 Result spin(int32_t poll_ms = 10) {
530 if (!initialized_) return Result(ErrorCode::NotInitialized);
531 return Result(nros_cpp_spin(storage_, poll_ms));
532 }
533
554 if (!initialized_) return Result(ErrorCode::NotInitialized);
555 return Result(nros_cpp_executor_cancel(storage_));
556 }
557
575 bool is_spinning() const {
576 return nros_cpp_executor_is_spinning(const_cast<uint8_t*>(storage_));
577 }
578
587 Result spin_for(uint32_t duration_ms, int32_t poll_ms = 10) {
588 if (!initialized_) return Result(ErrorCode::NotInitialized);
589 // Issue 0329 — the wall-clock budgeted loop (Phase 118.C: iteration-count
590 // budgeting collapses when `spin_once` returns early on a signaled
591 // condvar) now lives once, Rust-side, in `nros_cpp_spin_for`; forward to
592 // it so this and `rclcpp::spin()` share one implementation.
593 return Result(nros_cpp_spin_for(storage_, duration_ms, poll_ms));
594 }
595
597 bool ok() const { return initialized_; }
598
605 void* handle() { return storage_; }
606
630 Result add_pre_shutdown_callback(ShutdownCallback callback, void* context = nullptr,
631 PreShutdownCallbackHandle* out = nullptr) {
632 if (!initialized_) return Result(ErrorCode::NotInitialized);
633 nros_cpp_shutdown_callback_handle_t raw = NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID;
634 nros_cpp_ret_t ret = nros_cpp_add_pre_shutdown_callback(storage_, callback, context, &raw);
635 if (out != nullptr) *out = PreShutdownCallbackHandle(raw);
636 return Result(ret);
637 }
638
648 Result add_on_shutdown_callback(ShutdownCallback callback, void* context = nullptr,
649 OnShutdownCallbackHandle* out = nullptr) {
650 if (!initialized_) return Result(ErrorCode::NotInitialized);
651 nros_cpp_shutdown_callback_handle_t raw = NROS_CPP_SHUTDOWN_CALLBACK_HANDLE_INVALID;
652 nros_cpp_ret_t ret = nros_cpp_add_on_shutdown_callback(storage_, callback, context, &raw);
653 if (out != nullptr) *out = OnShutdownCallbackHandle(raw);
654 return Result(ret);
655 }
656
665 if (!initialized_) return false;
666 return nros_cpp_remove_pre_shutdown_callback(storage_, handle.value()) == 0;
667 }
668
673 if (!initialized_) return false;
674 return nros_cpp_remove_on_shutdown_callback(storage_, handle.value()) == 0;
675 }
676
679 if (!initialized_) return Result::success();
680 nros_cpp_ret_t ret = nros_cpp_fini(storage_);
681 initialized_ = false;
682 return Result(ret);
683 }
684
687 if (initialized_) {
688 nros_cpp_fini(storage_);
689 initialized_ = false;
690 }
691 }
692
693 // Move semantics (non-copyable)
694 Executor(Executor&& other) : initialized_(other.initialized_) {
695 for (unsigned i = 0; i < sizeof(storage_); ++i) {
696 storage_[i] = other.storage_[i];
697 other.storage_[i] = 0;
698 }
699 other.initialized_ = false;
700 }
701
703 if (this != &other) {
704 if (initialized_) {
705 nros_cpp_fini(storage_);
706 }
707 for (unsigned i = 0; i < sizeof(storage_); ++i) {
708 storage_[i] = other.storage_[i];
709 other.storage_[i] = 0;
710 }
711 initialized_ = other.initialized_;
712 other.initialized_ = false;
713 }
714 return *this;
715 }
716
717 private:
718 Executor(const Executor&) = delete;
719 Executor& operator=(const Executor&) = delete;
720
721 alignas(8) uint8_t storage_[NROS_CPP_EXECUTOR_STORAGE_SIZE];
722 bool initialized_;
723};
724
725} // namespace rclcpp
726
727#endif // NROS_RCLCPP_EXECUTOR_HPP
Definition executor.hpp:119
Result get_service_names_and_types(nros_cpp_names_and_types_visit_fn visit, void *ctx)
Definition executor.hpp:401
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
void add_node(const NodePtr &node, bool notify=true)
Definition rclcpp.hpp:1159
Result spin_once()
Definition executor.hpp:258
static Result create(Executor &out, const char *locator=nullptr, uint8_t domain_id=0)
Definition executor.hpp:133
Executor(Executor &&other)
Definition executor.hpp:694
Result spin(int32_t poll_ms=10)
Definition executor.hpp:529
Executor & operator=(Executor &&other)
Definition executor.hpp:702
Result spin_for(uint32_t duration_ms, int32_t poll_ms=10)
Definition executor.hpp:587
bool remove_pre_shutdown_callback(PreShutdownCallbackHandle handle)
Definition executor.hpp:664
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.
Definition executor.hpp:439
Result spin_some(uint32_t max_ms=0)
Definition executor.hpp:282
Result count_publishers(const char *topic_name, size_t *out_count)
Definition executor.hpp:411
NodeBuilder node_builder(const char *name)
Definition node.hpp:3719
Result get_topic_names_and_types(nros_cpp_names_and_types_visit_fn visit, void *ctx)
Definition executor.hpp:394
Result add_on_shutdown_callback(ShutdownCallback callback, void *context=nullptr, OnShutdownCallbackHandle *out=nullptr)
Definition executor.hpp:648
Result get_node_names(nros_cpp_node_visit_fn visit, void *ctx)
Definition executor.hpp:383
void * handle()
Definition executor.hpp:605
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)
Definition executor.hpp:455
bool remove_on_shutdown_callback(OnShutdownCallbackHandle handle)
Definition executor.hpp:672
Executor()
Default constructor — creates an uninitialized executor.
Definition executor.hpp:122
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.
Definition executor.hpp:494
void remove_node(const NodePtr &node, bool notify=true)
Definition executor.hpp:326
Result spin_all(uint32_t max_duration_ms)
Definition executor.hpp:296
FutureReturnCode spin_until_future_complete(Future &&future, int32_t timeout_ms=-1)
Definition rclcpp.hpp:1153
Result ping(int32_t timeout_ms)
Definition executor.hpp:361
Result cancel()
Definition executor.hpp:553
Result add_pre_shutdown_callback(ShutdownCallback callback, void *context=nullptr, PreShutdownCallbackHandle *out=nullptr)
Definition executor.hpp:630
Result get_publishers_info_by_topic(const char *topic_name, nros_cpp_endpoint_info_visit_fn visit, void *ctx)
Definition executor.hpp:486
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
Result count_subscribers(const char *topic_name, size_t *out_count)
Definition executor.hpp:418
Result spin_node_once(const NodePtr &node, int32_t timeout_ms)
Definition executor.hpp:345
bool is_spinning() const
Definition executor.hpp:575
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.
Definition executor.hpp:473
Result count_clients(const char *service_name, size_t *out_count)
Definition executor.hpp:426
~Executor()
Destructor — shuts down if still active.
Definition executor.hpp:686
Result count_services(const char *service_name, size_t *out_count)
Definition executor.hpp:433
static Result create_with_rmw(Executor &out, const char *rmw, const char *locator=nullptr, uint8_t domain_id=0, const char *session_name="nros_cpp")
Definition executor.hpp:174
Result spin_node_some(const NodePtr &node)
Definition executor.hpp:336
static Result create(Executor &out, const char *locator, uint8_t domain_id, const char *session_name)
Definition executor.hpp:143
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.
Definition executor.hpp:465
Definition future.hpp:337
Definition node.hpp:3640
Definition node.hpp:845
Definition executor.hpp:91
ShutdownCallbackHandle()
Definition executor.hpp:60
Definition executor.hpp:84
ShutdownCallbackHandle()
Definition executor.hpp:60
static constexpr ResultOf success()
Named constructors.
Definition result.hpp:239
ShutdownCallbackHandle(nros_cpp_shutdown_callback_handle_t value)
Definition executor.hpp:64
bool valid() const
True when this handle came from a successful registration.
Definition executor.hpp:67
ShutdownCallbackHandle()
Definition executor.hpp:60
nros_cpp_shutdown_callback_handle_t value() const
The raw FFI value, for passing to the C entry points directly.
Definition executor.hpp:70
int nros_cpp_ret_t
Definition future.hpp:25
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
Definition rclcpp.hpp:75
void(*)(void *context) ShutdownCallback
Definition executor.hpp:47
FutureReturnCode
Definition rclcpp.hpp:589
@ InvalidArgument
Null pointer, empty topic name, or out-of-range value.
Definition result.hpp:122
@ NotInitialized
Definition result.hpp:131
@ Unsupported
Operation not implemented by the active backend.
Definition result.hpp:152
ResultOf< void > Result
Definition result.hpp:247
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.