C API
The C user-facing surface lives in packages/api/nros-c/include/, at
upstream’s paths (rclc/, rcl/, rcl_action/, …). rclc/detail/ is internal.
Generated by Doxygen; always reflects the current main branch.
API reference
C API Doxygen — start here.
The umbrella header rclc/rclc.h pulls in every
public surface a user application needs.
Where to start
rclc/rclc.h— convenience umbrella includerclc/init.h—rclc_support_init,rclc_support_finirclc/node.h—rclc_node_init_default,rcl_node_finirclc/executor.h— spin loop, executor lifecyclerclc/publisher.h/subscription.h— pub/subrclc/service.h/client.h— servicesrclc/action.h— actionsrclc_parameter/rclc_parameter.h— parameter servicesrclc_lifecycle/rclc_lifecycle.h— REP-2002 lifecycle states
Two-layer API. Every entity exposes two parallel C entry-point sets:
rclc_*_init_default(Layer 1, caller polls) andrclc_executor_add_*(Layer 2, executor callback). Layer 1 grew an_init_polling+take_*_raw/send_*_rawfamily for inline-storage callers (no executor arena). Layer 2 pairs the samerclc_*_init_defaultwith the executor-add call, which is where the callback is supplied (rclc’s binding site). See Two-Layer API for the verb discipline and per-layer trade-offs.
Event-driven wake callbacks. Each L1 polling entity supports
nros_*_set_wake_callback(entity, &state, cb, ctx)for event- driven RTOS / embassy callers. State is a caller-ownedrclc_wake_state_tPOD; the backend firescb(ctx)on rx / reply / request arrival. Per-channel for actions (set_{goal,cancel,get_result}_wake_callbackon the server,set_{goal_response,cancel_response,result,feedback}_wake_callbackon the client). See the Doxygen for signatures.
Async action client
<rclc/action.h> exposes two parallel entry-point families for sending
goals, requesting results, and cancelling:
- Async (non-blocking):
rclc_action_send_goal_async(),rclc_action_get_result_async(),rclc_action_cancel_goal(),rclc_action_try_recv_feedback(). Return immediately. Replies are delivered through the callbacks registered withrclc_action_client_set_goal_response_callback(),rclc_action_client_set_feedback_callback(), andrclc_action_client_set_result_callback(). The callbacks fire fromrclc_executor_spin_some()— your spin loop is the only place callbacks run. - Blocking convenience:
rclc_action_send_goal()andrclc_action_get_result(). These call the async variant and then driverclc_executor_spin_some()internally on a wall-clock budget until the reply lands (or 15 s / 30 s timeout). They take therclc_executor_t*explicitly because the action client stores its handle as an opaque slot inside the executor (registered viarclc_executor_attach_action_client()). The same applies torclc_action_client_wait_for_action_server()andrclc_action_client_action_server_is_ready(). Calling any of them from inside a dispatch callback returnsRCLC_RET_REENTRANT. - Teardown:
rcl_action_server_fini()/rcl_action_client_fini()reset only the struct. To take an action off an executor that keeps running, callrclc_executor_remove_action_server()/rclc_executor_remove_action_client()first: they drop the arena entry (the action leaves the graph), free its callback slot and arena bytes for reuse, and bringrclc_executor_get_handle_count()back down. A program that tears the executor down withrclc_executor_fini()needs neither — that drops every entry (issue 1609). - The same holds for every other entity the executor registers (issue
1631):
rclc_executor_remove_subscription(),rclc_executor_remove_timer(),rclc_executor_remove_service()andrclc_executor_remove_client()— rclc’srclc_executor_remove_*shape — each drop the arena entry (receive ring included), free the slot and bringhandle_countback down. Call one before the entity’sfiniwhen the executor keeps running; a timer or client that isfini’d without it is still dispatched into. A removed client returns to INITIALIZED and can be re-added. All returnRCLC_RET_NOT_FOUNDfor an entity not registered on that executor andRCLC_RET_REENTRANTfrom inside a callback. A subscription whose callback also receives the sample’s attachment is registered withrclc_executor_add_subscription_with_info()into arcl_subscription_tand removed the same way (issue 1668); the older direct-argrclc_executor_add_subscription_raw_with_info()takes no subscription object, so its entry lives untilrclc_executor_fini().
The equivalent service client helper rclc_client_call() does not
take an explicit executor — the client stashes the executor pointer
during rclc_executor_attach_client() and recovers it internally —
but it follows the same async-then-spin contract.
Canonical pattern: declare the executor, register the client, then
either call the blocking helper or drive rclc_executor_spin_some()
yourself between async calls until your callback flags the result. See
the example layouts in examples/linux/c/action-client/ and
examples/mps2-an385-freertos/c/action-client/.
Phase 122.3 added a separate L1 polling family
(rclc_polling_action_client_init + the _raw send/recv siblings)
that stores ActionClientCore inline in the separate type
rclc_polling_action_client_t (phase-487 W4) and skips the executor entirely;
see the Doxygen for the full L1 verb list.
CMake integration
Out-of-tree projects link the NanoRos::NanoRos CMake target and
generate C bindings with nano_ros_generate_interfaces(<pkg> LANGUAGE C)
(the function defaults to LANGUAGE CPP; the full which-spelling table
is in Message Generation) —
covered in
docs/reference/c-api-cmake.md.
Generating locally
doxygen packages/api/nros-c/Doxyfile
xdg-open target/doc/c-api/html/index.html
Internals (porting only)
The rmw-cffi and platform-cffi Doxygen sites are not part of
the user API — they document the C vtables for porters writing a custom RMW
backend or platform port. See RMW API and
Platform API.