Configuru
|
Configuru, an experimental JSON config library for C++, by Emil Ernerfeldt.
This software is in the public domain. Where that dedication is not recognized, you are granted a perpetual, irrevocable license to copy and modify this file as you see fit.
That being said, I would appreciate credit! If you find this library useful, send a tweet to or mail me at emil.. erne rfeld t@gm ail.c om
You can read the documentation here: https://emilk.github.io/Configuru/html/index.html
Configuru 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.
configuru::Config
behaves in your code via compile time constants:CONFIGURU_ONERROR
to add additional debug info (like stack traces) on errors.CONFIGURU_ASSERT
to use your own asserts.CONFIGURU_ON_DANGLING
to customize how non-referenced/dangling keys are reported.CONFIGURU_IMPLICIT_CONVERSIONS
to allow things like float f = some_config;
CONFIGURU_VALUE_SEMANTICS
to have Config
behave like a value type rather than a reference type.FormatOptions
they will be parsed and written together with the right key/value.Configuru 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.
equal_in_object.json:1:16: Expected : after object key { "is_this_ok" = true } ^ bad_escape.json:1:9: Unknown escape character 'x' {"42":"\x42"} ^ no_quotes.json:2:22: Expected value "forgotten_quotes": here ^ trucated_key.json:1:2: Unterminated string {"X ^ single_line_comment.json:1:4: Single line comments forbidden. {} // Blah-bla ^ unary_plus.json:1:1: Prefixing numbers with + is forbidden. +42 ^
Note 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).
Similarly, using a parsed Config value in the wrong way produces nice error messages. Take the following file (config.json
):
Here's some use errors and their error messages:
config.json:2: Expected bool, got float
. Note that the file:line points to where the value is defined.
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.
config.json:2: Expected array, got float
.
config.json:3: Array index out of range
Expected object, got uninitialized. Did you forget to call Config::object()?
. The first line should read Config cfg = Config::object();
.
Expected array, got uninitialized. Did you forget to call Config::array()?
. The first line should read Config cfg = Config::array();
.
Configuru has a novel mechanism for detecting subtle typos in object keys. Suppose you have a Config that looks like this:
{ "colour": "red", ... }
Here's how it could be used:
The call to check_dangling
will print a warning:
config.json:2: Key 'colour' never accessed
This is akin to a compiler warning about unused variables and it's an effective way of finding mistakes that would otherwise go undetected.
The 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.
For using: #include <configuru.hpp>
And in one .cpp file:
Alternative form:
By default, Config objects acts like reference types, e.g. like a std::shared_ptr
:
You can control this behavior with #define CONFIGURU_VALUE_SEMANTICS 1
:
The default behavior of Configuru is to throw an std::runtime_error
on any error. You can change this behavior by overriding CONFIGURU_ONERROR
.
In 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:
values: [1 2 3 4 5 6] object: { nested_key: +inf } python_style: """This is a string which spans many lines.""" "C# style": @"Also nice for \ and stuff"
"""
starts a verbatim multi-line string
@"</tt> starts a C# style verbatim string which ends on next quote (except <tt>""
which is a single-quote).
Numbers can be represented in any common form: -42
, 1e-32
, 0xCAFE
, 0b1010
+inf
, -inf
, +NaN
are valid numbers.
Indentation is enforced, and must be done with tabs. Tabs anywhere else is not allowed.
You 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.
One 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++):
Here's how the output turns out in most JSON encoders (this one produced by the excellent nlohmann json library):
In contrast, here's how the output looks in Configuru:
Note 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 (turn off with object_align_values=false
). Writing the same data in the CFG format makes it turn out like this:
float: 3.14 double: 3.14 short_array: [ 1 2 3 ] long_array: [ "one" [ "two" "things" ] "three" ]