YAML-CPP
Introduction
- YAML (YAML Ain't Markup Language) is a human-readable data-serialization language, like JSON.
- YAML-CPP is a corresponding library for C++ able to create and read
.yamlfiles. - YAML is based on
key:valuespair. Those can be scalars (integers, float, string, bool), lists, associative arrays (maps, dictionaries) also seen as NODES, which are simply lists of multiple key:values. - YAML also allows to create aliases and references. See Wikipedia
- A very simple base file can be such as :
RootNode:
ANode1:
aString: My Name
aSubNode:
aFloat: 3.14
aSubNode2:
aFloat: 1.57
aBool: true
ANode2:
aInt : 34
How to use
The library can be found here.
Prepare library
- First, it must be built thanks to CMake for your particular builder.
- A build for MSVC 2019 can be found on GitLab.
- Once built/copied to a chosen path, the folder
YAML_CPP\share\cmake\yaml-cppmust be added to PATH.
Use it inside your project
- CMake integration is not standard.
- Normally, you would add
YAML_CPP_INCLUDE_DIRto theinclude_directories()directive, andYAML_CPP_LIBRARIESundertarget_link_libraries(). - But both these variables are empty. To use it, do as follow :
project(MyProject)
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Find the library
find_package(yaml-cpp REQUIRED) # here to ensure library exists
file(GLOB project_SOURCES
"src/*.h"
"src/*.hpp"
"src/*.cpp"
)
#Main app
add_executable(${PROJECT_NAME} "src/main.cpp" ${project_SOURCES})
target_link_libraries(${PROJECT_NAME} yaml-cpp) # here to include it
Warning
The library uses exceptions throwing whenever something goes wrong.
Please ensure to embrace your code with throw{} catch(...){} directives.
Documentation for the library can be found on Github.
Reading a YAML file
Once you get the file path, you can read it with :
YAML::Node root;
try
{
root = YAML::LoadFile(filepath);
}
catch(...){/*error - could not load*/}
Afterward, it is up to you to know how your file is structured. In the example given before, the first step is to ensure our root node RootNode exists and that it contains data :
// Do we have data as Nodes in our file ?
if(!root.IsMap())
{
// error - exit
}
// Check our RootNode exists
if(!root["RootNode"])
{
// error - exit
}
// OK, we can iterate over data
If you know exactly how you data should be, you must first ensure you key exists, then read the value while specifying its format :
// Example to access aFloat field
if(!root["RootNode"]["ANode1"]["aSubNode"]["aFloat"])
{
// does not exist
}
else
{
float value = root["RootNode"]["ANode1"]["aSubNode"]["aFloat"].as<float>();
// if wrong type is passed, throws an exception
}
- Nodes are accessed through their keys. If we have a length-undefined list, we can iterate through field of our node.
- Let's say I'd like to check all my aSubNodeX node :
// Ensure node exists
if(!root["RootNode"]["ANode1"])
{
// error
}
else
{
// create a const ref (we are just reading, so we won't modify it)
const YAML::Node& whereSubNodes = root["RootNode"]["ANode1"];
// iterate through keys to find our subnodes
try
{
for (YAML::const_iterator it = whereSubNodes.begin(); it != whereSubNodes.end(); ++it)
{
// it->first gives key, it->second gives value (can be a node too !)
if(it->first.as<std::string>().find("aSubNode") != std::string::npos)
{
// we found a correct Node, read float value if field exists else gives -1.0
float val = it->second["aFloat"] ? it->second["aFloat"].as<float>() : -1.0;
}
}
} catch(...) {/*error*/}
}
Writing a YAML file
- A
YAML::Emitter`` can be used to create a file. It can be written thanks to aspecial formatting. - Otherwise, you can populate Nodes and use the emitter afterward. Let's try creating a simple file :
// Root of our file
YAML::Node root;
// Create a node listing some values
root["MyNode"] = YAML::Node(YAML::NodeType::Map);
// We now will write our values under root["MyNode"]
YAML::Node& wrnode = root["MyNode"];
// Write some values
wrnode["seed"] = 3.14;
wrnode["name"] = "Glados";
wrnode["isTurret"] = false;
// Populate emitter
YAML::Emitter emitter;
emitter << root;
// Write to file
std::ofstream fout("my/path/to/file.yaml");
fout << emitter.c_str();
this will create the following file :
MyNode:
seed: 3.14
name: Glados
isTurret: False
