nros rmw-cffi
C vtable for plugging a third-party RMW backend into nros
Loading...
Searching...
No Matches
Data Structures | Macros | Typedefs | Enumerations | Functions
rmw_vtable.h File Reference

C function table for plugging third-party RMW backends into nros. More...

#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "nros/rmw_ret.h"
#include "nros/rmw_entity.h"
#include "nros/rmw_event.h"
Include dependency graph for rmw_vtable.h:

Go to the source code of this file.

Data Structures

struct  nros_rmw_vtable_t
 
struct  rmw_names_and_types_visitor_t
 
struct  rmw_node_visitor_t
 
struct  rmw_topic_endpoint_info_visitor_t
 

Macros

#define NROS_RMW_REGISTER_BACKEND(REGISTER_FN)
 
#define NROS_RMW_VISITOR_DEFINED   1
 

Typedefs

typedef struct rmw_loan_token_t rmw_loan_token_t
 
typedef bool(* rmw_names_and_types_visit_fn) (void *ctx, const char *name, const char *const *types, size_t types_count)
 
typedef bool(* rmw_node_visit_fn) (void *ctx, const char *node_name, const char *node_namespace, const char *enclave)
 
typedef bool(* rmw_topic_endpoint_info_visit_fn) (void *ctx, const rmw_topic_endpoint_info_t *info)
 

Enumerations

enum  nros_rmw_session_mode_t { NROS_RMW_SESSION_MODE_CLIENT = 0 , NROS_RMW_SESSION_MODE_PEER = 1 }
 

Functions

const nros_rmw_vtable_tnros_rmw_cffi_lookup (const char *name)
 
rmw_ret_t nros_rmw_cffi_register (const nros_rmw_vtable_t *vtable)
 
rmw_ret_t nros_rmw_cffi_register_named (const char *name, const nros_rmw_vtable_t *vtable)
 
size_t nros_rmw_cffi_registered_names (const char **buf, size_t cap)
 

Detailed Description

C function table for plugging third-party RMW backends into nros.

Implement the functions in nros_rmw_vtable_t and call nros_rmw_cffi_register() before creating any nros sessions.

Storage ownership. The runtime owns the entity-struct storage (rmw_session_t, rmw_publisher_t, rmw_subscription_t, rmw_service_t, rmw_client_t). Each create_* call receives a runtime-allocated, zero-initialised struct via the out pointer; the backend writes its backend_data into it. (It does NOT write can_loan_messages: the runtime derives that from the loan slots — issue 0814.) The runtime fills the metadata fields (topic_name, type_name, qos) before calling create_*; the backend reads them through the same struct.

destroy_* releases the backend's backend_data only. The struct shell stays valid until the runtime drops its owner.

Return-value conventions. One rule, no exceptions:

