{"id":13729797,"url":"https://github.com/emilk/Configuru","last_synced_at":"2025-05-08T02:30:46.250Z","repository":{"id":3648837,"uuid":"45793345","full_name":"emilk/Configuru","owner":"emilk","description":"Experimental config library for C++","archived":false,"fork":false,"pushed_at":"2022-03-10T07:01:43.000Z","size":600,"stargazers_count":143,"open_issues_count":9,"forks_count":28,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-05-07T10:17:00.071Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emilk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-08T18:36:50.000Z","updated_at":"2025-04-11T04:53:51.000Z","dependencies_parsed_at":"2022-08-06T14:01:20.138Z","dependency_job_id":null,"html_url":"https://github.com/emilk/Configuru","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilk%2FConfiguru","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilk%2FConfiguru/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilk%2FConfiguru/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilk%2FConfiguru/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emilk","download_url":"https://codeload.github.com/emilk/Configuru/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252986597,"owners_count":21836190,"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":[],"created_at":"2024-08-03T02:01:05.327Z","updated_at":"2025-05-08T02:30:45.892Z","avatar_url":"https://github.com/emilk.png","language":"C++","readme":"Configuru\n===============================================================================\nConfiguru, an experimental JSON config library for C++, by Emil Ernerfeldt.\n\n\nLicense\n-------------------------------------------------------------------------------\nThis software is in the public domain. Where that dedication is not\nrecognized, you are granted a perpetual, irrevocable license to copy\nand modify this file as you see fit.\n\nThat being said, I would appreciate credit!\nIf you find this library useful, send a tweet to [@ernerfeldt](https://twitter.com/ernerfeldt) or mail me at emil.ernerfeldt@gmail.com.\n\n\nDocumentation\n-------------------------------------------------------------------------------\nYou can read the documentation here: https://emilk.github.io/Configuru/html/index.html\n\n\nOverview\n-------------------------------------------------------------------------------\nConfiguru is a JSON parser/writer for C++11. Configuru was written for human created/edited config files and therefore prioritizes helpful error messages over parse speed.\n\n\nGoals\n-------------------------------------------------------------------------------\n* **Debugable**:\n\t* Find typos most config libs miss (like typos in keys).\n\t* Easily find source of typos with file name, line numbers and helpful error messages.\n\t* Cleverly help to point out mismatched braces in the right place.\n* **Configurable**:\n\t* Configure the format to allow relaxed and extended flavors of JSON.\n\t* Extensible with custom conversions.\n\t* Control how the `configuru::Config` behaves in your code via compile time constants:\n\t\t* Override `CONFIGURU_ONERROR` to add additional debug info (like stack traces) on errors.\n\t\t* Override `CONFIGURU_ASSERT` to use your own asserts.\n\t\t* Override `CONFIGURU_ON_DANGLING` to customize how non-referenced/dangling keys are reported.\n\t\t* Set `CONFIGURU_IMPLICIT_CONVERSIONS` to allow things like `float f = some_config;`\n\t\t* Set `CONFIGURU_VALUE_SEMANTICS` to have `Config` behave like a value type rather than a reference type.\n* **Easy to use**:\n\t* Smooth C++11 integration for reading and creating config values.\n* **JSON compliant**:\n\t* Configuru has one of the highest conformance ratings on the [Native JSON Benchmark](https://github.com/miloyip/nativejson-benchmark)\n* **Beautiful output** (pretty printing)\n* **Reversible with comments**:\n\t* If comments are turned on in `FormatOptions` they will be parsed and written together with the right key/value.\n\n### Non-goals\n* Low overhead\n\n\nError messages\n===============================================================================\nConfiguru prides itself on great error messages both for parse errors and for value errors (expecting one thing and getting another). All error messages come with file name a line number. When parsing from a string (rather than a file) the user can specify an identifier to be used in lieu of a file name.\n\n\nParse errors\n-------------------------------------------------------------------------------\n\n\tequal_in_object.json:1:16: Expected : after object key\n\t{ \"is_this_ok\" = true }\n\t               ^\n\n\tbad_escape.json:1:9: Unknown escape character 'x'\n\t{\"42\":\"\\x42\"}\n\t        ^\n\n\tno_quotes.json:2:22: Expected value\n\t   \"forgotten_quotes\": here\n\t                       ^\n\n\ttrucated_key.json:1:2: Unterminated string\n\t{\"X\n\t ^\n\n\tsingle_line_comment.json:1:4: Single line comments forbidden.\n\t{} // Blah-bla\n\t   ^\n\n\tunary_plus.json:1:1: Prefixing numbers with + is forbidden.\n\t+42\n\t^\n\nNote how all errors mention follow the standard `filename:line:column` structure (most errors above happen on line `1` since they are from small unit tests).\n\n\nValue errors\n-------------------------------------------------------------------------------\nSimilarly, using a parsed Config value in the wrong way produces nice error messages. Take the following file (`config.json`):\n\n```\n 1: {\n 2: \t\"pi\":    3.14,\n 3: \t\"array\": [ 1, 2, 3, 4 ],\n 4: \t\"obj\":   {\n 5: \t\t\"nested_value\": 42\n 6: \t}\n 7: }\n```\n\nHere's some use errors and their error messages:\n\n``` C++\nauto b = (bool)config[\"pi\"];\n```\n\n`config.json:2: Expected bool, got float`. Note that the file:line points to where the value is defined.\n\n``` C++\nstd::cout \u003c\u003c config[\"obj\"][\"does_not_exist\"];\n```\n\n`config.json:4: Failed to find key 'does_not_exist'`. Here the file and line of the owner (`\"obj\"`) of the missing value is referenced.\n\n``` C++\nstd::cout \u003c\u003c config[\"pi\"][5];\n```\n\n`config.json:2: Expected array, got float`.\n\n``` C++\nstd::cout \u003c\u003c config[\"array\"][5];\n```\n\n`config.json:3: Array index out of range`\n\n``` C++\nConfig cfg;\ncfg[\"hello\"] = 42;\n```\n\n`Expected object, got uninitialized. Did you forget to call Config::object()?`. The first line should read `Config cfg = Config::object();`.\n\n``` C++\nConfig cfg;\ncfg.push_back(\"hello\");\n```\n\n`Expected array, got uninitialized. Did you forget to call Config::array()?`. The first line should read `Config cfg = Config::array();`.\n\n\nUnused keys\n-------------------------------------------------------------------------------\nConfiguru has a novel mechanism for detecting subtle typos in object keys. Suppose you have a Config that looks like this:\n\n\t{\n\t\t\"colour\": \"red\",\n\t\t...\n\t}\n\nHere's how it could be used:\n\n``` C++\nauto cfg = configuru::parse_file(\"config.json\", configuru::JSON);\nauto color = cfg.get_or(\"color\", DEFAULT_COLOR);\ncfg.check_dangling();\n```\n\nThe call to `check_dangling` will print a warning:\n\n\tconfig.json:2: Key 'colour' never accessed\n\nThis is akin to a compiler warning about unused variables and it's an effective way of finding mistakes that would otherwise go undetected.\n\nThe call to `check_dangling` is recursive, so you only need to call it once for every config file. If you want to mute this warning for some key (which you may intentionally be ignoring, or saving for later) you can call `cfg.mark_accessed(true)`. This will recursively mark each `Config` as having been accessed.\n\n\nUsage\n===============================================================================\nFor using:\n\t`#include \u003cconfiguru.hpp\u003e`\n\nAnd in one .cpp file:\n\n``` C++\n#define CONFIGURU_IMPLEMENTATION 1\n#include \u003cconfiguru.hpp\u003e\n```\n\n\nUsage (parsing)\n-------------------------------------------------------------------------------\n``` C++\nConfig cfg = configuru::parse_file(\"input.json\", JSON);\nfloat alpha = (float)cfg[\"alpha\"];\nstd::cout \u003c\u003c \"alpha = \" \u003c\u003c alpha \u003c\u003c std::endl;\nif (cfg.has_key(\"beta\")) {\n\tstd::string beta = (std::string)cfg[\"beta\"];\n\tstd::cout \u003c\u003c \"beta = \" \u003c\u003c beta \u003c\u003c std::endl;\n}\nfloat pi = cfg.get_or(\"pi\", 3.14f);\nstd::cout \u003c\u003c \"pi = \" \u003c\u003c pi \u003c\u003c std::endl;\n\nif (cfg[\"array\"].is_array()) {\n\tstd::cout \u003c\u003c \"array:\" \u003c\u003c std::endl;\n\tfor (const Config\u0026 element : cfg[\"array\"].as_array()) {\n\t\tstd::cout \u003c\u003c \"\\t\" \u003c\u003c element \u003c\u003c std::endl;\n\t}\n}\n\nstd::cout \u003c\u003c \"object\" \u003c\u003c std::endl;\nfor (auto\u0026 p : cfg[\"object\"].as_object()) {\n\tstd::cout \u003c\u003c \"\\tKey: \" \u003c\u003c p.key() \u003c\u003c std::endl;\n\tstd::cout \u003c\u003c \"\\tValue: \" \u003c\u003c p.value() \u003c\u003c std::endl;\n\tp.value() = \"new value\";\n}\n\ntry {\n\tcfg.check_dangling(); // Make sure we haven't forgot reading a key!\n} catch (const std::exception \u0026e) {\n\tstd::cerr \u003c\u003c e.what() \u003c\u003c std::endl;\n}\n// You can modify the read config:\ncfg[\"message\"] = \"goodbye\";\n\ndump_file(\"output.json\", cfg, JSON);\n```\n\n\nUsage (writing)\n-------------------------------------------------------------------------------\n\n``` C++\nConfig cfg = Config::object();\ncfg[\"pi\"]     = 3.14;\ncfg[\"array\"]  = Config::array{ 1, 2, 3 };\ncfg[\"object\"] = Config::object({\n\t{ \"key1\", \"value1\" },\n\t{ \"key2\", \"value2\" },\n});\n```\n\nAlternative form:\n\n``` C++\nConfig cfg{\n\t{\"pi\",     3.14},\n\t{\"array\",  Config::array{ 1, 2, 3 }},\n\t{\"object\", {\n\t\t{ \"key1\", \"value1\" },\n\t\t{ \"key2\", \"value2\" },\n\t}},\n};\n```\n\n``` C++\nstd::string json = dump_string(cfg, JSON);\ndump_file(\"output.json\", cfg, JSON);\n```\n\n\nUsage (visit_struct.hpp)\n-------------------------------------------------------------------------------\nIf you include visit_struct.hpp from https://github.com/cbeck88/visit_struct *before* including configuru.hpp you will enable the following:\n\n``` C++\n#include \u003cvisit_struct/visit_struct.hpp\u003e\n#include \u003cconfiguru.hpp\u003e\n\nstruct Foo\n{\n\tfloat bar;\n\tstd::string baz;\n};\nVISITABLE_STRUCT(Foo, bar, baz);\n\nvoid error_reporter(std::string str)\n{\n\tstd::cerr \u003c\u003c str \u003c\u003c std::endl; // or throw or ignore\n}\n\nint main()\n{\n\tFoo foo{42, \"fortytwo\"};\n\tconfigur::Config cfg = configuru::serialize(foo);\n\t// Save/load cfg\n\tconfiguru::deserialize(\u0026foo, cfg, error_reporter);\n}\n```\n\nThe `serialize/deserialize` functions supports numbers, `bool`, `std::string`, `std::vector` and `struct`s annotated with `VISITABLE_STRUCT`.\nIt is recursive, so a `struct` can contain an `std::vector` of other `struct`s if both types of `struct`s are annotated with `VISITABLE_STRUCT`.\n\n\nReference semantics vs value semantics\n-------------------------------------------------------------------------------\nBy default, Config objects acts like reference types, e.g. like a `std::shared_ptr`:\n\n``` C++\nConfig cfg{{\"message\", \"original\"}};\nauto shallow_copy = cfg;\ncfg[\"message\"] = \"changed!\";\nstd::cout \u003c\u003c shallow_copy[\"message\"]; // Will print \"changed!\";\n\nauto deep_clone = cfg.deep_clone(); // Deep clones have to be explicit.\n```\n\nYou can control this behavior with `#define CONFIGURU_VALUE_SEMANTICS 1`:\n\n``` C++\n#define CONFIGURU_VALUE_SEMANTICS 1\n#include \u003cconfiguru.hpp\u003e\n...\nConfig cfg{{\"message\", \"original\"}};\nauto deep_clone = cfg;\ncfg[\"message\"] = \"changed!\";\nstd::cout \u003c\u003c deep_clone[\"message\"]; // Will print \"original\";\n```\n\n\nErrors\n-------------------------------------------------------------------------------\nThe default behavior of Configuru is to throw an `std::runtime_error` on any error. You can change this behavior by overriding `CONFIGURU_ONERROR`.\n\n\nCFG format\n===============================================================================\nIn addition to JSON, Configuru also has native support for a format I simply call *CFG*. CFG is a superset of JSON with some simplifications and extensions. Example file:\n\n\tvalues: [1 2 3 4 5 6]\n\tobject: {\n\t\tnested_key: +inf\n\t}\n\tpython_style: \"\"\"This is a string\n\t                 which spans many lines.\"\"\"\n\t\"C# style\": @\"Also nice for \\ and stuff\"\n\n* Top-level can be key-value pairs (no need for {} surrounding entire document).\n* Keys need not be quoted if identifiers.\n* Commas optional for arrays and objects.\n* Trailing , allowed in arrays and objects.\n\n`\"\"\"` starts a verbatim multi-line string\n\n`@\"` starts a C# style verbatim string which ends on next quote (except `\"\"` which is a single-quote).\n\nNumbers can be represented in any common form:\n`-42`, `1e-32`, `0xCAFE`, `0b1010`\n\n`+inf`, `-inf`, `+NaN` are valid numbers.\n\nIndentation is enforced, and must be done with tabs. Tabs anywhere else is not allowed.\n\nYou can also allow selective parts of the above extensions to create your own dialect of JSON. Look at the members of `configuru::FormatOptions` for details.\n\n\nBeautiful output\n===============================================================================\nOne of the great things about JSON is that it is human readable (as opposed to XML). Configuru goes to great lengths to make the output as beautiful as possible. Here's an example structure (as defined in C++):\n\n``` C++\nConfig cfg = Config::object{\n\t{\"float\",       3.14f},\n\t{\"double\",      3.14},\n\t{\"short_array\", Config::array({1, 2, 3})},\n\t{\"long_array\",  Config::array({\n\t\t\"one\",\n\t\tConfig::array({\"two\", \"things\"}),\n\t\t\"three\",\n\t})},\n};\n```\n\nHere's how the output turns out in most JSON encoders (this one produced by the excellent [nlohmann json library](https://github.com/nlohmann/json)):\n\n``` JSON\n{\n    \"double\": 3.14,\n    \"float\": 3.14000010490417,\n    \"long_array\": [\n        \"one\",\n        [\n            \"two\",\n            \"things\"\n        ],\n        \"three\"\n    ],\n    \"short_array\": [\n        1,\n        2,\n        3\n    ]\n}\n```\n\nIn contrast, here's how the output looks in Configuru:\n\n``` JSON\n{\n\t\"float\":       3.14,\n\t\"double\":      3.14,\n\t\"short_array\": [ 1, 2, 3 ],\n\t\"long_array\":  [\n\t\t\"one\",\n\t\t[ \"two\", \"things\" ],\n\t\t\"three\"\n\t]\n}\n```\n\nNote how Configuru refrains from unnecessary line breaks on short arrays and does not write superfluous (and ugly!) trailing decimals. Configuru also writes the keys of the objects in the same order as it was given (unless the `sort_keys` option is explicitly set). The aligned values is just a preference of mine, inspired by [how id software does it](http://kotaku.com/5975610/the-exceptional-beauty-of-doom-3s-source-code) (turn off with `object_align_values=false`). Writing the same data in the CFG format makes it turn out like this:\n\n\tfloat:       3.14\n\tdouble:      3.14\n\tshort_array: [ 1 2 3 ]\n\tlong_array:  [\n\t\t\"one\"\n\t\t[ \"two\" \"things\" ]\n\t\t\"three\"\n\t]\n","funding_links":[],"categories":["C++"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femilk%2FConfiguru","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femilk%2FConfiguru","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femilk%2FConfiguru/lists"}