rcpputils 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rcpputils
版本:2.4.6(Humble),单包仓库,构建类型 ament_cmake,语言 C++14,许可证 Apache 2.0 + BSD-3-Clause(部分文件)。
rcpputils 是 ROS 2 C++ 工具库,提供跨平台文件系统、动态库加载、字符串处理、断言/异常、RAII scope guard、线程安全注解等。被 rclcpp、rclpy(C++ 扩展)、rosbag2、rviz2、class_loader 等广泛依赖。设计上 在 rcutils C API 之上提供 C++ 友好封装(异常、std::string、模板),同时补充 rcutils 未覆盖的 C++ 专属能力(std::chrono、type traits、std::filesystem 替代)。
1. 总体认识
1.1 核心职责
| 类别 | 模块 | 说明 |
|---|---|---|
| RAII / 资源 | scope_exit.hpp |
作用域退出时自动执行清理 |
| 动态库 | shared_library.hpp |
封装 rcutils_shared_library,dlopen/LoadLibrary |
| 文件系统 | filesystem_helper.hpp |
std::filesystem 替代(跨平台 path 操作) |
| 库查找 | find_library.hpp |
在 LD_LIBRARY_PATH 等环境变量中搜索 .so |
| 字符串 | split/join/find_and_replace.hpp |
分割、拼接、替换 |
| 环境 / 进程 | env.hpp、process.hpp |
环境变量、可执行文件名 |
| 断言 | asserts.hpp |
require/check/assert 三层次异常 |
| 时间 | time.hpp |
std::chrono → 纳秒安全转换 |
| 类型 traits | pointer_traits.hpp |
智能指针 type traits |
| 字节序 | endian.hpp |
C++20 std::endian 替代 |
| 线程注解 | thread_safety_annotations.hpp |
Clang Thread Safety Analysis 宏 |
| 符号导出 | visibility_control.hpp |
RCPPUTILS_PUBLIC 等 |
| 数学 | rcppmath/clamp.hpp、rolling_mean_accumulator.hpp |
钳制、滑动均值 |
1.2 在 ROS 2 栈中的位置
| 对比 | rcutils | rcpputils |
|---|---|---|
| 语言 | C | C++ |
| 日志/错误/分配器 | 核心实现 | 不重复,依赖 rcutils |
| 文件系统 | rcutils/filesystem.h(C) |
fs::path 等 C++ 风格 API |
| 动态库 | rcutils/shared_library.h |
SharedLibrary 类 + 异常 |
| 环境变量 | rcutils/env.h |
get_env_var() 返回 std::string |
| 适用层 | rcl、rmw、rosidl C 运行时 | rclcpp 及 C++ 工具/应用 |
分工原则:需要 C ABI 稳定性的放 rcutils;C++ 便利性与模板工具放 rcpputils。两者不应重复实现同一逻辑——rcpputils 的
.cpp实现大多委托 rcutils。
2. 仓库结构
1 | rcpputils/ |
2.1 编译 vs 头文件-only
| 类型 | 文件 | 说明 |
|---|---|---|
编译进 librcpputils.so |
5 个 .cpp |
需链接 -lrcpputils |
| 头文件-only | scope_exit、split、join、clamp、time、pointer_traits、endian、process 等 |
仅 #include 即可 |
| vendored 头 | tl_expected/expected.hpp |
C++17 std::expected 替代,lint 排除 |
CMake 导出:ament_export_libraries(rcpputils)、ament_export_dependencies(rcutils)。
3. 依赖关系
1 | rcpputils |
构建工具:ament_cmake、ament_cmake_ros、ament_cmake_gen_version_h。
C++ 标准:C++14(注释说明待 CXX20 可用后移除 tl_expected)。
4. 核心模块详解
4.1 scope_exit — RAII 清理
ROS 2 C++ 代码中最常用的 rcpputils 特性之一。
1 | template<typename CallableT> |
| API | 用途 |
|---|---|
make_scope_exit(fn) |
创建 scope guard |
cancel() |
资源已手动释放时取消自动清理 |
RCPPUTILS_SCOPE_EXIT(...) |
宏简写(rclcpp executor、rosbag2 测试广泛使用) |
典型场景:测试里确保进程/线程在退出时 join,或临时修改状态后恢复。
4.2 SharedLibrary — 动态库加载
C++ 封装 rcutils_shared_library_t:
1 | SharedLibrary::SharedLibrary(const std::string & library_path) |
| 方法 | 底层 |
|---|---|
| 构造 / 析构 | rcutils_load/unload_shared_library |
get_symbol() |
rcutils_get_symbol |
has_symbol() |
rcutils_has_symbol |
get_platform_library_name() |
rcutils_get_platform_library_name |
下游关键用法:rclcpp/typesupport_helpers.cpp 运行时加载 rosidl_typesupport_cpp 共享库以解析消息 typesupport:
1 | const std::string library_path = rcpputils::path_for_library( |
4.3 find_library — 库路径搜索
1 | std::string find_library_path(const std::string & library_name) |
| 函数 | 作用 |
|---|---|
find_library_path(name) |
在 OS 库搜索路径中查找 |
path_for_library(dir, name) |
在指定目录查找 |
filename_for_library(name) |
生成平台库文件名(lib*.so / .dll / .dylib) |
4.4 filesystem_helper — 跨平台文件系统
源自 pluginlib 的 filesystem_helper,在 ROS 2 全平台尚未统一支持 std::filesystem 时提供替代。
命名空间:rcpputils::fs
| 类型 / 函数 | 能力 |
|---|---|
fs::path |
路径拼接 /、exists()、is_directory()、parent_path()、extension() |
temp_directory_path() |
临时目录(Windows: GetTempPathA;Unix: $TMPDIR 或 /tmp) |
create_temp_directory(base, parent) |
唯一临时目录(mkdtemp 风格) |
create_directories(p) |
递归创建目录 |
remove / remove_all |
删除文件或目录树 |
current_path() |
当前工作目录 |
copy / rename |
文件复制与重命名 |
实现(494 行)调用 rcutils 与平台 API(stat、mkdir、remove 等)。
下游:rosbag2 测试临时目录、rviz 配置文件路径、rclcpp 测试资源路径等。
4.5 字符串工具
| 头文件 | 函数 | 说明 |
|---|---|---|
split.hpp |
split(input, delim, skip_empty) |
返回 vector<string> 或写入迭代器 |
join.hpp |
join(container, delim) |
容器元素拼接 |
find_and_replace.hpp |
find_and_replace(str, from, to) |
全局替换 |
下游示例:
rclcpp_components/component_manager.cpp— 解析 ament index 组件清单(splitby\n/;)rosbag2_transport/topic_filter.cpp— topic 名 token 化rclpy/node.cpp— 参数名 wildcard 正则构造(find_and_replace)
4.6 env / process
env.cpp — 封装 rcutils 环境 API:
1 | std::string get_env_var(const char * env_var) |
process.hpp — 头文件 inline 函数,调用 rcutils_get_executable_name() 返回 std::string。
4.7 asserts — 三层次校验
| 函数 | 异常类型 | 行为 |
|---|---|---|
require_true(cond, msg) |
std::invalid_argument |
校验输入参数 |
check_true(cond, msg) |
IllegalStateException |
校验内部状态 |
assert_true(cond, msg) |
AssertionException |
校验结果;Release 下为 no-op(NDEBUG) |
下游:rclcpp/serialization.cpp 在序列化前 check_true 指针非空。
4.8 time.hpp — chrono 转换
1 | template<typename DurationRepT, typename DurationT> |
防止 duration_cast 静默溢出,供 Executor 超时等场景使用。
4.9 pointer_traits — 智能指针 traits
扩展标准库:
rcpputils::is_pointer<T>— 识别 raw /shared_ptr/unique_ptrrcpputils::remove_pointer<T>— 从任意指针类型提取 pointee
用于模板 API 同时接受 raw 与 smart pointer。
4.10 endian.hpp
- C++17 以上:直接使用
std::endian - 否则:自定义
enum class endian { little, big, native }(Linux 用<endian.h>)
供序列化/网络字节序相关代码使用。
4.11 thread_safety_annotations.hpp
Clang Thread Safety Analysis 宏(GUARDED_BY、LOCKS_EXCLUDED 等)。非 Clang 编译器下展开为空,跨平台安全。
测试构建时对 Clang 启用 -Wthread-safety -Werror。
4.12 visibility_control.hpp
定义 RCPPUTILS_PUBLIC / RCPPUTILS_LOCAL / RCPPUTILS_BUILDING_DLL,控制 Windows DLL 导出与 Unix 默认 visibility。
5. rcppmath 命名空间
与 rcpputils 同包分发,独立命名空间 rcppmath。
5.1 clamp.hpp
1 | rcppmath::clamp(value, low, high); |
C++14 时代尚未有标准 std::clamp(C++17)时的替代;行为与标准库一致。
5.2 RollingMeanAccumulator
滑动窗口均值累加器(替代 boost rolling mean,避免 boost 依赖):
1 | void accumulate(T val) { ... O(1) 更新 sum_ ... } |
环形缓冲区 + 运行和,窗口满后 O(1) 更新。
6. tl_expected(vendored)
include/rcpputils/tl_expected/expected.hpp — TartanLlama/tl::expected 的 vendored 拷贝(CC0 公共领域)。
提供 C++17 风格的 expected<T, E>(类似 Rust Result),供尚未使用 C++23 std::expected 的代码使用。CMake lint 明确排除此文件。
7. 实现与 rcutils 委托关系
| rcpputils | 委托 rcutils / OS |
|---|---|
SharedLibrary |
rcutils_load/unload/get_symbol |
get_env_var / set_env_var |
rcutils_get_env / rcutils_set_env |
fs::* |
rcutils_is_file/is_directory + 平台 syscall |
find_library_path |
rcutils_is_file + 环境变量路径 |
get_executable_name |
rcutils_get_executable_name |
错误处理模式:rcutils 返回码 + 线程局部错误链 → rcpputils 提取 rcutils_get_error_string() 后 throw std::runtime_error(SharedLibrary/env),或返回 bool/空字符串(find_library)。
8. 下游消费者
| 包 / 模块 | 使用的 rcpputils 能力 |
|---|---|
| rclcpp | SharedLibrary、path_for_library(typesupport 动态加载)、check_true(序列化)、RCPPUTILS_SCOPE_EXIT(executor/action) |
| rclcpp_components | split、fs::path(解析组件资源) |
| rclpy(C++) | find_and_replace(参数 wildcard) |
| rosbag2 | fs::path、create_temp_directory、remove_all、make_scope_exit、split |
| rviz2 | fs::path |
| class_loader / pluginlib | 历史上 filesystem/split 源码同源,现多直接使用 rcpputils |
package.xml 声明依赖 rcpputils 的包:rclcpp、rclpy、rosbag2 等。
9. 测试
test/ 下 16 个 gtest,与模块一一对应:
| 测试 | 覆盖 |
|---|---|
test_asserts |
debug/release 下 assert_true 行为差异 |
test_shared_library |
加载 dummy .so、symbol 查找 |
test_filesystem_helper |
path 操作、临时目录 |
test_find_library |
LD_LIBRARY_PATH 搜索 |
test_env |
空/正常环境变量 |
test_scope_exit |
cancel 与析构调用 |
test_thread_safety_annotations |
Clang 注解编译 |
test_clamp / test_accumulator |
rcppmath |
10. 与 rcutils 文档对照
详见 rcutils 源码详细分析。
| 需求场景 | 应使用 |
|---|---|
| rcl/rmw C 代码 | rcutils |
| rclcpp / C++ 工具 | rcpputils(必要时仍可直接调 rcutils) |
| 仅头文件 split/join | #include rcpputils/split.hpp,无需链接 |
| 动态加载 typesupport | #include rcpputils/shared_library.hpp,链接 rcpputils |
| 日志 / 分配器 / C 错误链 | rcutils(rcpputils 不提供) |
11. 设计特点与演进
- 轻量:仅 5 个
.cpp,其余 header-only,降低链接开销 - 跨平台优先:filesystem、endian、库名前缀/后缀均处理 Windows/macOS/Linux 差异
- 渐进废弃:
get_env.hpp→env.hpp;fs::path待全平台 C++17 后或迁移至std::filesystem - Quality Level 1:见
QUALITY_DECLARATION.md,完整 CI lint + 测试 - 不重复 rcutils:新 C 级能力应先进 rcutils,C++ 包装再进 rcpputils
12. 推荐阅读顺序
docs/FEATURES.md— 官方功能清单与示例scope_exit.hpp— 理解 ROS 2 C++ 中最常见的用法shared_library.cpp+find_library.cpp— 动态加载链(连接 rclcpp typesupport)filesystem_helper.hpp+.cpp— 最大模块,跨平台 pathasserts.hpp— 三种断言语义split.hpp/join.hpp— 字符串工具模式rcppmath/*— 数学小工具- 对照
rclcpp/typesupport_helpers.cpp— 真实集成场景 - 对照 rcutils 分析 — 理解 C/C++ 分层
13. 小结
rcpputils 是 ROS 2 C++ 生态的基础工具层,位于 rcutils 之上、rclcpp 之下:
- 编译库(5 个源文件)提供 filesystem、shared_library、find_library、env、asserts
- 头文件库提供 scope guard、字符串、chrono、traits、endian、线程注解
rcppmath提供 clamp 与滑动均值- 核心模式:rcutils C API + C++ 异常/string + 跨平台 shim
日常阅读 rclcpp/rosbag2 代码时,常见 #include "rcpputils/..." 与 RCPPUTILS_SCOPE_EXIT;排查动态 typesupport 加载问题时,应沿 path_for_library → SharedLibrary → rcutils 链分析。
正在加载留言…