rosidl 源码详细分析

rosidl 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rosidl
版本:3.1.8(Humble),子包 12 个,许可证 Apache 2.0

rosidl 是 ROS 2 接口定义语言(IDL)工具链的核心仓库:将 .msg / .srv / .action(或原生 .idl)解析为 AST,经 Empy 模板 生成 C/C++ 结构体与函数,并注册 typesupport 扩展点;运行时通过 rosidl_message_type_support_t 与 RMW/DDS 衔接。

范围说明:本仓库含 编译期 核心(adapter / parser / generator / runtime / introspection typesupport)。Python 生成器rosidl_generator_py)、通用 typesupport 分发rosidl_typesupport_cpp)、Fast-DDS typesupportrosidl_typesupport_fastrtps_*)、默认生成器聚合rosidl_default_generators)位于 独立仓库,见 §12。


1. 总体认识

1.1 核心职责

能力 实现位置 说明
Legacy 格式适配 rosidl_adapter .msg/.srv/.action.idl
IDL 解析 rosidl_parser Lark 语法 → definition.py AST
CMake 编排 rosidl_cmake rosidl_generate_interfaces()
C 代码生成 rosidl_generator_c struct + init/fini + type_support 声明
C++ 代码生成 rosidl_generator_cpp struct + builder + traits
C 运行时 rosidl_runtime_c String、序列、message_type_support_t
C++ 运行时 rosidl_runtime_cpp traits、bounded_vector、type_support 声明
Typesupport 接口 rosidl_typesupport_interface 符号命名宏
Introspection TS rosidl_typesupport_introspection_* 字段反射(Python/rqt/ros2 topic echo)
CLI 工具 rosidl_cli rosidl generate / translate
集成测试 rosidl_typesupport_introspection_tests introspection 正确性验证

1.2 在 ROS 2 栈中的位置

接口作者rosidl 仓库关联仓库 - 非本目录ROS 2 运行时foo.msg / bar.srv / Baz.actionrosidl_adapter\n.msg → .idlrosidl_parser\nLark ASTrosidl_cmake\nrosidl_generate_interfacesrosidl_generator_crosidl_generator_cpprosidl_runtime_crosidl_runtime_cpprosidl_typesupport_introspection_*rosidl_generator_pyrosidl_typesupport_cpprosidl_typesupport_fastrtps_*rosidl_default_generatorsrcl / rclcpprmw → DDS 序列化
阶段 输入 输出
适配 Chatter.msg Chatter.idl(OMG IDL 模块语法)
解析 .idl 文本 IdlFile / Message / Service / Action AST
生成 AST + Empy 模板 *.h / *.hpp / __functions.c / typesupport 源
链接 多个 __rosidl_* 可链接的 interface package
运行 rosidl_message_type_support_t RMW publish/take、Python 绑定、introspection

1.3 端到端流水线

1
2
3
4
5
6
7
8
9
10
11
package.xml + CMakeLists.txt
└── rosidl_generate_interfaces(${PROJECT_NAME} "msg/Foo.msg" DEPENDENCIES std_msgs)
├── [非 .idl] rosidl_adapter → 生成 build/.../msg/Foo.idl
├── rosidl_parser → AST(构建时由 generator 调用)
└── ament_execute_extensions("rosidl_generate_idl_interfaces")
├── rosidl_generator_c → lib...__rosidl_generator_c.so
├── rosidl_generator_cpp → 头文件库 __rosidl_generator_cpp
├── rosidl_typesupport_introspection_c/cpp
├── [外部] rosidl_generator_py
├── [外部] rosidl_typesupport_cpp
└── [外部] rosidl_typesupport_fastrtps_c/cpp

