RMW API — every upstream symbol, side by side
What ROS 2’s rmw asks an implementation for, what nano-ros provides, and
the reason wherever the two differ. Derived from three sources — the ROS 2
signature extract, the nano-ros ABI headers, and the authored reason map
docs/reference/rmw-api-map.toml that just check rmw-api-parity also reads —
so a slot changing shape moves this page in the same commit or fails the gate.
For the prose rationale behind the big divergences, see
RMW API: Differences from upstream rmw.h.
How to read a row
The signature says which surface it is on. nano-ros answers an upstream symbol in one of two ways, and C syntax already distinguishes them:
rmw_ret_t (*count_publishers)(...) // vtable slot — a function pointer,
// may differ per backend
rmw_ret_t rmw_compare_gids_equal(...) // global — a plain exported function,
// defined once for the image
Slot names drop the rmw_ prefix because that is genuinely the name in
nros_rmw_vtable_t; showing rmw_count_publishers on the right would flatter
the comparison.
Each side is spelled the way its own headers spell it. Upstream writes
const rmw_publisher_t * publisher, we write const rmw_publisher_t *publisher,
and the two columns keep their own convention rather than being normalised to
a third that neither project uses. Parameter NAMES are shown on both sides;
the comparison behind the highlighting is on TYPES only, because a renamed
argument is not an ABI difference.
The chip says what we DID with the symbol, which is a different question from what the signature shows:
| chip | means |
|---|---|
● same | identical signature and name |
● re-shaped | one slot, one symbol, different signature |
◆ re-mapped | answered, but NOT 1:1 — decomposed, merged, or off this seam. The arrows under it name what provides the capability |
✕ not-supported | a decision, permanent; the reason names the constraint |
○ not-implemented | a gap, with the issue tracking it |
same and re-shaped are derived from the signatures, so the map cannot
assert a match the types deny. The other three are authored, because no
signature can say whether an absent symbol was decomposed or dropped — and
not-implemented must name an issue, so silence cannot turn a gap into a
decision.
Marks show the difference. Red — upstream takes it, we do not. Green — we
take it, upstream does not. Yellow — the name differs from the mechanical one
(upstream minus rmw_). A row with no marks is identical on both sides and
carries no reason, because there is nothing to explain.
Argument names appear on the right only. The ROS 2 side is read from a
signature extract that drops parameter names on purpose — a renamed argument is
not an ABI difference, and reporting one trains people to skim. Ours are kept
because they are the only place a reader learns what an argument means:
size_t twice in a row is not a signature anyone can act on. The comparison
itself runs on types, so a name never colours a row.
An empty right-hand cell reads rejected when the symbol is deliberately absent, or answered elsewhere when the capability ships outside the RMW seam — in the executor, in codegen, or inside a backend. Both carry the reason.
A dimmed right-hand cell is an inert slot: declared in the vtable, written and read by nothing. A reserved shape, not a working capability (issue 0800).
What is being compared
Not rmw.h against rmw_vtable.h — that comparison is wrong twice. Upstream
declares 177 RMW_PUBLIC functions, but most are utilities rmw itself
defines; an implementation links those, so comparing against 177 manufactures
~90 phantom gaps. And our vtable is only the backend seam: plenty of what
upstream calls rmw lives one layer up in the executor, one layer down in a
backend, or in codegen.
So the contract is empirical — the 88 rmw_* symbols that
librmw_fastrtps_cpp.so and librmw_zenoh_cpp.so both define. Two independent
implementations with identical symbol sets is a better definition of “what an
rmw must provide” than any reading of the headers.
| vtable | global | executor | platform | build time | serdes | runtime | none | total | |
|---|---|---|---|---|---|---|---|---|---|
| same | 12 | 1 | 13 | ||||||
| re-shaped | 33 | 1 | 34 | ||||||
| re-mapped | 16 | 4 | 1 | 3 | 3 | 1 | 28 | ||
| not supported — by decision | 13 | 13 | |||||||
| total | 61 | 2 | 4 | 1 | 3 | 3 | 1 | 13 | 88 |
Read a row for what we did, a column for where it lives. Only not implemented should shrink over time; not supported is the one line that is a decision rather than a state, so it is expected to stay.
Every contract symbol
| ROS 2 | nano-ros | reason |
|---|---|---|
rmw_ret_t rmw_borrow_loaned_message( const rmw_publisher_t * publisher, const rosidl_message_type_support_t * type_support, void * * ros_message ) |
rmw_ret_t (*borrow_loaned_message)( const rmw_publisher_t *publisher, size_t requested_len, rmw_mut_byte_span_t *out_slot, rmw_loan_token_t **out_token ) |
● re-shaped
types are resolved at BUILD time — Upstream resolves a type through a runtime typesupport pointer. Ours are resolved by codegen before the image exists, so the seam carries bytes and a type hash rather than a pointer to a type description that would have to be walked at runtime.bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_client_request_publisher_get_actual_qos( const rmw_client_t * client, rmw_qos_profile_t * qos ) |
rmw_ret_t (*client_request_publisher_get_actual_qos)( const rmw_client_t *client, rmw_qos_profile_t *qos ) |
● same
|
rmw_ret_t rmw_client_response_subscription_get_actual_qos( const rmw_client_t * client, rmw_qos_profile_t * qos ) |
rmw_ret_t (*client_response_subscription_get_actual_qos)( const rmw_client_t *client, rmw_qos_profile_t *qos ) |
● same
|
rmw_ret_t rmw_client_set_on_new_response_callback( rmw_client_t * client, rmw_event_callback_t callback, const void * user_data ) |
— |
✕ not-supported · by decision
as rmw_service_set_on_new_request_callback (issue 0960). |
rmw_ret_t rmw_compare_gids_equal( const rmw_gid_t * gid1, const rmw_gid_t * gid2, bool * result ) |
rmw_ret_t rmw_compare_gids_equal( const rmw_gid_t *gid1, const rmw_gid_t *gid2, bool *result ) |
● same
|
rmw_ret_t rmw_context_fini(rmw_context_t * context) |
rmw_ret_t (*destroy_session)(rmw_session_t *session) |
◆ re-mapped · 2 upstream → 1 slot
renamed — the slot is destroy_session.the SESSION is the seam — As `rmw_node_t`: upstream's context is the process-wide init state, and ours is the session the slot is being called on. |
rmw_ret_t rmw_count_publishers( const rmw_node_t * node, const char * topic_name, size_t * count ) |
rmw_ret_t (*count_publishers)( const rmw_session_t *session, const char *topic_name, size_t *count ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_ret_t rmw_count_subscribers( const rmw_node_t * node, const char * topic_name, size_t * count ) |
rmw_ret_t (*count_subscribers)( const rmw_session_t *session, const char *topic_name, size_t *count ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_client_t * rmw_create_client( const rmw_node_t * node, const rosidl_service_type_support_t * type_support, const char * service_name, const rmw_qos_profile_t * qos_policies ) |
rmw_ret_t (*create_client)( const rmw_node_t *node, const rmw_service_type_support_t *type_support, const char *service_name, uint32_t domain_id, const rmw_qos_profile_t *qos, rmw_client_t *out ) |
● re-shaped
types are resolved at BUILD time — As the message typesupport. |
rmw_guard_condition_t * rmw_create_guard_condition(rmw_context_t * context) |
— |
◆ re-mapped · executor
executor: EntryKind::GuardCondition
|
rmw_node_t * rmw_create_node( rmw_context_t * context, const char * name, const char * namespace_ ) |
rmw_ret_t (*create_node)( rmw_session_t *session, const char *name, const char *namespace_, rmw_node_t *out ) |
● re-shaped
the SESSION is the seam — As `rmw_node_t`: upstream's context is the process-wide init state, and ours is the session the slot is being called on. |
rmw_publisher_t * rmw_create_publisher( const rmw_node_t * node, const rosidl_message_type_support_t * type_support, const char * topic_name, const rmw_qos_profile_t * qos_profile, const rmw_publisher_options_t * publisher_options ) |
rmw_ret_t (*create_publisher)( const rmw_node_t *node, const rmw_message_type_support_t *type_support, const char *topic_name, uint32_t domain_id, const rmw_qos_profile_t *qos, const rmw_publisher_options_t *options, rmw_publisher_t *out ) |
● re-shaped
types are resolved at BUILD time — Upstream resolves a type through a runtime typesupport pointer. Ours are resolved by codegen before the image exists, so the seam carries bytes and a type hash rather than a pointer to a type description that would have to be walked at runtime. |
rmw_service_t * rmw_create_service( const rmw_node_t * node, const rosidl_service_type_support_t * type_support, const char * service_name, const rmw_qos_profile_t * qos_profile ) |
rmw_ret_t (*create_service)( const rmw_node_t *node, const rmw_service_type_support_t *type_support, const char *service_name, uint32_t domain_id, const rmw_qos_profile_t *qos, rmw_service_t *out ) |
● re-shaped
types are resolved at BUILD time — As the message typesupport. |
rmw_subscription_t * rmw_create_subscription( const rmw_node_t * node, const rosidl_message_type_support_t * type_support, const char * topic_name, const rmw_qos_profile_t * qos_policies, const rmw_subscription_options_t * subscription_options ) |
rmw_ret_t (*create_subscription)( const rmw_node_t *node, const rmw_message_type_support_t *type_support, const char *topic_name, uint32_t domain_id, const rmw_qos_profile_t *qos, const rmw_subscription_options_t *options, rmw_subscription_t *out ) |
● re-shaped
types are resolved at BUILD time — Upstream resolves a type through a runtime typesupport pointer. Ours are resolved by codegen before the image exists, so the seam carries bytes and a type hash rather than a pointer to a type description that would have to be walked at runtime. |
rmw_wait_set_t * rmw_create_wait_set( rmw_context_t * context, size_t max_conditions ) |
— |
◆ re-mapped · executor
executor: the arena entry table, allocated once
|
rmw_ret_t rmw_deserialize( const rmw_serialized_message_t * serialized_message, const rosidl_message_type_support_t * type_support, void * ros_message ) |
— |
◆ re-mapped · 1 → 2
nros-serdes: Deserialize / DeserializeView
codegen packs
|
rmw_ret_t rmw_destroy_client( rmw_node_t * node, rmw_client_t * client ) |
rmw_ret_t (*destroy_client)(rmw_client_t *client) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_ret_t rmw_destroy_guard_condition(rmw_guard_condition_t * guard_condition) |
— |
◆ re-mapped · executor
executor: EntryKind::GuardCondition
|
rmw_ret_t rmw_destroy_node(rmw_node_t * node) |
rmw_ret_t (*destroy_node)(rmw_node_t *node) |
● same
|
rmw_ret_t rmw_destroy_publisher( rmw_node_t * node, rmw_publisher_t * publisher ) |
rmw_ret_t (*destroy_publisher)(rmw_publisher_t *publisher) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_ret_t rmw_destroy_service( rmw_node_t * node, rmw_service_t * service ) |
rmw_ret_t (*destroy_service)(rmw_service_t *server) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_ret_t rmw_destroy_subscription( rmw_node_t * node, rmw_subscription_t * subscription ) |
rmw_ret_t (*destroy_subscription)(rmw_subscription_t *subscription) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_ret_t rmw_destroy_wait_set(rmw_wait_set_t * wait_set) |
— |
◆ re-mapped · executor
executor: the arena entry table, allocated once
|
rmw_ret_t rmw_event_set_callback( rmw_event_t * event, rmw_event_callback_t callback, const void * user_data ) |
rmw_ret_t (*publisher_event_init)( const rmw_publisher_t *publisher, rmw_event_type_t kind, uint32_t deadline_ms, rmw_status_event_callback_t cb, void *user_context ) |
◆ re-mapped · 2 upstream → 1 slot
renamed — the slot is publisher_event_init.events are a slot family, not an event object — Upstream models an event as an object you take from. Ours are per-kind slots on the session (`take_event` and the event mask in `rmw_event.h`), so the object that would carry the kind is the slot itself. one wake callback per session — Upstream installs a callback per entity. Ours is `set_wake_callback` on the session: one executor drives several backends, so the wake has to be attributable to a session rather than to whichever entity fired. bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
bool rmw_feature_supported(rmw_feature_t feature) |
bool (*feature_supported)(rmw_feature_t feature) |
◆ re-mapped
per-capability slots, answered by nullity or a dedicated probe
|
rmw_ret_t rmw_fini_publisher_allocation(rmw_publisher_allocation_t * allocation) |
— |
✕ not-supported · by decision
as above |
rmw_ret_t rmw_fini_subscription_allocation(rmw_subscription_allocation_t * allocation) |
— |
✕ not-supported · by decision
as above |
rmw_ret_t rmw_get_client_names_and_types_by_node( const rmw_node_t * node, rcutils_allocator_t * allocator, const char * node_name, const char * node_namespace, rmw_names_and_types_t * service_names_and_types ) |
rmw_ret_t (*get_client_names_and_types_by_node)( const rmw_session_t *session, const char *node_name, const char *node_namespace, rmw_names_and_types_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — Upstream fills a heap-owning out-parameter the caller must `fini`. We pass a VISITOR callback invoked once per entry, with borrowed strings valid for the call. No allocation, bounded work, and nothing for a caller to leak — which is what makes the graph family available on a target with no allocator at all. |
rmw_ret_t rmw_get_gid_for_publisher( const rmw_publisher_t * publisher, rmw_gid_t * gid ) |
rmw_ret_t (*get_gid_for_publisher)( const rmw_publisher_t *publisher, rmw_gid_t *gid ) |
● same
|
const char * rmw_get_implementation_identifier(void) |
const char * (*get_implementation_identifier)(void) |
◆ re-mapped · runtime
runtime: the registry name
|
rmw_ret_t rmw_get_node_names( const rmw_node_t * node, rcutils_string_array_t * node_names, rcutils_string_array_t * node_namespaces ) |
rmw_ret_t (*get_node_names)( const rmw_session_t *session, rmw_node_visitor_t visitor ) |
◆ re-mapped · 2 upstream → 1 slot
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.visitor, not an owning out-param — As `rmw_names_and_types_t` — an owning array upstream, a visitor here. |
rmw_ret_t rmw_get_node_names_with_enclaves( const rmw_node_t * node, rcutils_string_array_t * node_names, rcutils_string_array_t * node_namespaces, rcutils_string_array_t * enclaves ) |
rmw_ret_t (*get_node_names)( const rmw_session_t *session, rmw_node_visitor_t visitor ) |
◆ re-mapped · 2 upstream → 1 slot
renamed — the slot is get_node_names.the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. visitor, not an owning out-param — As `rmw_names_and_types_t` — an owning array upstream, a visitor here. |
rmw_ret_t rmw_get_publisher_names_and_types_by_node( const rmw_node_t * node, rcutils_allocator_t * allocator, const char * node_name, const char * node_namespace, bool no_demangle, rmw_names_and_types_t * topic_names_and_types ) |
rmw_ret_t (*get_publisher_names_and_types_by_node)( const rmw_session_t *session, const char *node_name, const char *node_namespace, bool no_demangle, rmw_names_and_types_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — Upstream fills a heap-owning out-parameter the caller must `fini`. We pass a VISITOR callback invoked once per entry, with borrowed strings valid for the call. No allocation, bounded work, and nothing for a caller to leak — which is what makes the graph family available on a target with no allocator at all. |
rmw_ret_t rmw_get_publishers_info_by_topic( const rmw_node_t * node, rcutils_allocator_t * allocator, const char * topic_name, bool no_mangle, rmw_topic_endpoint_info_array_t * publishers_info ) |
rmw_ret_t (*get_publishers_info_by_topic)( const rmw_session_t *session, const char *topic_name, bool no_mangle, rmw_topic_endpoint_info_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — `rmw_topic_endpoint_info_array_t` and `rmw_network_flow_endpoint_array_t` are heap-owning arrays with their own `fini`. Visited one entry at a time instead. |
const char * rmw_get_serialization_format(void) |
const char * (*get_serialization_format)(void) |
◆ re-mapped
vtable: get_serialization_format, per session
|
rmw_ret_t rmw_get_serialized_message_size( const rosidl_message_type_support_t * type_support, const rosidl_runtime_c__Sequence__bound * message_bounds, size_t * size ) |
— |
◆ re-mapped · serdes
nros-serdes: size.rs (size_bound / max_serialized_size)
|
rmw_ret_t rmw_get_service_names_and_types( const rmw_node_t * node, rcutils_allocator_t * allocator, rmw_names_and_types_t * service_names_and_types ) |
rmw_ret_t (*get_service_names_and_types)( const rmw_session_t *session, rmw_names_and_types_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — Upstream fills a heap-owning out-parameter the caller must `fini`. We pass a VISITOR callback invoked once per entry, with borrowed strings valid for the call. No allocation, bounded work, and nothing for a caller to leak — which is what makes the graph family available on a target with no allocator at all. |
rmw_ret_t rmw_get_service_names_and_types_by_node( const rmw_node_t * node, rcutils_allocator_t * allocator, const char * node_name, const char * node_namespace, rmw_names_and_types_t * service_names_and_types ) |
rmw_ret_t (*get_service_names_and_types_by_node)( const rmw_session_t *session, const char *node_name, const char *node_namespace, rmw_names_and_types_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — Upstream fills a heap-owning out-parameter the caller must `fini`. We pass a VISITOR callback invoked once per entry, with borrowed strings valid for the call. No allocation, bounded work, and nothing for a caller to leak — which is what makes the graph family available on a target with no allocator at all. |
rmw_ret_t rmw_get_subscriber_names_and_types_by_node( const rmw_node_t * node, rcutils_allocator_t * allocator, const char * node_name, const char * node_namespace, bool no_demangle, rmw_names_and_types_t * topic_names_and_types ) |
rmw_ret_t (*get_subscriber_names_and_types_by_node)( const rmw_session_t *session, const char *node_name, const char *node_namespace, bool no_demangle, rmw_names_and_types_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — Upstream fills a heap-owning out-parameter the caller must `fini`. We pass a VISITOR callback invoked once per entry, with borrowed strings valid for the call. No allocation, bounded work, and nothing for a caller to leak — which is what makes the graph family available on a target with no allocator at all. |
rmw_ret_t rmw_get_subscriptions_info_by_topic( const rmw_node_t * node, rcutils_allocator_t * allocator, const char * topic_name, bool no_mangle, rmw_topic_endpoint_info_array_t * subscriptions_info ) |
rmw_ret_t (*get_subscriptions_info_by_topic)( const rmw_session_t *session, const char *topic_name, bool no_mangle, rmw_topic_endpoint_info_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — `rmw_topic_endpoint_info_array_t` and `rmw_network_flow_endpoint_array_t` are heap-owning arrays with their own `fini`. Visited one entry at a time instead. |
rmw_ret_t rmw_get_topic_names_and_types( const rmw_node_t * node, rcutils_allocator_t * allocator, bool no_demangle, rmw_names_and_types_t * topic_names_and_types ) |
rmw_ret_t (*get_topic_names_and_types)( const rmw_session_t *session, bool no_demangle, rmw_names_and_types_visitor_t visitor ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down.no allocator at this seam — This ABI does not allocate. Upstream hands an `rcutils_allocator_t` so the implementation can size and own the result; ours writes into a caller-owned byte range whose capacity the caller already knows. An allocator parameter cannot cross a seam that has nothing to give it. visitor, not an owning out-param — Upstream fills a heap-owning out-parameter the caller must `fini`. We pass a VISITOR callback invoked once per entry, with borrowed strings valid for the call. No allocation, bounded work, and nothing for a caller to leak — which is what makes the graph family available on a target with no allocator at all. |
rmw_ret_t rmw_init( const rmw_init_options_t * options, rmw_context_t * context ) |
rmw_ret_t (*create_session)( const char *locator, uint8_t mode, uint32_t domain_id, const char *node_name, const rmw_session_options_t *options, rmw_session_t *out ) |
● re-shaped
renamed — the slot is create_session.build-time POD, no init/copy/fini — Upstream's options own heap and carry an allocator, which is why they need the init/copy/fini trio. Ours are a build-time POD passed by value. the SESSION is the seam — As `rmw_node_t`: upstream's context is the process-wide init state, and ours is the session the slot is being called on. |
rmw_ret_t rmw_init_options_copy( const rmw_init_options_t * src, rmw_init_options_t * dst ) |
— |
◆ re-mapped · build time
build time: `copy` is `=`
|
rmw_ret_t rmw_init_options_fini(rmw_init_options_t * init_options) |
— |
◆ re-mapped · build time
build time: `fini` is nothing
|
rmw_ret_t rmw_init_options_init( rmw_init_options_t * init_options, rcutils_allocator_t allocator ) |
— |
◆ re-mapped · build time
build time: rmw_init_options_t is a POD
|
rmw_ret_t rmw_init_publisher_allocation( const rosidl_message_type_support_t * type_support, const rosidl_runtime_c__Sequence__bound * message_bounds, rmw_publisher_allocation_t * allocation ) |
— |
✕ not-supported · by decision
upstream's first two parameters are a `rosidl_message_type_support_t *` and a `rosidl_runtime_c__Sequence__bound *`. The sequence bound is declined ABI-wide (it is a rosidl runtime type), and the third parameter is an `rmw_publisher_allocation_t`, whose body is `{const char *implementation_identifier; void *data;}` — an opaque handle whose contents only the implementation that made it can read. Nothing crosses this seam usefully. THREE earlier reasons here were wrong, and the corrections are the record worth keeping: 'pools are baked' (false for four backends of five — issue 0777, now RESOLVED with the finding that every deviation reason built on that clause was false); 'upstream pre-sizes an rcutils_allocator_t the caller owns' (false — verified against Humble's rmw/types.h); and, until phase-406, a tail deferring the capability question to 0777 as though it were still open. It is not: 0777 closed, and the pre-sizing CAPABILITY for cyclonedds is a separate question that needs its own issue if anyone wants it, not a citation here. |
rmw_ret_t rmw_init_subscription_allocation( const rosidl_message_type_support_t * type_support, const rosidl_runtime_c__Sequence__bound * message_bounds, rmw_subscription_allocation_t * allocation ) |
— |
✕ not-supported · by decision
as above |
const rmw_guard_condition_t * rmw_node_get_graph_guard_condition(const rmw_node_t * node) |
rmw_ret_t (*node_get_graph_guard_condition)( rmw_session_t *session, rmw_event_callback_t callback, const void *user_data ) |
✕ not-supported · by decision
inert — declared, written and read by nothing.the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_ret_t rmw_publish( const rmw_publisher_t * publisher, const void * ros_message, rmw_publisher_allocation_t * allocation ) |
rmw_ret_t (*publish)( const rmw_publisher_t *publisher, rmw_byte_span_t payload ) |
◆ re-mapped · 2 upstream → 1 slot
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call.pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_publish_loaned_message( const rmw_publisher_t * publisher, void * ros_message, rmw_publisher_allocation_t * allocation ) |
rmw_ret_t (*publish_loaned_message)( const rmw_publisher_t *publisher, rmw_loan_token_t *token, size_t actual_len ) |
● re-shaped
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call.pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_publish_serialized_message( const rmw_publisher_t * publisher, const rmw_serialized_message_t * serialized_message, rmw_publisher_allocation_t * allocation ) |
rmw_ret_t (*publish)( const rmw_publisher_t *publisher, rmw_byte_span_t payload ) |
◆ re-mapped · 2 upstream → 1 slot
renamed — the slot is publish.no allocator at this seam — `rmw_serialized_message_t` is an `rcutils_uint8_array_t`, which carries an allocator. The byte range and its length cross instead. pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_publisher_assert_liveliness(const rmw_publisher_t * publisher) |
rmw_ret_t (*publisher_assert_liveliness)(const rmw_publisher_t *publisher) |
● same
|
rmw_ret_t rmw_publisher_count_matched_subscriptions( const rmw_publisher_t * publisher, size_t * subscription_count ) |
rmw_ret_t (*publisher_count_matched_subscriptions)( const rmw_publisher_t *publisher, size_t *subscription_count ) |
● same
|
rmw_ret_t rmw_publisher_event_init( rmw_event_t * rmw_event, const rmw_publisher_t * publisher, rmw_event_type_t event_type ) |
rmw_ret_t (*publisher_event_init)( const rmw_publisher_t *publisher, rmw_event_type_t kind, uint32_t deadline_ms, rmw_status_event_callback_t cb, void *user_context ) |
◆ re-mapped · 2 upstream → 1 slot
events are a slot family, not an event object — Upstream models an event as an object you take from. Ours are per-kind slots on the session (`take_event` and the event mask in `rmw_event.h`), so the object that would carry the kind is the slot itself. |
rmw_ret_t rmw_publisher_get_actual_qos( const rmw_publisher_t * publisher, rmw_qos_profile_t * qos ) |
rmw_ret_t (*publisher_get_actual_qos)( const rmw_publisher_t *publisher, rmw_qos_profile_t *qos ) |
● same
|
rmw_ret_t rmw_publisher_get_network_flow_endpoints( const rmw_publisher_t * publisher, rcutils_allocator_t * allocator, rmw_network_flow_endpoint_array_t * network_flow_endpoint_array ) |
— |
✕ not-supported · by decision
DECLINED, phase-407: reporting the transport's 5-tuples is diagnostics, and the value is in a multi-homed deployment with dynamic discovery — the case least like this ABI's target, where one interface and a static config mean the answer is already in the file you wrote. Landed in phase-376 W5 for shape parity, never filled, never read (issue 0956). Cheap to revisit for Cyclone alone if `ros2` tooling ever needs to introspect a nano-ros node: it is read-only and has no wire effect. |
rmw_ret_t rmw_publisher_wait_for_all_acked( const rmw_publisher_t * publisher, rmw_time_t wait_timeout ) |
rmw_ret_t (*publisher_wait_for_all_acked)( const rmw_publisher_t *publisher, uint32_t timeout_ms ) |
✕ not-supported · by decision
inert — declared, written and read by nothing.milliseconds, not a struct — `rmw_time_t` is a `{sec, nsec}` pair. This ABI carries `uint32_t` milliseconds throughout — the resolution every supported platform clock actually offers, and the unit the deadline arithmetic already uses. |
rmw_ret_t rmw_qos_profile_check_compatible( const rmw_qos_profile_t publisher_profile, const rmw_qos_profile_t subscription_profile, rmw_qos_compatibility_type_t * compatibility, char * reason, size_t reason_size ) |
rmw_ret_t rmw_qos_profile_check_compatible( rmw_qos_profile_t publisher_profile, rmw_qos_profile_t subscription_profile, rmw_qos_compatibility_type_t *compatibility, char *reason, size_t reason_size ) |
● re-shaped
by pointer, not by value — Upstream passes two `rmw_qos_profile_t` BY VALUE. The struct is ~80 bytes, and a by-value copy of it crosses the seam twice on every compatibility check — on a target where the call may run from a 512-byte task stack. Ours takes `const rmw_qos_profile_t *`, which is the same information without the copies. |
rmw_ret_t rmw_return_loaned_message_from_publisher( const rmw_publisher_t * publisher, void * loaned_message ) |
rmw_ret_t (*return_loaned_message_from_publisher)( const rmw_publisher_t *publisher, rmw_loan_token_t *token ) |
● re-shaped
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_return_loaned_message_from_subscription( const rmw_subscription_t * subscription, void * loaned_message ) |
rmw_ret_t (*return_loaned_message_from_subscription)( const rmw_subscription_t *subscription, rmw_loan_token_t *token ) |
● re-shaped
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_send_request( const rmw_client_t * client, const void * ros_request, int64_t * sequence_id ) |
rmw_ret_t (*send_request)( const rmw_client_t *client, rmw_byte_span_t request, int64_t *sequence_id ) |
● re-shaped
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_send_response( const rmw_service_t * service, rmw_request_id_t * request_header, void * ros_response ) |
rmw_ret_t (*send_response)( const rmw_service_t *server, int64_t seq, rmw_byte_span_t response ) |
● re-shaped
visitor, not an owning out-param — As `rmw_service_info_t` — the sequence number and writer GUID travel in the borrowed view rather than a separate out-param.bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_serialize( const void * ros_message, const rosidl_message_type_support_t * type_support, rmw_serialized_message_t * serialized_message ) |
— |
◆ re-mapped · 1 → 2
nros-serdes: Serialize
codegen packs
|
rmw_ret_t rmw_service_request_subscription_get_actual_qos( const rmw_service_t * service, rmw_qos_profile_t * qos ) |
rmw_ret_t (*service_request_subscription_get_actual_qos)( const rmw_service_t *service, rmw_qos_profile_t *qos ) |
● same
|
rmw_ret_t rmw_service_response_publisher_get_actual_qos( const rmw_service_t * service, rmw_qos_profile_t * qos ) |
rmw_ret_t (*service_response_publisher_get_actual_qos)( const rmw_service_t *service, rmw_qos_profile_t *qos ) |
● same
|
rmw_ret_t rmw_service_server_is_available( const rmw_node_t * node, const rmw_client_t * client, bool * is_available ) |
rmw_ret_t (*service_server_is_available)( const rmw_client_t *client, bool *out_available ) |
● re-shaped
the SESSION is the seam — Upstream passes the node into almost every call. Our vtable is scoped to a session handle that already knows its node, so re-passing it would ask the caller to carry an identity the callee holds — the same argument that decided `handle-owns-node` in the C API, one layer down. |
rmw_ret_t rmw_service_set_on_new_request_callback( rmw_service_t * service, rmw_event_callback_t callback, const void * user_data ) |
— |
✕ not-supported · by decision
DECLINED, phase-407: readiness belongs to the EXECUTOR here, not to the backend. `set_wake_callback` says "something on this session moved" and `spin_once` then walks its entry table calling `has_data` per entity — one vtable call each, on images whose entity count is single digits. Upstream's per-entity callback carries `number_of_events`, so honouring it means every backend tracking per-entity pending DEPTH: new state on a target where static RAM is already the pressure (issue 0827 measures a talker reserving 275 KB), bought for a scan nobody has measured as costly and a consumer that does not exist. If rclcpp `EventsExecutor` compatibility is ever wanted, that is a phase with a stated goal, not three slots kept warm (issue 0960). |
rmw_ret_t rmw_set_log_severity(rmw_log_severity_t severity) |
rmw_ret_t (*set_log_severity)(rmw_log_severity_t severity) |
● same
|
rmw_ret_t rmw_shutdown(rmw_context_t * context) |
rmw_ret_t (*destroy_session)(rmw_session_t *session) |
◆ re-mapped · 2 upstream → 1 slot
renamed — the slot is destroy_session.the SESSION is the seam — As `rmw_node_t`: upstream's context is the process-wide init state, and ours is the session the slot is being called on. |
rmw_ret_t rmw_subscription_count_matched_publishers( const rmw_subscription_t * subscription, size_t * publisher_count ) |
rmw_ret_t (*subscription_count_matched_publishers)( const rmw_subscription_t *subscription, size_t *publisher_count ) |
● same
|
rmw_ret_t rmw_subscription_event_init( rmw_event_t * rmw_event, const rmw_subscription_t * subscription, rmw_event_type_t event_type ) |
rmw_ret_t (*subscription_event_init)( const rmw_subscription_t *subscription, rmw_event_type_t kind, uint32_t deadline_ms, rmw_status_event_callback_t cb, void *user_context ) |
● re-shaped
events are a slot family, not an event object — Upstream models an event as an object you take from. Ours are per-kind slots on the session (`take_event` and the event mask in `rmw_event.h`), so the object that would carry the kind is the slot itself. |
rmw_ret_t rmw_subscription_get_actual_qos( const rmw_subscription_t * subscription, rmw_qos_profile_t * qos ) |
rmw_ret_t (*subscription_get_actual_qos)( const rmw_subscription_t *subscription, rmw_qos_profile_t *qos ) |
● same
|
rmw_ret_t rmw_subscription_get_content_filter( const rmw_subscription_t * subscription, rcutils_allocator_t * allocator, rmw_subscription_content_filter_options_t * options ) |
— |
✕ not-supported · by decision
as rmw_subscription_set_content_filter — declined with it, since a getter for a filter nothing can set is not a capability (issue 0956). |
rmw_ret_t rmw_subscription_get_network_flow_endpoints( const rmw_subscription_t * subscription, rcutils_allocator_t * allocator, rmw_network_flow_endpoint_array_t * network_flow_endpoint_array ) |
— |
✕ not-supported · by decision
as rmw_publisher_get_network_flow_endpoints (issue 0956). |
rmw_ret_t rmw_subscription_set_content_filter( rmw_subscription_t * subscription, const rmw_subscription_content_filter_options_t * options ) |
— |
✕ not-supported · by decision
DECLINED, phase-407: network-side content filtering needs a DDS feature our pinned Cyclone does not have. 0.10.5 exposes `dds_set_topic_filter{,_and_arg,_extended}` — a LOCAL sample callback applied after the message arrives — and not `dds_create_topic_filtered`, which is the one that propagates the predicate to the writer. Shipping the local filter under this name would promise saved BANDWIDTH and deliver saved CPU, which is the class of deviation reason W4 spent its time undoing. The slot existed from phase-376 W5 to 2026-08-31 and no backend ever filled it (issue 0956). |
rmw_ret_t rmw_subscription_set_on_new_message_callback( rmw_subscription_t * subscription, rmw_event_callback_t callback, const void * user_data ) |
— |
✕ not-supported · by decision
as rmw_service_set_on_new_request_callback — and note the header had argued, correctly, that `set_wake_callback` does NOT cover this trio: it is session-scoped and serves subscriptions, services and clients identically. That is why these are DECLINED rather than re-mapped: nothing else answers them, and we have decided not to (issue 0960). |
rmw_ret_t rmw_take( const rmw_subscription_t * subscription, void * ros_message, bool * taken, rmw_subscription_allocation_t * allocation ) |
rmw_ret_t (*take)( const rmw_subscription_t *subscription, rmw_mut_byte_span_t *out, bool *taken ) |
◆ re-mapped · 2 upstream → 1 slot
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call.pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_take_event( const rmw_event_t * event_handle, void * event_info, bool * taken ) |
rmw_ret_t (*subscription_take_event)( const rmw_subscription_t *subscription, rmw_event_type_t kind, rmw_event_payload_t *out, bool *taken ) |
● re-shaped
renamed — the slot is subscription_take_event.events are a slot family, not an event object — Upstream models an event as an object you take from. Ours are per-kind slots on the session (`take_event` and the event mask in `rmw_event.h`), so the object that would carry the kind is the slot itself. bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_take_loaned_message( const rmw_subscription_t * subscription, void * * loaned_message, bool * taken, rmw_subscription_allocation_t * allocation ) |
rmw_ret_t (*take_loaned_message)( const rmw_subscription_t *subscription, rmw_byte_span_t *out_view, rmw_loan_token_t **out_token, bool *taken ) |
● re-shaped
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call.pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_take_loaned_message_with_info( const rmw_subscription_t * subscription, void * * loaned_message, bool * taken, rmw_message_info_t * message_info, rmw_subscription_allocation_t * allocation ) |
rmw_ret_t (*take_loaned_message_with_info)( const rmw_subscription_t *subscription, rmw_byte_span_t *out_view, rmw_loan_token_t **out_token, bool *taken, rmw_message_info_t *message_info ) |
◆ re-mapped · 1 → 2
take_loaned_message
the attachment on the message already taken
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_take_request( const rmw_service_t * service, rmw_service_info_t * request_header, void * ros_request, bool * taken ) |
rmw_ret_t (*take_request)( const rmw_service_t *server, rmw_mut_byte_span_t *request, int64_t *seq_out, bool *taken ) |
● re-shaped
visitor, not an owning out-param — The request/response metadata upstream writes into a caller-owned struct arrives here through the same borrowed-view discipline as the graph family.bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_take_response( const rmw_client_t * client, rmw_service_info_t * request_header, void * ros_response, bool * taken ) |
rmw_ret_t (*take_response)( const rmw_client_t *client, rmw_mut_byte_span_t *reply, int64_t *seq_out, bool *taken ) |
● re-shaped
visitor, not an owning out-param — The request/response metadata upstream writes into a caller-owned struct arrives here through the same borrowed-view discipline as the graph family.bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. |
rmw_ret_t rmw_take_sequence( const rmw_subscription_t * subscription, size_t count, rmw_message_sequence_t * message_sequence, rmw_message_info_sequence_t * message_info_sequence, size_t * taken, rmw_subscription_allocation_t * allocation ) |
rmw_ret_t (*take_sequence)( const rmw_subscription_t *subscription, uint8_t *buf, size_t per_msg_cap, size_t max_msgs, size_t *out_lens, size_t *taken ) |
● re-shaped
visitor, not an owning out-param — `rmw_take_sequence`'s message and info sequences are caller-owned arrays sized by an allocator. The bounded form is a repeated take, which is what the executor does.pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_take_serialized_message( const rmw_subscription_t * subscription, rmw_serialized_message_t * serialized_message, bool * taken, rmw_subscription_allocation_t * allocation ) |
rmw_ret_t (*take)( const rmw_subscription_t *subscription, rmw_mut_byte_span_t *out, bool *taken ) |
◆ re-mapped · 2 upstream → 1 slot
renamed — the slot is take.no allocator at this seam — `rmw_serialized_message_t` is an `rcutils_uint8_array_t`, which carries an allocator. The byte range and its length cross instead. pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_take_serialized_message_with_info( const rmw_subscription_t * subscription, rmw_serialized_message_t * serialized_message, bool * taken, rmw_message_info_t * message_info, rmw_subscription_allocation_t * allocation ) |
rmw_ret_t (*take_with_info)( const rmw_subscription_t *subscription, rmw_mut_byte_span_t *message, bool *taken, rmw_message_info_t *message_info ) |
◆ re-mapped · 2 upstream → 1 slot
take
the attachment on the message already taken
take_with_info.inert — declared, written and read by nothing. no allocator at this seam — `rmw_serialized_message_t` is an `rcutils_uint8_array_t`, which carries an allocator. The byte range and its length cross instead. pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_take_with_info( const rmw_subscription_t * subscription, void * ros_message, bool * taken, rmw_message_info_t * message_info, rmw_subscription_allocation_t * allocation ) |
rmw_ret_t (*take_with_info)( const rmw_subscription_t *subscription, rmw_mut_byte_span_t *message, bool *taken, rmw_message_info_t *message_info ) |
◆ re-mapped · 2 upstream → 1 slot
take
the attachment on the message already taken
bytes, not an untyped pointer — Upstream's `void *` is the message, interpreted through the typesupport pointer beside it. With types resolved at build time there is nothing to interpret it WITH, so the seam carries an explicit byte range and its length — which is also what makes the buffer's capacity checkable at the call. pre-allocation declined ABI-wide — `rmw_publisher_allocation_t` / `rmw_subscription_allocation_t` are upstream's pre-sizing handles. Declined at this seam because their first parameters are a typesupport pointer and a sequence bound, both declined ABI-wide. The CAPABILITY question — can a backend pre-size — is live for cyclonedds alone, and issue 0777 is CLOSED, so it is no longer a place to route it: 0777 resolved with the finding that every deviation reason built on "pools are baked" was false, which settles the reason and not the capability. Anyone who wants pre-sizing for cyclonedds needs a NEW issue; a citation to a resolved one is not a reason. |
rmw_ret_t rmw_trigger_guard_condition(const rmw_guard_condition_t * guard_condition) |
— |
◆ re-mapped · platform
platform: the wake primitive, via GuardCondition::trigger
|
rmw_ret_t rmw_wait( rmw_subscriptions_t * subscriptions, rmw_guard_conditions_t * guard_conditions, rmw_services_t * services, rmw_clients_t * clients, rmw_events_t * events, rmw_wait_set_t * wait_set, const rmw_time_t * wait_timeout ) |
— |
◆ re-mapped · 1 → 5
has_data
has_request
drive_io
set_wake_callback
next_deadline_ms
|
Reproduce
| command | asks |
|---|---|
just check rmw-api-parity | is every contract symbol classified |
just check rmw-abi-shape | does the vtable mirror it — name, args, return |
just check rmw-slot-producers | which slots anything actually writes or reads |
python3 scripts/rmw-api-parity.py --contract | re-derive the contract from an installed impl |