Prerequisites
- A C++14 compiler (GCC 6+, Clang 5+, or
arm-none-eabi-g++)
- CMake >= 3.15
- Rust nightly toolchain (needed to build
libnros_cpp.a)
- zenohd router (
just build-zenohd in the nano-ros source tree, or install a matching version from zenoh releases)
1. Create a CMake Project
mkdir my-cpp-talker && cd my-cpp-talker
mkdir src
CMakeLists.txt (Phase 140 — add_subdirectory is the only consumption shape):
cmake_minimum_required(VERSION 3.22)
project(my_cpp_talker LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(NANO_ROS_PLATFORM posix)
set(NANO_ROS_RMW zenoh)
add_subdirectory(/path/to/nano-ros nano_ros)
# Generate C++ message bindings from .msg files
nano_ros_generate_interfaces(std_msgs
"msg/Int32.msg"
LANGUAGE CPP
SKIP_INSTALL
)
add_executable(my_cpp_talker src/main.cpp)
target_link_libraries(my_cpp_talker PRIVATE
NanoRos::NanoRosCpp
std_msgs__nano_ros_cpp
)
nros_platform_link_app(my_cpp_talker)
NanoRos::NanoRosCpp is the C++ INTERFACE target wired by the root CMake; nano_ros_generate_interfaces(... LANGUAGE CPP) becomes available the moment add_subdirectory(nano-ros) runs.
3. Code Generation
Generated message types use ROS 2 standard namespaces: std_msgs::msg::Int32, geometry_msgs::msg::Point, etc. Each generated type provides:
TYPE_NAME — fully-qualified ROS 2 type name (string literal)
TYPE_HASH — RIHS01 type hash, or TypeHashNotSupported on Humble
ffi_publish() / ffi_take() — codegen-emitted serialise + FFI calls
You never hand-write CDR serialisation. The generator handles it.
4. Write a Talker
src/main.cpp:
#include <cstdio>
#include <csignal>
#include "std_msgs.hpp"
static volatile sig_atomic_t g_running = 1;
static void on_signal(int) { g_running = 0; }
struct Ctx {
int count;
};
static void on_tick(void* ctx_ptr) {
auto* ctx = static_cast<Ctx*>(ctx_ptr);
std_msgs::msg::Int32 msg;
msg.data = ++ctx->count;
if (ctx->pub->publish(msg).ok()) {
std::printf("Published %d\n", ctx->count);
}
}
int main() {
Ctx ctx{ &pub, 0 };
std::signal(SIGINT, on_signal);
}
return 0;
}
Result create_timer(Timer &out, uint64_t period_ms, nros_cpp_timer_callback_t callback, void *context=nullptr)
Definition node.hpp:437
Result create_publisher(Publisher< M > &out, const char *topic, const QoS &qos=QoS::default_profile())
Definition publisher.hpp:272
Result spin_once(int32_t timeout_ms=10)
Definition nros.hpp:62
Result shutdown()
Definition node.hpp:663
bool ok()
Check if the nros session is initialized.
Definition node.hpp:717
Result init(const char *locator=nullptr, uint8_t domain_id=0)
Definition node.hpp:568
Result create_node(Node &out, const char *name, const char *ns=nullptr)
Definition node.hpp:728
Umbrella header — pulls in every public C++ API surface.
#define NROS_TRY(expr)
Definition result.hpp:90
NROS_TRY(expr) short-circuits on the first error — equivalent to the Rust ? operator. Available without NROS_CPP_STD.
5. Build and Run
mkdir build && cd build
cmake ..
cmake --build .
# Terminal 1
zenohd --listen tcp/127.0.0.1:7447
# Terminal 2
./my_cpp_talker
6. Listener
Replace the publisher loop with a subscription:
[](const std_msgs::msg::Int32& msg) {
std::printf("Received: %d\n", msg.data);
}));
Result create_subscription(Subscription< M > &out, const char *topic, const QoS &qos=QoS::default_profile())
Definition subscription.hpp:460
Zephyr Integration
In prj.conf:
CONFIG_NROS=y
CONFIG_NROS_CPP_API=y
CONFIG_NROS_RMW_ZENOH=y # or CONFIG_NROS_RMW_XRCE=y
In your application's CMakeLists.txt:
nros_generate_interfaces(std_msgs
"msg/Int32.msg"
LANGUAGE CPP
)
target_sources(app PRIVATE src/main.cpp)
target_link_libraries(app PRIVATE std_msgs__cpp)
See examples/zephyr/cpp/ for full Zephyr templates.
Std-Mode Convenience
For host platforms where the STL is available:
#define NROS_CPP_STD
#include <chrono>
using namespace std::chrono_literals;
std::function<void(const std_msgs::msg::Int32&)>{...});
NROS_CPP_STD is opt-in; the freestanding surface remains the canonical API.