2. 仓库结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
rosidl/
├── rosidl_adapter/ # ★ .msg/.srv/.action → .idl (Python)
│ ├── rosidl_adapter/
│ │ ├── parser.py # Legacy 格式解析(与 parser 包不同)
│ │ ├── msg/ srv/ action/ # convert_*_to_idl
│ │ └── resource/*.idl.em # Empy 模板
│ └── cmake/rosidl_adapt_interfaces.cmake
├── rosidl_parser/ # ★ IDL 语法解析 (Lark)
│ ├── rosidl_parser/
│ │ ├── grammar.lark
│ │ ├── parser.py
│ │ └── definition.py # AST 类型定义 (~765 行)
│ └── bin/idl2png
├── rosidl_cmake/ # ★ CMake 宏与 generate_files 工具
│ ├── cmake/rosidl_generate_interfaces.cmake
│ ├── cmake/rosidl_get_typesupport_target.cmake
│ └── rosidl_cmake/__init__.py # Empy 驱动 generate_files()
├── rosidl_generator_c/ # C struct + functions
│ ├── resource/*.em
│ └── rosidl_generator_c/__init__.py
├── rosidl_generator_cpp/ # C++ struct + builder + traits
│ ├── resource/*.em
│ └── rosidl_generator_cpp/__init__.py
├── rosidl_runtime_c/ # C 运行时类型与 type_support 结构
├── rosidl_runtime_cpp/ # C++ traits、bounded_vector
├── rosidl_typesupport_interface/# typesupport 符号宏
├── rosidl_typesupport_introspection_c/
├── rosidl_typesupport_introspection_cpp/
├── rosidl_typesupport_introspection_tests/
└── rosidl_cli/ # rosidl 命令行

2.1 子包一览

包名 语言 职责
rosidl_adapter Python Legacy 接口 → IDL
rosidl_parser Python IDL → AST
rosidl_cmake CMake+Python 构建编排、generate_files
rosidl_generator_c Python+Empy 生成 C 代码
rosidl_generator_cpp Python+Empy 生成 C++ 代码
rosidl_runtime_c C 字符串、序列、type_support 结构体
rosidl_runtime_cpp C++ traits、初始化策略
rosidl_typesupport_interface C 头文件 符号命名约定
rosidl_typesupport_introspection_c Python+Empy C introspection typesupport
rosidl_typesupport_introspection_cpp Python+Empy C++ introspection typesupport
rosidl_typesupport_introspection_tests C++ introspection 测试框架
rosidl_cli Python 独立 CLI(非 colcon 主路径)

3. rosidl_adapter:Legacy 格式 → IDL

3.1 入口

CMake 调用 rosidl_adapt_interfaces(),最终执行:

1
2
# rosidl_adapter/main.py
convert_to_idl(basepath, package_name, relative_path, output_dir)

按后缀分发:

后缀 函数 输出目录
.msg convert_msg_to_idl output_dir/msg/
.srv convert_srv_to_idl output_dir/srv/
.action convert_action_to_idl output_dir/action/

3.2 Legacy 解析器(rosidl_adapter/parser.py

rosidl_parser 独立:专门解析 ROS 1 风格 .msg 文本。

支持的 primitive 类型

1
2
3
bool, byte, char, float32, float64,
int8/uint8, int16/uint16, int32/uint32, int64/uint64,
string, wstring, duration, time

语法元素

  • 注释:#
  • 常量:NAME=123
  • 数组:type[N] 固定长度,type[] / type[<=N] 动态/有界序列
  • Service:以 --- 分隔 request/response
  • Action:三段 --- 分隔 goal/result/feedback

命名校验:package/field 名小写+下划线;message 名 PascalCase(兼容 ROS 1 的宽松模式可选)。

3.3 类型映射(MSG → IDL)

1
2
3
4
5
6
7
8
9
# rosidl_adapter/msg/__init__.py
MSG_TYPE_TO_IDL = {
'bool': 'boolean',
'byte': 'octet',
'char': 'uint8',
'float32': 'float',
'float64': 'double',
# ...
}

3.4 IDL 模板(Empy)

msg.idl.em 将解析结果渲染为 OMG IDL 模块:

1
2
3
4
5
6
7
8
9
10
11
module test_msgs {
module msg {
struct Test {
boolean bool_value;
octet byte_value;
uint8 char_value;
float float32_value;
// ...
};
};
};

注释通过 @verbatim 注解保留。输出编码为 iso-8859-1(兼容非 ASCII 注释)。

Service / Action 模板(srv.idl.emaction.idl.em)生成对应 module 结构;Action 在 adapter 阶段仅转换用户定义的三段,衍生类型(SendGoal/GetResult 等)由 rosidl_parserAction 类在解析 IDL 后构建。


4. rosidl_parser:IDL → AST

4.1 解析器实现

  • 语法引擎:Lark(grammar.lark
  • 入口parse_idl_file(IdlLocator)IdlFile(locator, content)
  • 调试idl2png 可将语法树导出为 PNG
1
2
3
4
# parser.py
_parser = Lark(grammar, start='specification', maybe_placeholders=False)
tree = _parser.parse(idl_string)
content = extract_content_from_ast(tree)

4.2 AST 类型体系(definition.py

类型 说明
BasicType IDL 基本类型(int32booleanoctet…)
NamespacedType pkg::msg::Message 命名空间类型
Array / BoundedSequence / UnboundedSequence 数组与序列
BoundedString / UnboundedString 字符串(含 wstring)
Member 结构体字段
Constant 模块级常量
Structure struct 定义
Message 包装 Structure
Service request/response 两个 Message
Action goal/result/feedback + 衍生 service/message
Include #include 依赖
Annotation @verbatim
IdlContent 单文件全部内容
IdlLocator (basepath, relative_path) 定位器

命名后缀常量(全栈统一):

1
2
3
4
5
6
7
8
SERVICE_REQUEST_MESSAGE_SUFFIX = '_Request'
SERVICE_RESPONSE_MESSAGE_SUFFIX = '_Response'
ACTION_GOAL_SUFFIX = '_Goal'
ACTION_RESULT_SUFFIX = '_Result'
ACTION_FEEDBACK_SUFFIX = '_Feedback'
ACTION_GOAL_SERVICE_SUFFIX = '_SendGoal'
ACTION_RESULT_SERVICE_SUFFIX = '_GetResult'
ACTION_FEEDBACK_MESSAGE_SUFFIX = '_FeedbackMessage'

4.3 Action 衍生类型

用户 .action 文件只定义 goal/result/feedback 三段;Action 类自动构造:

衍生类型 成员
{Action}_SendGoal 服务 Request: goal_id (UUID) + goal;Response: accepted + stamp
{Action}_GetResult 服务 Request: goal_id;Response: status + result
{Action}_FeedbackMessage goal_id + feedback

隐式依赖:builtin_interfaces/msg/Time.idlunique_identifier_msgs/msg/UUID.idl

因此 action 包必须在 package.xml 中依赖 action_msgs(CMake 会检查)。


5. rosidl_cmake:构建编排

5.1 rosidl_generate_interfaces

核心步骤rosidl_generate_interfaces.cmake):

  1. 校验接口文件存在;支持 basepath:relative_path 元组
  2. 分离 .idl 与 legacy 文件
  3. 对 legacy 文件调用 rosidl_adapt_interfaces 生成 .idl
  4. .srv 额外在 build 目录生成 _Request.msg / _Response.msg(供 linter/索引)
  5. 收集 DEPENDENCIES 中的 IDL 文件
  6. 注册 ament index rosidl_interfaces
  7. ament_execute_extensions("rosidl_generate_idl_interfaces") — 触发所有 generator/typesupport
  8. 安装 .idl / .msgshare/${PROJECT_NAME}/

package.xml 要求:安装接口的包须声明:

1
<member_of_group>rosidl_interface_packages</member_of_group>

5.2 扩展点机制

各 generator 通过 ament_register_extension 注册:

1
2
3
4
5
# rosidl_generator_c/cmake/register_c.cmake
ament_register_extension(
"rosidl_generate_idl_interfaces"
"rosidl_generator_c"
"rosidl_generator_c_generate_interfaces.cmake")

拓扑顺序:后注册的 extension 可声明对先生成 target 的依赖。例如 introspection 要求:

1
2
3
if(NOT TARGET ${target}__rosidl_generator_cpp)
message(FATAL_ERROR "rosidl_generator_cpp must be executed before introspection...")
endif()

典型顺序:generator_c → generator_cpp → typesupport_*(introspection、fastrtps、cpp、py)

5.3 generate_files() — 共享生成框架

rosidl_cmake/__init__.py 中:

  1. 读取 JSON generator arguments(idl_tuplestemplate_diroutput_dir
  2. 对每个 IDL:parse_idl_file(locator) 得 AST
  3. Empy 渲染模板映射表
  4. 增量构建:比较 template/idl 与已有输出 mtime

C 与 C++ generator 仅 mapping 表 不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# rosidl_generator_c
mapping = {
'idl.h.em': '%s.h',
'idl__struct.h.em': 'detail/%s__struct.h',
'idl__functions.c.em': 'detail/%s__functions.c',
...
}

# rosidl_generator_cpp
mapping = {
'idl.hpp.em': '%s.hpp',
'idl__struct.hpp.em': 'detail/%s__struct.hpp',
'idl__builder.hpp.em': 'detail/%s__builder.hpp',
'idl__traits.hpp.em': 'detail/%s__traits.hpp',
...
}

5.4 rosidl_get_typesupport_target

供同包内其他 target 依赖生成的 typesupport 库:

1
2
3
rosidl_get_typesupport_target(cpp_typesupport_target
"${PROJECT_NAME}__rosidl_typesupport_cpp" "rosidl_typesupport_cpp")
target_link_libraries(my_node ${cpp_typesupport_target})

6. rosidl_generator_c / rosidl_generator_cpp

6.1 生成产物(以 std_msgs/msg/String 为例)

C 侧rosidl_generator_c/):

文件 内容
string.h 对外 include
detail/string__struct.h C struct 定义
detail/string__functions.h/.c init/fini/copy/resize
detail/string__type_support.h type_support 声明

C++ 侧rosidl_generator_cpp/):

文件 内容
string.hpp 对外 include
detail/string__struct.hpp String_<ContainerAllocator> 模板 struct
detail/string__builder.hpp fluent builder API
detail/string__traits.hpp data_type()name()has_fixed_size 等特化
detail/string__type_support.hpp C++ type_support 声明

6.2 类型映射

IDL → CBASIC_IDL_TYPES_TO_C):

  • booleanbool
  • octetuint8_t
  • charsigned char
  • wcharuint16_t

IDL → C++MSG_TYPE_TO_CPP):

  • 基本类型 → uint32_t
  • stringstd::basic_string<char, ...>(allocator 模板参数)
  • 嵌套类型 → pkg::msg::Type_<ContainerAllocator>
  • 固定数组 → std::array<T, N>
  • 动态序列 → std::vector<T, Allocator>

6.3 生成的 CMake Target

每个 interface package 产生多个库 target,后缀模式:

Target 后缀 内容
__rosidl_generator_c C struct + functions 源文件
__rosidl_generator_cpp 仅头文件(INTERFACE 库)
__rosidl_typesupport_introspection_c introspection C 实现
__rosidl_typesupport_introspection_cpp introspection C++ 实现
[外部] __rosidl_typesupport_cpp C++ typesupport 分发
[外部] __rosidl_typesupport_fastrtps_* Fast-DDS 序列化

7. rosidl_runtime_c / rosidl_runtime_cpp

7.1 C 运行时

字符串string.h):

1
2
3
4
5
typedef struct rosidl_runtime_c__String {
char * data;
size_t size; // 不含 '\0'
size_t capacity;
} rosidl_runtime_c__String;

宽字符串u16string.h
动态数组primitives_sequence.h + *_functions.h
有界序列sequence_bound.hstring_bound.h

Type support 结构message_type_support_struct.h):

1
2
3
4
5
struct rosidl_message_type_support_t {
const char * typesupport_identifier;
const void * data;
rosidl_message_typesupport_handle_function func;
};
  • typesupport_identifier:如 "rosidl_typesupport_introspection_cpp"
  • func:按 identifier 链式查找其他 typesupport 的 handle
  • ROSIDL_GET_MSG_TYPE_SUPPORT(Pkg, msg, MsgName):获取默认 C typesupport 符号

类似结构存在于 service_type_support_struct.haction_type_support_struct.h

消息初始化message_initialization.h):ROSIDL_RUNTIME_C_MSG_INIT_ALL / ZERO / DEFAULTS_ONLY 等策略。

7.2 C++ 运行时

traitstraits.hpp)提供通用模板与工具函数:

  • rosidl_generator_traits::value_to_yaml() — 各类型 YAML 序列化
  • has_fixed_size<T> / has_bounded_size<T> — 类型属性 trait
  • is_message<T> / is_service_request<T> / is_action_goal<T> — 类型分类(由 generator 特化)

bounded_vector.hpp:有界动态数组的 C++ 容器实现。

message_initialization.hpp:C++ 侧对应初始化枚举。


8. Typesupport 体系

8.1 接口层(rosidl_typesupport_interface

统一 符号命名,避免各 typesupport 插件冲突:

1
2
3
4
#define ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME( \
typesupport_name, package_name, interface_type, message_name) \
typesupport_name ## __get_message_type_support_handle ## __ \
package_name ## __ ## interface_type ## __ ## message_name

示例符号:rosidl_typesupport_introspection_cpp__get_message_type_support_handle__std_msgs__msg__String

8.2 Introspection Typesupport

用途:运行时反射消息字段——ros2 topic echorclpy、参数转换、调试工具。

核心结构message_introspection.hpp):

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct MessageMember_s {
const char * name_;
uint8_t type_id_; // ROS_TYPE_FLOAT, ROS_TYPE_MESSAGE, ...
size_t string_upper_bound_;
const rosidl_message_type_support_t * members_; // 嵌套消息
bool is_array_;
size_t array_size_;
bool is_upper_bound_;
uint32_t offset_; // 字段在 struct 内偏移
const void * default_value_;
// size/get/fetch/assign/resize 函数指针...
} MessageMember;

Identifieridentifier.cpp):

1
const char * typesupport_identifier = "rosidl_typesupport_introspection_cpp";

Generator 为每个消息生成 MessageMembers 静态表 + get_message_type_support_handle() 导出函数。

8.3 Typesupport 链与 RMW

运行时 publish 路径(简化):

1
2
3
4
5
rclcpp::Publisher::publish(msg)
→ rosidl_typesupport_cpp::get_message_type_support_handle<Message>()
→ 按 rmw 选择的 typesupport_identifier 查找
→ rosidl_typesupport_fastrtps_cpp(DDS CDR 序列化)
→ rmw_publish

Introspection typesupport 不参与 DDS 线格式序列化,但 Python 层需要它把 C struct 转为 Python 对象。


9. rosidl_cli

独立于 colcon 的 命令行工具rosidl_cli/cli.py):

1
2
rosidl generate ...   # 调用已注册 generator 扩展
rosidl translate ... # 格式转换(如 msg → idl)

通过 rosidl_cli/extensions.pyentry_points 可扩展子命令;主要用于工具开发/debug,普通接口包构建走 CMake 路径


10. 完整示例:从 .msg 到可链接库

10.1 作者侧

my_msgs/msg/Velocity.msg

1
2
float64 linear
float64 angular

CMakeLists.txt

1
2
3
4
5
6
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Velocity.msg"
)
ament_export_dependencies(rosidl_default_runtime)
ament_package()

package.xml

1
2
3
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

10.2 构建时发生的事

typesupport_*rosidl_generator_*rosidl_adapterCMaketypesupport_*rosidl_generator_*rosidl_adapterCMakeconvert Velocity.msgbuild/.../msg/Velocity.idlament_execute_extensionsparse_idl + Empy 渲染Velocity.h/hpp, __functions.cintrospection + fastrtps + ...libmy_msgs__rosidl_* targets

10.3 安装布局

1
2
3
4
5
6
7
8
9
10
11
install/my_msgs/
├── share/my_msgs/
│ ├── msg/Velocity.msg
│ ├── msg/Velocity.idl
│ └── cmake/... # exported targets
├── include/my_msgs/my_msgs/
│ └── msg/velocity.hpp
└── lib/
├── libmy_msgs__rosidl_generator_c.so
├── libmy_msgs__rosidl_typesupport_introspection_c.so
└── libmy_msgs__rosidl_typesupport_fastrtps_cpp.so

11. Service 与 Action 的处理差异

类型 Adapter 输出 Parser 产物 额外生成
Message 单 struct IDL Message 1 组 msg 文件
Service request/response struct Service + 两个 Message Request/Response 消息 + srv typesupport
Action goal/result/feedback struct Action + 衍生 3 类 Goal/Result/Feedback + SendGoal/GetResult + FeedbackMessage

CMake 对 .srv 还会在 build 目录拆分出 _Request.msg / _Response.msg,便于 rosidl_interfaces index 与 linter。


12. 关联仓库(非本目录)

仓库路径(Humble) 职责
rosidl_default_generators ros2/rosidl_defaults 聚合默认 generator + typesupport 依赖
rosidl_default_runtime ros2/rosidl_defaults 运行时依赖聚合
rosidl_generator_py ros2/rosidl_python Python 模块生成
rosidl_typesupport_c/cpp ros2/rosidl_typesupport typesupport 分发层
rosidl_typesupport_fastrtps_* ros2/rosidl_typesupport_fastrtps Fast-DDS CDR 序列化
rosidl_typesupport_introspection_* 本仓库 反射 typesupport

find_package(rosidl_default_generators) 会拉入上述外部 generator,使 rosidl_generate_interfaces 一次触发完整工具链。


13. 与 ROS 1 genmsg 对比

维度 ROS 1 genmsg rosidl
源格式 .msg only .msg/.srv/.action + .idl
中间表示 内部 AST 标准 OMG IDL
生成语言 C++(roscpp) C + C++ + Python
Typesupport 无插件概念 多 typesupport 插件
字符串 std::string rosidl_runtime_c__String / 可配置 allocator
数组 固定/vector 固定 array + bounded/unbounded sequence
Action 无原生 原生 .action + 衍生类型
构建 catkin msg 宏 ament rosidl_generate_interfaces + extensions

14. 调试与常见问题

现象 排查方向
rosidl_generate_interfaces 报错缺 action_msgs action 包未声明依赖
缺少 rosidl_interface_packages group package.xml 未加 member_of_group
找不到 typesupport symbol 未 link __rosidl_typesupport_cpp target
修改 .msg 未生效 检查 build 目录缓存;clean rebuild
IDL 解析失败 rosidl translateidl2png 看语法树
Python 导入失败 确认 rosidl_generator_py 已执行(外部包)
introspection 字段偏移错误 查看 rosidl_typesupport_introspection_tests 对应用例

实用命令

1
2
3
4
5
6
7
8
# 查看某包安装了哪些接口
cat install/share/my_msgs/rosidl_interfaces

# 单独转换 msg → idl(debug)
rosidl translate msg my_msgs/msg/Velocity.msg --output-idl ...

# 查看生成头文件
ls build/my_msgs/rosidl_generator_cpp/my_msgs/msg/

15. 源码阅读顺序

  1. 走通样例:手写最小 msg 包,colcon build 后对照 build/install/ 生成物
  2. 适配层rosidl_adapter/parser.pymsg/__init__.pyresource/msg.idl.em
  3. IDL ASTrosidl_parser/definition.pyparser.pygrammar.lark
  4. CMake 编排rosidl_generate_interfaces.cmakeregister_c.cmake
  5. 生成框架rosidl_cmake/__init__.pygenerate_files()
  6. C 生成rosidl_generator_c/__init__.py + resource/idl__struct.h.em
  7. C++ 生成rosidl_generator_cpp/__init__.py + __traits.hpp.em
  8. 运行时message_type_support_struct.htraits.hpp
  9. Introspectionmessage_introspection.hpprosidl_typesupport_introspection_cpp/resource/msg__type_support.cpp.em
  10. 外部衔接rosidl_typesupport_cpp(另一仓库)→ rmw_fastrtps

16. 小结

rosidl 目录实现 ROS 2 强类型接口的编译期核心rosidl_adapter 兼容 Legacy 格式,rosidl_parser 提供标准 IDL AST,rosidl_cmake 通过 ament 扩展点 编排多 generator/typesupport 并行生成,rosidl_runtime_* 定义运行时公共类型与 type_support 结构,rosidl_typesupport_introspection_* 提供字段反射能力。

理解「.msg 如何变成 DDS 线上的字节」需串联 本仓库生成链路外部 typesupport_fastrtps + rmw 两段;调试接口问题时,从 rosidl_generate_interfaces 的 build log 与生成的 detail/*__struct.hpp 入手最为直接。

文章互动

阅读 --

留言

0 条留言

正在加载留言…