SkillAgentSearch skills...

Figcone

Read JSON, YAML, TOML, XML or INI configuration by declaring a struct

Install / Use

/learn @kamchatka-volcano/Figcone
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <img height="128" src="doc/logo.jpg"/> </p>

build & test (clang, gcc, MSVC)

figcone - is a C++17 / C++20 library, providing a convenient declarative interface for configuration parsers and built-in support for reading JSON, YAML, TOML, XML, INI and shoal config files. To use it, create a configuration schema by declaring a structure for each level of your config file and load it by calling a method, matching the preferred configuration format:

#include <figcone/figcone.h>
#include <filesystem>
#include <iostream>
#include <vector>

struct ThumbnailCfg {
    int maxWidth;
    int maxHeight;
};

struct PhotoViewerCfg {
    std::filesystem::path rootDir;
    std::vector<std::string> supportedFiles;
    ThumbnailCfg thumbnailSettings;
};

int main()
{
    auto cfgReader = figcone::ConfigReader{};
    auto cfg = cfgReader.readToml<PhotoViewerCfg>(R"(
        rootDir = "~/Photos"
        supportedFiles = [".jpg", ".png"]
        [thumbnailSettings]
          maxWidth = 256
          maxHeight = 256
    )");
    //At this point your config is ready to use
    std::cout << "Launching PhotoViewer in directory " << cfg.rootDir << std::endl;
}

This example uses the static reflection interface based on the pfr library. It requires C++20 and only works with aggregate initializable structures. On C++17, the runtime reflection solution originally developed with figcone is used to register configuration fields:

#include <figcone/figcone.h>
#include <filesystem>
#include <iostream>
#include <vector>

struct ThumbnailCfg : public figcone::Config
{
    int maxWidth = param<&ThumbnailCfg::maxWidth>();
    int maxHeight = param<&ThumbnailCfg::maxHeight>();
};

struct PhotoViewerCfg : public figcone::Config{
    //alternatively config fields can be created with macros:
    FIGCONE_PARAM(rootDir, std::filesystem::path);
    FIGCONE_PARAMLIST(supportedFiles, std::vector<std::string>);
    FIGCONE_NODE(thumbnailSettings, ThumbnailCfg);
};

int main()
{
    auto cfgReader = figcone::ConfigReader{};
    auto cfg = cfgReader.readToml<PhotoViewerCfg>(R"(
        rootDir = "~/Photos"
        supportedFiles = [".jpg", ".png"]
        [thumbnailSettings]
          maxWidth = 256
          maxHeight = 256
    )");
    //At this point your config is ready to use
    std::cout << "Launching PhotoViewer in directory " << cfg.rootDir << std::endl;
}

Table of Contents

Usage

Config structure for runtime reflection (C++17)

To register configuration structure, subclass figcone::Config and declare fields using the following macros:

  • FIGCONE_PARAM(name, type) - creates a type name; config field and registers it in the parser.
  • FIGCONE_PARAMLIST(name, listType) - creates a listType name; config field and registers it in the parser. listType can be any sequence container that supports the emplace_back operation, such as vector, deque, or list from the STL.
  • FIGCONE_NODE(name, type) - creates a type name; config field for a nested configuration structure and registers it in the parser. The type of the name field must be a subclass of figcone::Config.
  • FIGCONE_NODELIST(name, listType) - creates a listType name; config field for a list of nested configuration structures and registers it in the parser. listType can be any sequence container that supports the emplace_back operation, such as vector, deque, or list from the STL. The type stored in the list (listType::value_type) must be a subclass of figcone::Config.
  • FIGCONE_COPY_NODELIST(name, listType) - creates a listType name; config field for a list of nested configuration structures and registers it in the parser. listType can be any sequence container that supports the emplace_back operation, such as vector, deque, or list from the STL. The type stored in the list (listType::value_type) must be a subclass of figcone::Config. The first element of this list acts as a template for the other elements, which means that all unspecified parameters of the second and following elements will be copied from the first element without raising a parsing error for missing parameters.
  • FIGCONE_DICT(name, mapType) - creates a mapType name; config field for a nested dictionary and registers it in the parser. mapType can be any associative container that supports the emplace operation, such as map or unordered_map from the STL. The key type of the map must be std::string
    The preprocessor doesn't handle commas between template arguments in the correct way, so you need to create an alias for your map in order to use it with this macro:
   using StringMap = std::map<std::string, std::string>;
   FIGCONE_DICT(testDict, StringMap);

