Changing yaml parsing library
created: 2025-03-09 17:08:31 UTClast updated: 2025-03-09 17:09:53 UTC
Initially I had planned to use libcyaml since that is what I was using in my initial prototype written in C, prior to the current conversion to C++. Migrating the code over, I ended up running into issues with C++ playing nicely with it. Instead of fixing those issues, since I am now using C++, I decided to take a look at the other options. Right now I am currently playing around with using a library called yaml-cpp. So far, it is pretty simple to get going with.
#include "Loader.h"
#include <yaml-cpp/yaml.h>
#include <iostream>
Loader::Loader(std::string path) : path(path) {}
Loader::~Loader() {}
void Loader::Load() {
YAML::Node config = YAML::LoadFile(path);
float playerX = config["player"]["x"].as<float>();
float playerY = config["player"]["y"].as<float>();
std::cout << "Player X from config: " << playerX << std::endl;
std::cout << "Player Y from config: " << playerY << std::endl;
if (config["wall_textures"]) {
if (config["wall_textures"].IsSequence()) {
std::vector<std::string> wallTextures =
config["wall_textures"].as<std::vector<std::string>>();
for (auto texture : wallTextures) {
std::cout << "Wall Texture: " << texture << std::endl;
}
}
}
if (config["sprite_textures"]) {
if (config["sprite_textures"].IsSequence()) {
std::vector<std::string> spriteTextures =
config["sprite_textures"].as<std::vector<std::string>>();
for (auto texture : spriteTextures) {
std::cout << "Sprite Texture: " << texture << std::endl;
}
}
}
int mapRows = config["map"]["rows"].as<int>();
int mapColumns = config["map"]["columns"].as<int>();
std::string mapPath = config["map"]["path"].as<std::string>();
std::cout << "Map Rows: " << mapRows << std::endl;
std::cout << "Map Columns: " << mapColumns << std::endl;
std::cout << "Map File: " << mapPath << std::endl;
}That is what the loader currently looks like. Not a whole lot and will definitely be built up over time, but working with the library is a bit simpler than libcyaml. Libcyaml requires setting up a lot up front. However, that set up is beneficial since it does give fine grain control over deserializing the data in a yaml file into structs. But with yaml-cpp, you can just index into it like a map and define the type it should deserialize into.
Below is a code snippet from the C version.
#include "level.h"
#include <stdio.h>
level_t *level = NULL;
static const cyaml_config_t config = {
.log_fn = cyaml_log,
.mem_fn = cyaml_mem,
.log_level = CYAML_LOG_WARNING,
};
static const cyaml_schema_value_t string_ptr_schema = {
CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
};
static const cyaml_schema_field_t sprite_field_schema[] = {
CYAML_FIELD_FLOAT("x", CYAML_FLAG_DEFAULT, sprite_t, x),
CYAML_FIELD_FLOAT("y", CYAML_FLAG_DEFAULT, sprite_t, y),
CYAML_FIELD_INT("texture", CYAML_FLAG_DEFAULT, sprite_t, texture),
CYAML_FIELD_END,
};
static const cyaml_schema_value_t sprite_schema = {
CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, sprite_t, sprite_field_schema),
};
static const cyaml_schema_field_t player_field_schema[] = {
CYAML_FIELD_FLOAT("x", CYAML_FLAG_DEFAULT, player_t, x),
CYAML_FIELD_FLOAT("y", CYAML_FLAG_DEFAULT, player_t, y),
CYAML_FIELD_END,
};
static const cyaml_schema_field_t map_field_schema[] = {
CYAML_FIELD_INT("rows", CYAML_FLAG_DEFAULT, map_t, rows),
CYAML_FIELD_INT("columns", CYAML_FLAG_DEFAULT, map_t, cols),
CYAML_FIELD_STRING_PTR("file", CYAML_FLAG_POINTER, map_t, map_file, 0, CYAML_UNLIMITED),
CYAML_FIELD_END,
};
static const cyaml_schema_field_t level_fields_schema[] = {
CYAML_FIELD_MAPPING_PTR("player", CYAML_FLAG_POINTER, level_t, p,
player_field_schema),
CYAML_FIELD_MAPPING_PTR("map", CYAML_FLAG_POINTER, level_t, map, map_field_schema),
CYAML_FIELD_SEQUENCE_COUNT("textures", CYAML_FLAG_POINTER, level_t,
textures, n_textures, &string_ptr_schema, 0,
CYAML_UNLIMITED),
CYAML_FIELD_SEQUENCE_COUNT("sprites", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, level_t, sprites,
n_sprites, &sprite_schema, 0, CYAML_UNLIMITED),
CYAML_FIELD_END,
};
static const cyaml_schema_value_t level_schema = {
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, level_t, level_fields_schema),
};
void level_load(char *path) {
cyaml_err_t err;
if (level != NULL) {
level_free();
}
err = cyaml_load_file(path, &config, &level_schema, (void **)&level, NULL);
if (err != CYAML_OK) {
fprintf(stderr, "Error: %s\n", cyaml_strerror(err));
return;
}
}
void level_free() {
cyaml_free(&config, &level_schema, level, 0);
}I am doing some changes to the structure of the yaml files. Right now, primarily splitting wall textures from sprite textures.
player: x: 110 y: 110 map: rows: 13 columns: 20 path: ./assets/map/test wall_textures: - "./assets/textures/redbrick.png" - "./assets/textures/graystone.png" sprite_textures: - "./assets/textures/armor.png" - "./assets/textures/barrel.png"