1) The ROS 2 code generator emits rosidl::Buffer<uint8_t> instead of std::vector<uint8_t> for uint8[] fields. 2) Buffer<T> uses the PIMPL idiom and delegates to BufferImplBase<T>; by default it creates a CpuBufferImpl<T> wrapping std::vector<T>. 3) The backend is set once at construction (there is no post-construction setter, to avoid race conditions). 4) On CPU the full std::vector API and implicit conversion are available; on non-CPU backends element access throws std::runtime_error and a host copy is obtained via to_vector(). 5) Vendor backends (BufferBackend) are discovered and loaded via pluginlib (BufferBackendRegistry). 6) During communication the serialization layer calls create_descriptor_with_endpoint(); when both peers share a backend a descriptor (<= 4096 B, e.g. a GPU-memory IPC handle) is transmitted and reconstructed via from_descriptor_with_endpoint() without copying the data; when the peer does not support the backend the descriptor is nullptr and it falls back to CPU serialization. Endpoints advertise supported backends and negotiate compatibility (on_creating_endpoint / on_discovering_endpoint).
In ROS 2, binary message fields (uint8[]) were represented as host-memory std::vector<T>; for large payloads (images, point clouds, tensors) this forced expensive CPU-accelerator copies and prevented native zero-copy transport of GPU memory between nodes without out-of-tree solutions.
Templated container replacing std::vector<T> for uint8[] fields. For the CPU backend it exposes the full std::vector API and implicit conversion to std::vector<T>&; the all-backend API includes size(), get_backend_type(), get_impl(), to_vector(). Value semantics: deep copy via clone().
Minimal base class (get_backend_type(), size(), to_cpu(), clone()). Concrete backends: CpuBufferImpl, CUDA, ROCm, etc.
Official
Implementation wrapping std::vector<T>; the default backend providing full backward compatibility.
Official
Abstract interface (rosidl_buffer_backend package). Creates and reads descriptors (create_descriptor_with_endpoint / from_descriptor_with_endpoint, <= 4096 B), handles endpoint discovery/negotiation hooks (on_creating_endpoint, on_discovering_endpoint) and stays RMW-agnostic.
Official
The rosidl_buffer_backend_registry package. Dynamically discovers and instantiates backends via pluginlib (create_backend_instance, get_backend_names).
operator[], at(), iterators and other std::vector operations throw std::runtime_error for non-CPU backends.
to_vector() is an escape hatch returning a CPU copy, which defeats the zero-copy benefit.
Every descriptor a backend produces must serialize to at most kMaxBufferDescriptorSize (4096 B).
When the peer does not support the backend, create_descriptor_with_endpoint() returns nullptr and transport falls back to standard CPU serialization (losing zero-copy).
ROS Discourse thread presenting a working prototype of zero-copy accelerator-memory transport for uint8[] fields, led by NVIDIA engineers.
PR #941 (merged 2026-03-31) introduces rosidl::Buffer<T>, BufferImplBase<T>, CpuBufferImpl<T> and BufferBackend as the core native-buffer types.
rosidl_buffer 5.1.4 (2026-04-09): the C++ generator path starts emitting rosidl::Buffer for uint8[] fields (PR #942).
Buffer backend type set at construction: 'cpu' (default), 'cuda', 'rocm', 'demo'. Set once, with no post-construction setter.
Template parameter of Buffer<T>. The C++ generator emits rosidl::Buffer<uint8_t> for uint8[] fields.
Upper bound on the serialized backend descriptor: 4096 bytes.
The CUDA backend allows holding and transporting GPU memory without host copies (zero-copy via descriptors), which is the feature's main goal.
The default CPU backend and pluggable architecture make the container itself independent of any specific hardware.