yaml-cpp 学习笔记
yaml-cpp 学习笔记
yaml-cpp 是 C++ 下使用最广泛的 YAML 1.2 解析与生成库,纯 C++,无运行时依赖,CMake 友好。本笔记按「装 → 读 → 写 → 异常 → 集成」顺序梳理常用 API。
安装
方式一:源码编译 + 安装
git clone https://github.com/jbeder/yaml-cpp.git
cd yaml-cpp
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/path/to/install -DYAML_BUILD_SHARED_LIBS=ON
cmake --build build --config Release --target install
常用 CMake 选项:
| 选项 | 默认 | 说明 |
|---|---|---|
YAML_BUILD_SHARED_LIBS |
OFF | 编译为动态库 |
YAML_CPP_BUILD_TESTS |
OFF | 是否构建测试 |
YAML_CPP_BUILD_TOOLS |
OFF | 是否构建命令行工具 |
YAML_CPP_INSTALL |
ON | 是否生成 install 目标 |
方式二:vcpkg
vcpkg install yaml-cpp
方式三:Conan
conan install --requires=yaml-cpp/0.8.0
节点与类型
yaml-cpp 的核心类型是 YAML::Node,YAML 中的任何元素(标量 / 序列 / 映射)都用一个 Node 表示。
#include <yaml-cpp/yaml.h>
Node::Type() 返回值:
| 类型 | 含义 |
|---|---|
YAML::NodeType::Null |
null / 空节点 |
YAML::NodeType::Scalar |
标量(字符串、数字、布尔) |
YAML::NodeType::Sequence |
序列(数组) |
YAML::NodeType::Map |
映射(键值对) |
YAML::NodeType::Undefined |
不存在的节点(访问越界) |
判断方法:node.IsScalar() / IsSequence() / IsMap() / IsNull() / IsDefined()。
加载 YAML
从文件加载
YAML::Node config = YAML::LoadFile("config.yaml");
从字符串加载
YAML::Node node = YAML::Load("name: yaml-cpp\nversion: 0.8.0");
多文档加载
YAML 支持用 --- 分割多文档,对应 LoadAllFromFile:
std::vector<YAML::Node> docs = YAML::LoadAllFromFile("multi.yaml");
for (const auto& doc : docs) {
// ...
}
读取与遍历
假设 config.yaml:
name: yaml-cpp
version: 0.8.0
authors:
- Jesse Beder
- Contributors
options:
shared: true
tests: false
取字段
auto config = YAML::LoadFile("config.yaml");
std::string name = config["name"].as<std::string>();
std::string version = config["version"].as<std::string>();
bool shared = config["options"]["shared"].as<bool>();
缺省值
如果字段可能不存在,用带参的 as<T>(default) 更安全:
bool tests = config["options"]["tests"].as<bool>(false);
int retries = config["retries"].as<int>(3); // 字段不存在时返回 3
遍历 Sequence
for (const auto& author : config["authors"]) {
std::cout << author.as<std::string>() << "\n";
}
遍历 Map
for (const auto& kv : config["options"]) {
auto key = kv.first.as<std::string>();
auto val = kv.second.as<bool>();
std::cout << key << " = " << std::boolalpha << val << "\n";
}
类型转换
内置类型
int / double / bool / std::string 直接 as<T>() 即可。
STL 容器
std::vector / std::list / std::map 都支持开箱即用:
auto authors = config["authors"].as<std::vector<std::string>>();
auto options = config["options"].as<std::map<std::string, bool>>();
自定义类型
通过特化 YAML::convert<T> 给自己的类加上序列化能力。
struct Vec3 {
double x, y, z;
};
namespace YAML {
template<>
struct convert<Vec3> {
static Node encode(const Vec3& v) {
Node node;
node.push_back(v.x);
node.push_back(v.y);
node.push_back(v.z);
return node;
}
static bool decode(const Node& node, Vec3& v) {
if (!node.IsSequence() || node.size() != 3) return false;
v.x = node[0].as<double>();
v.y = node[1].as<double>();
v.z = node[2].as<double>();
return true;
}
};
}
之后即可:
Vec3 pos = node["position"].as<Vec3>();
修改与写入
Node 可以直接像 STL 容器一样赋值:
YAML::Node node;
node["name"] = "demo";
node["count"] = 42;
node["tags"].push_back("alpha");
node["tags"].push_back("beta");
// 直接写到文件
std::ofstream fout("out.yaml");
fout << node;
输出:
name: demo
count: 42
tags:
- alpha
- beta
注意:
Node是引用语义(指向同一份底层数据)。auto a = node["x"]; a = 1;会改到node["x"],不会创建副本。
Emitter — 精细控制输出格式
YAML::Emitter 可以控制风格(块式 / 流式 / 缩进 / 注释):
YAML::Emitter out;
out << YAML::BeginMap
<< YAML::Key << "name" << YAML::Value << "yaml-cpp"
<< YAML::Key << "tags" << YAML::Value
<< YAML::Flow << YAML::BeginSeq << "a" << "b" << YAML::EndSeq
<< YAML::Key << "config" << YAML::Value
<< YAML::BeginMap
<< YAML::Key << "shared" << YAML::Value << true
<< YAML::EndMap
<< YAML::EndMap;
std::cout << out.c_str();
输出:
name: yaml-cpp
tags: [a, b]
config:
shared: true
常用 Manipulator:
| Manipulator | 作用 |
|---|---|
BeginMap / EndMap |
开始/结束映射 |
BeginSeq / EndSeq |
开始/结束序列 |
Key / Value |
标记键值 |
Flow |
用流式([a, b])输出 |
Block |
用块式(默认)输出 |
Comment("...") |
插入注释 |
Literal / DoubleQuoted |
字符串风格 |
异常处理
yaml-cpp 抛 YAML::Exception 体系下的派生类,常见的有:
| 异常 | 触发场景 |
|---|---|
YAML::ParserException |
YAML 语法解析错误 |
YAML::BadFile |
文件打开失败 |
YAML::BadConversion |
as<T>() 类型转换失败 |
YAML::KeyNotFound |
强制访问不存在的键 |
YAML::InvalidNode |
操作未初始化/无效节点 |
推荐做法:解析时抓 ParserException / BadFile,访问字段时用带默认值的 as<T>(default) 而非 try/catch 包每一处:
try {
auto config = YAML::LoadFile("config.yaml");
auto port = config["server"]["port"].as<int>(8080);
// ...
} catch (const YAML::BadFile& e) {
std::cerr << "config file missing: " << e.what() << "\n";
} catch (const YAML::ParserException& e) {
std::cerr << "YAML syntax error: " << e.what() << "\n";
}
CMake 集成
安装到系统目录后,下游项目直接:
find_package(yaml-cpp REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE yaml-cpp::yaml-cpp)
如果安装到了非默认路径,用 CMAKE_PREFIX_PATH 指向安装根目录:
cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/install
或单独指定:
cmake -S . -B build -Dyaml-cpp_DIR=/path/to/install/lib/cmake/yaml-cpp
找不到 yaml-cpp-targets.cmake 的常见原因
- 编译时未指定
-DYAML_CPP_INSTALL=ON(0.7+ 默认 ON,但旧版本需要显式打开) - 没跑
--target install,只编了库但没 install CMAKE_PREFIX_PATH没传到 cmake 命令- 同时装了 Debug / Release 但只 install 了其中一个 config
FetchContent 方式(不安装直接拉源码)
include(FetchContent)
FetchContent_Declare(
yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
GIT_TAG 0.8.0
)
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(yaml-cpp)
target_link_libraries(myapp PRIVATE yaml-cpp::yaml-cpp)