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"