Notes:

  • All config entities listed above provide the parenthesis operator () which sets the default value and makes this config field optional. This means that the field can be omitted from the configuration file without raising an error. The empty operator () makes a field's value default initialized, otherwise the passed parameters are used for initialization. FIGCONE_NODE, FIGCONE_NODELIST, and FIGCONE_COPY_NODELIST only support default initialization.
  • It is also possible to make any config field optional by placing it in figcone::optional (a std::optional-like wrapper with a similar interface). If a value for this field is missing from the config file, the field remains uninitialized and no error occurs.
  • Types used for config parameters must be default constructible and copyable.

Supporting non-aggregate config structures

Runtime reflection interface of figcone relies on aggregate initialization of user-provided structures. If your config object needs to contain private data or virtual functions, it becomes a non-aggregate type. In this case, you must use the following using declaration to inherit figcone::Config's constructors: using Config::Config;

struct PhotoViewerCfg : public figcone::Config
{
    using Config::Config;
    virtual ~PhotoViewerCfg() = default; //virtual destructor makes PhotoViewerCfg non-aggregate
    FIGCONE_PARAM(rootDir, std::filesystem::path);
    FIGCONE_PARAMLIST(supportedFiles, std::vector<std::string>);
    FIGCONE_NODE(thumbnailSettings, ThumbnailCfg);
};

Registration without macros

Runtime reflection interface of figcone can be used without macros, as every configuration entity described earlier can be registered with the similarly named figcone::Config's member templates:

    struct Cfg : public figcone::Config{
        int testParam                               = param<&Cfg::testParam>();
        int testParam2                              = param<&Cfg::testParam2>()(100);
        figcone::optional<int> testParam3           = param<&Cfg::testParam3>();
        std::vector<double> testParamList           = paramList<&Cfg::testParamList>();
        TestNode testNode                           = node<&Cfg::testNode>();
        figcone::optional<TestNode> testNode2       = node<&Cfg::testNode2>();
        std::vector<TestNode> testNodeList          = nodeList<&Cfg::testNodeList>();
        std::vector<TestNode> copyTestNodeList      = copyNodeList<&Cfg::copyTestNodeList>();
        std::map<std::string, std::string> testDict = dict<&Cfg::testDict>();
    };

Internally, these methods use the nameof library to get config fields' names as strings. Note that nameof relies on non-standard functionality of C++ compilers, so if you don't like it, you can use figcone without it by providing names for config fields yourself:

    struct Cfg : public figcone::Config{
        int testParam                          = param<&Cfg::testParam>("testParam");
        int testParam2                         = param<&Cfg::testParam2>("testParam2")(100);
        figcone::optional<int> testParam3      = param<&Cfg::testParam3>("testParam3");
        std::vector<double> testParamList      = paramList<&Cfg::testParamList>("testParamList");
        TestNode testNode                      = node<&Cfg::testNode>("testNode");
        figcone::optional<TestNode> testNode2  = node<&Cfg::testNode2>("testNode2");
        std::vector<TestNode> testNodeList     = nodeList<&Cfg::testNodeList>("testNodeList");
        std::vector<TestNode> copyTestNodeList = copyNodeList<&Cfg::copyTestNodeList>("copyTestNodeList");
        std::map<std::string, std::string> testDict = dict<&Cfg::testDict>("testDict");
    };

Ple

View on GitHub
GitHub Stars118
CategoryDevelopment
Updated2mo ago
Forks5

Languages

C++

Security Score

100/100

Audited on Jan 29, 2026

No findings