Figcone
Read JSON, YAML, TOML, XML or INI configuration by declaring a struct
Install / Use
/learn @kamchatka-volcano/FigconeREADME
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
- Installation
- Running tests
- Building examples
- License
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 atype name;config field and registers it in the parser. - FIGCONE_PARAMLIST(
name,listType) - creates alistType name;config field and registers it in the parser. listType can be any sequence container that supports theemplace_backoperation, such asvector,deque, orlistfrom the STL. - FIGCONE_NODE(
name,type) - creates atype name;config field for a nested configuration structure and registers it in the parser. The type of the name field must be a subclass offigcone::Config. - FIGCONE_NODELIST(
name,listType) - creates alistType name;config field for a list of nested configuration structures and registers it in the parser.listTypecan be any sequence container that supports theemplace_backoperation, such asvector,deque, orlistfrom the STL. The type stored in the list (listType::value_type) must be a subclass offigcone::Config. - FIGCONE_COPY_NODELIST(
name,listType) - creates alistType name;config field for a list of nested configuration structures and registers it in the parser.listTypecan be any sequence container that supports theemplace_backoperation, such asvector,deque, orlistfrom the STL. The type stored in the list (listType::value_type) must be a subclass offigcone::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 amapType name;config field for a nested dictionary and registers it in the parser.mapTypecan be any associative container that supports the emplace operation, such asmaporunordered_mapfrom the STL. The key type of the map must bestd::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 emptyoperator ()makes a field's value default initialized, otherwise the passed parameters are used for initialization.FIGCONE_NODE,FIGCONE_NODELIST, andFIGCONE_COPY_NODELISTonly support default initialization. - It is also possible to make any config field optional by placing it in
figcone::optional(astd::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
