{"id":16693188,"url":"https://github.com/biojppm/c4conf","last_synced_at":"2025-03-21T18:33:46.008Z","repository":{"id":82224607,"uuid":"191062142","full_name":"biojppm/c4conf","owner":"biojppm","description":"YAML-based configuration data trees, with override facilities including command line arguments.","archived":false,"fork":false,"pushed_at":"2024-04-28T23:47:30.000Z","size":105,"stargazers_count":21,"open_issues_count":2,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-18T04:12:26.463Z","etag":null,"topics":["configuration","data-trees","yaml"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/biojppm.png","metadata":{"files":{"readme":"README.md","changelog":"changelog/0.1.0.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-09T22:36:18.000Z","updated_at":"2025-03-06T18:04:23.000Z","dependencies_parsed_at":"2024-10-28T11:29:53.853Z","dependency_job_id":"42b45735-4156-40fd-bb23-86925502d049","html_url":"https://github.com/biojppm/c4conf","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Fc4conf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Fc4conf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Fc4conf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biojppm%2Fc4conf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/biojppm","download_url":"https://codeload.github.com/biojppm/c4conf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244849847,"owners_count":20520804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","data-trees","yaml"],"created_at":"2024-10-12T16:29:30.313Z","updated_at":"2025-03-21T18:33:45.637Z","avatar_url":"https://github.com/biojppm.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# c4conf\n[![MIT Licensed](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/biojppm/c4conf/blob/master/LICENSE.txt)\n[![release](https://img.shields.io/github/v/release/biojppm/c4conf?color=g\u0026include_prereleases\u0026label=release%20\u0026sort=semver)](https://github.com/biojppm/c4conf/releases)\n\n[![test](https://github.com/biojppm/c4conf/workflows/test/badge.svg?branch=master)](https://github.com/biojppm/c4conf/actions)\n[![Coveralls](https://coveralls.io/repos/github/biojppm/c4conf/badge.svg?branch=master)](https://coveralls.io/github/biojppm/c4conf)\n[![Codecov](https://codecov.io/gh/biojppm/c4conf/branch/master/graph/badge.svg?branch=master)](https://codecov.io/gh/biojppm/c4conf)\n[![Total alerts](https://img.shields.io/lgtm/alerts/g/biojppm/c4conf.svg?logo=lgtm\u0026logoWidth=18)](https://lgtm.com/projects/g/biojppm/c4conf/alerts/)\n[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/biojppm/c4conf.svg?logo=lgtm\u0026logoWidth=18)](https://lgtm.com/projects/g/biojppm/c4conf/context:cpp)\n\nc4conf is a C++ library offering use of a YAML tree as a program's configuration, using [rapidyaml](https://github.com/biojppm/rapidyaml). It leverages YAML's rich data grammar to simplify loading and successively overriding configurations:\n  - from explicit YAML configurations (eg, `mapnode.seqnode[1]=[seq,of,values]`)\n  - from YAML files, optionally targetting a nested node\n  - from directories (ie, walk through YAML files in the directory), also with optional target node.\n\nc4conf can be used with regular function calls from your code, or through fully customizable command line arguments, provided with parsing facilities.\n\nAfter c4conf finishes with the configuration tree, you can visit the tree and read its values using any of the deserialization mechanisms available in [rapidyaml](https://github.com/biojppm/rapidyaml). See the [rapidyaml documentation here](https://rapidyaml.docsforge.com/), in particular the [rapidyaml quickstart](https://rapidyaml.docsforge.com/master/getting-started/#quick-start).\n\nc4conf follows the same design principles of [rapidyaml](https://github.com/biojppm/rapidyaml) (low latency, low number of allocations, full control of allocations and error behaviors, no dependency on the STL). It is written in C++11, and is extensively tested in the same compilers/platforms where [rapidyaml](https://github.com/biojppm/rapidyaml) is tested.\n\n### Quickstart\n(See the complete [quickstart code here](samples/quickstart.cpp), should be enough to get going). This example has a default config tree, which is changed based on the command line arguments and then simply printed to stdout:\n```c++\n#include \u003cc4/conf/conf.hpp\u003e\n#include \u003ciostream\u003e\n\nusing namespace c4::conf;\n\n// these are settings used to fill the default config tree of this example:\nconst char default_settings[] = R\"(\nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n)\";\n\n// some custom actions for command line switches\nvoid setfoo(Tree \u0026tree, csubstr);\nvoid setbar(Tree \u0026tree, csubstr);\nvoid setfoo3(Tree \u0026tree, csubstr foo3val);\nvoid setbar2(Tree \u0026tree, csubstr bar2val);\nvoid show_help(const char *exename);\n\n// create the specs for the command line options to be handled by\n// c4conf. These options will be used to transform the config tree:\nconstexpr const ConfigActionSpec conf_specs[] = {\n    spec_for\u003cConfigAction::set_node \u003e(\"-cn\" , \"--conf-node\"  ), // override a node (scalars, seqs or vals)\n    spec_for\u003cConfigAction::load_file\u003e(\"-cf\" , \"--conf-file\"  ), // override from a file, optionally into a nested node\n    spec_for\u003cConfigAction::load_dir \u003e(\"-cd\" , \"--conf-dir\"   ), // override from all files in a directory, optionally into a nested node\n    spec_for(\u0026setfoo ,                \"-sf\" , \"--set-foo\"     , {}           , \"call setfoo()\"),\n    spec_for(\u0026setbar ,                \"-sb\" , \"--set-bar\"     , {}           , \"call setbar()\"),\n    spec_for(\u0026setfoo3,                \"-sf3\", \"--set-foo3-val\", \"\u003cfoo3val\u003e\"  , \"call setfoo3() with a required arg)\"),\n    spec_for(\u0026setbar2,                \"-sb2\", \"--set-bar2-val\", \"[\u003cbar2val\u003e]\", \"call setbar2() with an optional arg)\"),\n};\n\n// Load settings, and override them with any command-line arguments.\n// The arguments registered above are filtered out of the input, and\n// any other arguments will remain.\nc4::yml::Tree makeconf(int *argc, char ***argv)\n{\n    // This is our config tree; fill it with the defaults.\n    c4::yml::Tree tree = c4::yml::parse(\"(defaults)\", default_settings);\n    // Parse the input args, filtering out the config options\n    // registered above, and gathering them into the returned\n    // container. Any options not listed in conf_specs are ignored and\n    // will remain in (argc, argv). Note that this overload creating a\n    // vector of ParsedOpt is chosen for brevity; you can use a\n    // lower-level overload writing into a memory span.\n    auto configs = parse_opts\u003cstd::vector\u003cParsedOpt\u003e\u003e(argc, argv, conf_specs, std::size(conf_specs));\n    // After successfully parsing, you should also do validation, but\n    // we skip that step in this sample.\n    //\n    // Now apply the config options onto the defaults tree.  All\n    // options are handled in the order given by the user.\n    c4::conf::Workspace workspace(\u0026tree);\n    workspace.apply_opts(configs);\n    return tree;\n}\n\nint main(int argc, char *argv[])\n{\n    // This is our resulting config tree:\n    c4::yml::Tree cfg = makeconf(\u0026argc, \u0026argv);\n    // That's it. All your settings are now loaded into cfg.\n    // Any c4conf arguments are filtered out of argc\n    // and argv. If there are further arguments to be parsed by the\n    // application, this is the occasion to do it. In this example, we\n    // only have --help, and raise an error on any other argument. To\n    // be clear, --help could be handled by parse_opts(), but we\n    // choose to deal with it here instead, to show that c4conf does\n    // not take over the options of your program.\n    for(const char *arg = argv + 1; arg \u003c argv + argc; ++arg)\n    {\n        csubstr s{*arg, strlen(*arg)};\n        if(s == \"-h\" || s == \"--help\")\n        {\n            show_help(argv[0]);\n            return 0;\n        }\n        else\n        {\n            std::cout \u003c\u003c \"unknown argument: \" \u003c\u003c *arg \u003c\u003c \"\\n\";\n            return (int)(arg - argv);\n        }\n    }\n    // Now you can deserialize the config tree\n    // into your program's native data structures.\n    // Since this is an example, we stop here and just\n    // dump the tree to the output:\n    std::cout \u003c\u003c tree;\n}\n```\nc4conf also provides facilities to print formatted help for the arguments registered to it:\n```c++\nvoid show_help(const char *exename)\n{\n    auto dump = [](csubstr s){ std::cout \u003c\u003c s; };\n    print_help(dump, conf_specs, std::size(conf_specs), \"conf options\");\n}\n```\n\n### Usage examples\n\nNow here are some examples with this executable:\n\n```console\n# getting help\n$ quickstart --help\n------------\nconf options\n------------\n  -cn [\u003ctargetpath\u003e=]\u003cvalidyaml\u003e, --conf-node [\u003ctargetpath\u003e=]\u003cvalidyaml\u003e\n                      Load explicit YAML code into a target config node.\n                      \u003ctargetpath\u003e is optional, and defaults to the root\n                      level; ie, when \u003ctargetpath\u003e is omitted, then the\n                      YAML tree resulting from parsing \u003cvalidyaml\u003e is merged\n                      starting at the config tree's root node. Otherwise\n                      the tree from \u003cvalidyaml\u003e is merged starting at the\n                      config tree's node at \u003ctargetpath\u003e. \n  -cf [\u003ctargetpath\u003e=]\u003cfilename\u003e, --conf-file [\u003ctargetpath\u003e=]\u003cfilename\u003e\n                      Load a YAML file and merge into a target config node.\n                      \u003ctargetpath\u003e is optional, and defaults to the root\n                      level; ie, when \u003ctargetpath\u003e is omitted, then the\n                      YAML tree resulting from parsing \u003cvalidyaml\u003e is merged\n                      starting at the config tree's root node. Otherwise\n                      the tree from \u003cvalidyaml\u003e is merged starting at the\n                      config tree's node at \u003ctargetpath\u003e. \n  -cd [\u003ctargetpath\u003e=]\u003cdirectory\u003e, --conf-dir [\u003ctargetpath\u003e=]\u003cdirectory\u003e\n                      Consecutively load all files in a directory as YAML\n                      into a target config node. All files are visited\n                      even if their extension is neither of .yml or .yaml.\n                      Files are visited in alphabetical order. \u003ctargetpath\u003e\n                      is optional, and defaults to the root level; ie,\n                      when \u003ctargetpath\u003e is omitted, then the YAML tree\n                      resulting from parsing \u003cvalidyaml\u003e is merged starting\n                      at the config tree's root node. Otherwise the tree\n                      from \u003cvalidyaml\u003e is merged starting at the config\n                      tree's node at \u003ctargetpath\u003e. \n  -sf, --set-foo      call setfoo() \n  -sb, --set-bar      call setbar() \n  -sf3 \u003cfoo3val\u003e, --set-foo3-val \u003cfoo3val\u003e\n                      call setfoo3() with a required arg \n  -sb2 [\u003cbar2val\u003e], --set-bar2-val [\u003cbar2val\u003e]\n                      call setbar2() with an optional arg\n\n\n# run with defaults, do not change anything\n$ quickstart\nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# change seq node values:\n$ quickstart -cn foo[1]=1.234e9 \nfoo:\n  - foo0\n  - 1.234e9\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# change map node values:\n$ quickstart -cn bar.bar2=newvalue \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: newvalue\nbaz: definitely\n\n\n# change values repeatedly. Later values prevail:\n$ quickstart -cn foo[1]=1.234e9 -cn foo[1]=2.468e9 \nfoo:\n  - foo0\n  - 2.468e9\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# append elements to a seq:\n$ quickstart -cn foo=[these,will,be,appended] \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\n  - these\n  - will\n  - be\n  - appended\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# append elements to a map:\n$ quickstart -cn bar='{these: will, be: appended}' \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\n  these: will\n  be: appended\nbaz: definitely\n\n\n# remove all elements in a seq, replace with different elements:\n$ quickstart -cn 'foo=~' -cn foo=[all,new] \nfoo:\n  - all\n  - new\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# remove all elements in a map, replace with different elements:\n$ quickstart -cn 'bar=~' -cn bar='{all: new}' \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  all: new\nbaz: definitely\n\n\n# replace a seq with a different type, eg val:\n$ quickstart -cn foo=newfoo \nfoo: newfoo\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# replace a map with a different type, eg val:\n$ quickstart -cn bar=newbar \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar: newbar\nbaz: definitely\n\n\n# add new nodes, eg seq:\n$ quickstart -cn coffee=[morning,lunch,afternoon] \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\ncoffee:\n  - morning\n  - lunch\n  - afternoon\n\n\n# add new nodes, eg map:\n$ quickstart -cn wine='{green: summer, red: winter, champagne: year-round}' \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\nwine:\n  green: summer\n  red: winter\n  champagne: 'year-round'\n\n\n# add new nested nodes to a seq:\n$ quickstart -cn foo[3]=[a,b,c] \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - - a\n    - b\n    - c\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# add new nested nodes to a map:\n$ quickstart -cn bar.possibly=[d,e,f] \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\n  possibly:\n    - d\n    - e\n    - f\nbaz: definitely\n\n\n# In seqs, target node indices do not need to be contiguous.\n# This will add a new seq nested in foo, and\n# foo[4] will also be created with a null:\n$ quickstart -cn foo[5]=[d,e,f] \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\n  - \n  - - d\n    - e\n    - f\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# NOTE:\n# All of -cn/-cf/-cd have an implied target node, which\n# defaults to the tree's root node. So take care if\n# omitting the target node; that will replace the whole\n# root node with the given value:\n$ quickstart -cn eraseall \neraseall\n\n\n# call setfoo():\n$ quickstart -sf \nfoo:\n  - foo0\n  - foo1\n  - 'foo2, actually footoo'\n  - - foo3elm0\n    - foo3elm1\n    - foo3elm2\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# call setfoo3(). The following arg is mandatory:\n$ quickstart -sf3 123 \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - 123\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n# this is equivalent to the previous example:\n$ quickstart -cn foo[3]=123 \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - 123\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: indeed2\nbaz: definitely\n\n\n# call setbar2():\n$ quickstart -sb2 'the value' \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: the value\nbaz: definitely\n\n# this is equivalent to the previous example:\n$ quickstart -cn 'bar.bar2=the value' \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: the value\nbaz: definitely\n\n\n# call setbar2(), with omitted arg:\n$ quickstart -sb2 \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: \nbaz: definitely\n\n# this is equivalent to the previous example:\n$ quickstart -cn 'bar.bar2=~' \nfoo:\n  - foo0\n  - foo1\n  - foo2\n  - foo3\nbar:\n  bar0: indeed0\n  bar1: indeed1\n  bar2: ~\nbaz: definitely\n```\n\nNotice above that tilde `~` (which in YAML is understood as the null value) is escaped with surrounding quotes when it is part of an argument. This is needed to prevent the shell from replacing `~` with the user's home dir before the executable is called.\n\n\n# License\n\nPermissively licensed with the [MIT License](./LICENSE.txt).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiojppm%2Fc4conf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbiojppm%2Fc4conf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiojppm%2Fc4conf/lists"}