This paragraph described the PRE-W3.d ABI until 2026-08-24 and was wrong on five counts at once: it promised NEGATIVE error constants (step B adopted upstream's positive numbering), it said try_recv_* returned a byte count and has_data returned 1-or-0 (step A moved both to out-parameters), it said destroy_* returned void (W5 gave all six a status), and it named five slots that no longer exist — publish_raw, send_reply, try_recv_raw, try_recv_request, try_recv_reply_raw, all renamed by W3.b. Prose in the SSoT header is not covered by any of the shape gates, which compare DECLARATIONS against the header and never read what the header says about itself.

Macro Definition Documentation

◆ NROS_RMW_REGISTER_BACKEND

#define NROS_RMW_REGISTER_BACKEND (   REGISTER_FN)

Phase 249 P4b.2 — convenience macro for static-library backends. Place in exactly one TU per backend to self-register the backend on library load. REGISTER_FN is a no-arg function that calls nros_rmw_cffi_register_named for the backend.

Example: static void zenoh_register(void) { nros_rmw_cffi_register_named("zenoh", &VTABLE); } NROS_RMW_REGISTER_BACKEND(zenoh_register)

HOSTED (Rust + C/C++ on a hosted loader): the macro expands to an .init_array constructor (__attribute__((constructor))) that the loader fires before main() — hence before nros_support_init / nros::init — calling REGISTER_FN. The --whole-archive link keeps this object's .init_array slot. nros_rmw_cffi_register_named is idempotent (same-name overwrite), so re-registration is harmless. This consolidates the former linkme section walk onto the ctor (RFC-0042 §D3.3; the linkme distributed slice / section walker is deleted).

EMBEDDED (bare-metal / RTOS: Zephyr, NuttX, esp-idf, VxWorks): the loader does not run .init_array constructors on the startup path, so the macro expands to nothing. Registration is wired explicitly by the board / typed carrier via an explicit nros_rmw_<x>_register() call (phase-249 P1). For C/C++-via-cmake the nano_ros_link_rmw strong stub is the primary registration trigger (P2b/P4a); this hosted ctor is belt-and-suspenders.

Gating mirrors the cyclonedds vtable.cpp constructor (off the RTOS targets) and requires GCC/Clang constructor-attribute support.

◆ NROS_RMW_VISITOR_DEFINED

#define NROS_RMW_VISITOR_DEFINED   1

A visitor: the function and the context it needs — phase-406 W2.

Upstream fills a heap-owning out-parameter the caller must fini (rmw_names_and_types_t, rcutils_string_array_t). We visit instead: one call per entry, strings borrowed for the call, no allocation and nothing to leak — which is what makes the graph family available on a target with no allocator at all.

The pair was TWO arguments on twelve slots, and they are meaningless apart: a visit without its ctx cannot find its state, and a ctx without its visit goes nowhere. Grouping them is the same move as the type support, and it makes "pass one, forget the other" unrepresentable rather than merely discouraged.

fn == NULL is INVALID_ARGUMENT, never a silent no-op: a graph query that visits nothing is indistinguishable from an empty graph, which is the exact confusion issue 0903 cost days to.

Passed BY VALUE — two words, and nothing is written back through it.

Typedef Documentation

◆ rmw_loan_token_t

An opaque per-loan handle — phase-406 W3.

Was a bare void *. The type says nothing a reader can use, and nothing stops a caller returning a publisher's loan token to a subscription, or a token from one backend to another: both compile, and both are undefined behaviour discovered at run time on a target with no allocator to notice.

An INCOMPLETE type costs nothing at the ABI — it is still a pointer — and makes those two mistakes a compile error. The backend defines the struct privately; nobody outside it may dereference one, which was already the contract and is now enforced rather than documented.

Issue 0781 asked whether the subscription-side loan slots earn their place given nothing implements them. The answer taken here is KEEP AND TYPE: a slot nobody can use safely is worse than one nobody uses.

◆ rmw_names_and_types_visit_fn

typedef bool(* rmw_names_and_types_visit_fn) (void *ctx, const char *name, const char *const *types, size_t types_count)

Visit one name and the types on it. types_count may legitimately be 0 on a partially discovered graph — reporting the name without a type beats dropping it. Return false to stop.

◆ rmw_node_visit_fn

typedef bool(* rmw_node_visit_fn) (void *ctx, const char *node_name, const char *node_namespace, const char *enclave)

Visit one node. enclave is NULL where the backend does not track one — which is what lets a single slot answer both rmw_get_node_names and rmw_get_node_names_with_enclaves. Return false to stop.

◆ rmw_topic_endpoint_info_visit_fn

typedef bool(* rmw_topic_endpoint_info_visit_fn) (void *ctx, const rmw_topic_endpoint_info_t *info)

Visit one discovered endpoint. Return false to stop.

Enumeration Type Documentation

◆ nros_rmw_session_mode_t

Session mode for create_session's mode parameter (issue 0331).

These values were previously an undocumented bare uint8_t with no legal- value list — the only slot in the vtable without one — encoded inline as 0u8 / 1u8 at the Rust boundary.

Divergence from rmw.h, recorded deliberately: Humble's rmw_init_options_t carries instance_id, implementation_identifier, domain_id, security_options, localhost_only, enclave, allocator and impl — and has NO session-mode concept. (This list said "domain_id, enclave, security_options and discovery_options" until 2026-08-24, which omitted five of the eight and named one, discovery_options, that is an IRON field and does not exist in the distro our recorded contract is taken from. What we do and do not carry from those eight is issue 0785.) This parameter is closest to zenoh's whatami, and a backend that has no such notion (cyclonedds, XRCE) is expected to IGNORE it rather than fail. Folding it into backend-private config behind the locator — so the agnostic vtable stops carrying a backend-shaped field — is the structural fix, and is not done here; see issue 0808. (This said "see issue 0331" until 2026-08-26. That issue documented this enum and stated in its own resolution that the structural half was NOT done, deferring it to issue 0330 part 3 — which resolved a different concern entirely. Both are closed, so the fold was tracked by nothing; 0808 is its home.)

Enumerator
NROS_RMW_SESSION_MODE_CLIENT 

Connect to a router/agent as a client. The default.

NROS_RMW_SESSION_MODE_PEER 

Peer-to-peer, no router. Backends without a peer mode ignore this.

Function Documentation

◆ nros_rmw_cffi_lookup()

const nros_rmw_vtable_t * nros_rmw_cffi_lookup ( const char *  name)

Look up a backend's vtable by name. Returns NULL if no backend is registered under name. The returned pointer is valid for the program's lifetime.

◆ nros_rmw_cffi_register()

rmw_ret_t nros_rmw_cffi_register ( const nros_rmw_vtable_t vtable)

Register a custom RMW backend under the implicit name "default". Legacy single-arg form retained for source compatibility with backend ctors authored before the named registry (Phase 104.B.2).

Deprecated (Phase 128.B.5): every in-tree backend now calls nros_rmw_cffi_register_named with its canonical name. The unnamed shim will be removed in a follow-up phase. Returns NROS_RMW_RET_OK.

◆ nros_rmw_cffi_register_named()

rmw_ret_t nros_rmw_cffi_register_named ( const char *  name,
const nros_rmw_vtable_t vtable 
)

Phase 104.B.2 — register a backend under a stable name. Multiple backends can coexist (bridge nodes); consumers select via nros_rmw_cffi_lookup or the higher-level Executor::node_builder(...).rmw(...) path.

Names: UTF-8, NUL-terminated, ≤ 31 bytes (excluding NUL). Reserved: "zenoh", "dds", "xrce", "cyclonedds", future "uorb". "default" is the implicit name used by nros_rmw_cffi_register.

Duplicate registration of the same name overwrites the previous vtable (idempotent for ctor-fires-twice).

Returns:

  • NROS_RMW_RET_OK on success.
  • NROS_RMW_RET_INVALID_ARGUMENT if name or vtable is NULL, the name is empty, or exceeds 31 bytes.
  • NROS_RMW_RET_ERROR if the registry is full (NROS_RMW_MAX_BACKENDS reached).

◆ nros_rmw_cffi_registered_names()

size_t nros_rmw_cffi_registered_names ( const char **  buf,
size_t  cap 
)

Diagnostic helper — fills buf with pointers to up to cap registered backend names. Returns the total number of registered backends (may exceed cap; caller can re-query with a larger buffer). Pointer-valid for the program's lifetime. Pass buf=NULL, cap=0 to query the count only.