toml11 ====== [![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11) [![Build status on Appveyor](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master) [![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master) [![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases) [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) toml11 is a C++11 header-only toml parser/encoder depending only on C++ standard library. compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) after version 2.0.0. It passes [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](https://circleci.com/gh/ToruNiina/toml11). You can see the error messages about invalid files and serialization results of valid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11). ## Example ```cpp #include #include int main() { const auto data = toml::parse("example.toml"); // title = "an example toml file" std::string title = toml::get(data.at("title")); std::cout << "the title is " << title << std::endl; // nums = [1, 2, 3, 4, 5] std::vector nums = toml::get>(data.at("nums")); std::cout << "the length of `nums` is" << nums.size() << std::endl; return 0; } ``` ## Table of Contents - [Integration](#integration) - [Decoding a toml file](#decoding-a-toml-file) - [In the case of syntax error](#in-the-case-of-syntax-error) - [Getting a toml value](#getting-a-toml-value) - [In the case of type error](#in-the-case-of-type-error) - [Getting an array](#getting-an-array) - [Getting a table](#getting-a-table) - [Dotted keys](#dotted-keys) - [Getting an array of tables](#getting-an-array-of-tables) - [Cost of conversion](#cost-of-conversion) - [Getting datetime and its variants](#getting-datetime-and-its-variants) - [Getting with a fallback](#getting-with-a-fallback) - [Expecting conversion](#expecting-conversion) - [Finding a value from a table](#finding-a-value-from-a-table) - [Checking value type](#checking-value-type) - [Visiting a toml::value](#visiting-a-tomlvalue) - [TOML literal](#toml-literal) - [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) - [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints) - [Formatting user-defined error messages](#formatting-user-defined-error-messages) - [Getting comments related to a value](#getting-comments) - [Serializing TOML data](#serializing-toml-data) - [Underlying types](#underlying-types) - [Running Tests](#running-tests) - [Contributors](#contributors) - [Licensing Terms](#licensing-terms) ## Integration Just include the file after adding it to the include path. ```cpp #include // that's all! now you can use it. #include int main() { const auto data = toml::parse("example.toml"); const auto title = toml::get(data.at("title")); std::cout << "the title is " << title << std::endl; return 0; } ``` ## Decoding a toml file To parse a toml file, the only thing you have to do is to pass a filename to the `toml::parse` function. ```cpp const std::string fname("sample.toml"); const toml::table data = toml::parse(fname); ``` If it encounters a file open error, it will throw `std::runtime_error`. You can also pass a `std::istream` to the `toml::parse` function. To show a filename in an error message, it is recommended to pass the filename with the stream. ```cpp std::ifstream ifs("sample.toml", std::ios_base::binary); assert(ifs.good()); const auto data = toml::parse(ifs, /*optional*/ "sample.toml"); ``` Note that on Windows, if a file is opened in text-mode, CRLF ("\r\n") will automatically be converted to LF ("\n") and this causes inconsistency between file size and the contents that would be read. This causes weird error. To use a file stream with `toml::parse` on Windows, don't forget to open it in binary mode. ### In the case of syntax error If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`. toml11 has clean and informative error messages inspired by Rust and it looks like the following. ```console terminate called after throwing an instance of 'toml::syntax_error' what(): [error] toml::parse_table: invalid line format # error description --> example.toml # file name 3 | a = 42 = true # line num and content | ^------ expected newline, but got '='. # error reason ``` If you (mistakenly) duplicate tables and got an error, it is helpful to see where they are. toml11 shows both at the same time like the following. ```console terminate called after throwing an instance of 'toml::syntax_error' what(): [error] toml::insert_value: table ("table") already exists. --> duplicate-table.toml 1 | [table] | ~~~~~~~ table already exists here ... 3 | [table] | ~~~~~~~ table defined twice ``` Since the error message generation is generally a difficult task, the current status is not ideal. If you encounter a weird error message, please let us know and contribute to improve the quality! ## Getting a toml value After parsing successfully, you can obtain the values from the result of `toml::parse` using `toml::get` function. ```toml # sample.toml answer = 42 pi = 3.14 numbers = [1,2,3] time = 1979-05-27T07:32:00Z [tab] key = "value" ``` ``` cpp const auto data = toml::parse("sample.toml"); const auto answer = toml::get(data.at("answer")); const auto pi = toml::get(data.at("pi")); const auto numbers = toml::get>(data.at("numbers")); const auto timepoint = toml::get(data.at("time")); const auto tab = toml::get(data.at("tab")); const auto key = toml::get( tab.at("key")); ``` When you pass an exact TOML type that does not require type conversion, `toml::get` returns a reference through which you can modify the content. ```cpp auto data = toml::parse("sample.toml"); auto& answer = toml::get(data["answer"]); // get reference answer = 6 * 9; // write to data.answer std::cout << toml::get(data.at("answer")) << std::endl; // 54 ``` If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types). NOTE: To enable to get a reference, conversions between Float and Integer are not supported. After C++17, you can use `std::string_view` to get a string from a `toml::value`. ```cpp const auto sv = toml::get(tab.at("key")); ``` ### In the case of type error If you pass an invalid type to `toml::get`, `toml::type_error` will be thrown. Similar to the case of syntax error, toml11 also displays clean error messages. The error message when you choose `int` to get `string` value would be like this. ```console terminate called after throwing an instance of 'toml::type_error' what(): [error] toml::value bad_cast to integer --> example.toml 3 | title = "TOML Example" | ~~~~~~~~~~~~~~ the actual type is string ``` NOTE: In order to show this kind of error message, all the toml values have pointers to represent its range in a file. The entire contents of a file is shared by `toml::value`s and remains on the heap memory. It is recommended to destruct all the `toml::value` classes after configuring your application if you have a large TOML file compared to the memory resource. ## Getting an array You can get any kind of `container` class from a `toml::array` except for `map`-like classes. ``` cpp // # sample.toml // numbers = [1,2,3] const auto vc = toml::get >(data.at("numbers")); const auto ls = toml::get >(data.at("numbers")); const auto dq = toml::get >(data.at("numbers")); const auto ar = toml::get>(data.at("numbers")); // if the size of data.at("numbers") is larger than that of std::array, // it will throw toml::type_error because std::array is not resizable. ``` Surprisingly, you can also get `std::pair`s and `std::tuple`s from `toml::array`. ```cpp const auto tp = toml::get>(data.at("numbers")); ``` This functionality is helpful when you have the following toml file. ```toml array_of_arrays = [[1, 2, 3], ["foo", "bar", "baz"]] # toml allows this ``` What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vector`s. ```cpp const auto aofa = toml::get< std::pair, std::vector> >(data.at("array_of_arrays")); ``` If you don't know the type of the elements, you can use `toml::array`, which is a `std::vector` of `toml::value`, instead. ```cpp const auto aofa = toml::get(data.at("array_of_arrays")); const auto first = toml::get>(aofa.at(0)); ``` See also [expecting conversion](#expecting-conversion) and [checking-value-type](#checking-value-type). ## Getting a table `toml::table` is a key component of this library, which is an alias of a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`. `toml::parse` returns this. Since it is just an alias of `std::unordered_map`, it has all the functionalities that `std::unordered_map` has, e.g. `operator[]`, `count`, and `find`. ```cpp toml::table data = toml::parse("example.toml"); if(data.count("title") != 0) { data["title"] = std::string("TOML example"); } ``` When all the values of the table have the same type, toml11 allows you to convert a `toml::table` to a `map` that contains the convertible type. ```toml [tab] key1 = "foo" # all the values are key2 = "bar" # toml String ``` ```cpp const auto data = toml::parse("sample.toml"); const auto tab = toml::get>(data.at("tab")); std::cout << tab["key1"] << std::endl; // foo std::cout << tab["key2"] << std::endl; // bar ``` ## Dotted keys TOML v0.5.0 has a new feature named "dotted keys". You can chain keys to represent the structure of the data. ```toml physical.color = "orange" physical.shape = "round" ``` This is equivalent to the following. ```toml [physical] color = "orange" shape = "round" ``` You can get both of the above formats with the same c++ code. ```cpp const auto physical = toml::get(data.at("physical")); const auto color = toml::get(physical.at("color")); ``` ## Getting an array of tables An array of tables is just an array of tables. You can get it completely in the same way as the other arrays and tables. ```toml # sample.toml array_of_inline_tables = [{key = "value1"}, {key = "value2"}, {key = "value3"}] [[array_of_tables]] key = "value4" [[array_of_tables]] key = "value5" [[array_of_tables]] key = "value6" ``` ```cpp const auto data = toml::parse("sample.toml"); const auto aot1 = toml::get>(data.at("array_of_inline_tables")); const auto aot2 = toml::get>(data.at("array_of_tables")); ``` ## Cost of conversion Although `toml::get` is convenient, it has additional copy-cost because it copies data contained in `toml::value` to the user-specified type. Of course in some cases this overhead is not ignorable. ```cpp // the following code constructs a std::vector. // it requires heap allocation for vector and element conversion. const auto array = toml::get>(data.at("foo")); ``` By passing the exact types, `toml::get` returns reference that has no overhead. ``` cpp const auto& tab = toml::get(data.at("tab")); const auto& numbers = toml::get(data.at("numbers")); ``` In this case you need to call `toml::get` each time you access to the element of `toml::array` because `toml::array` is an array of `toml::value`. ```cpp const auto& num0 = toml::get(numbers.at(0)); const auto& num1 = toml::get(numbers.at(1)); const auto& num2 = toml::get(numbers.at(2)); ``` ## Getting datetime and its variants TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, `local_datetime`, and `offset_datetime`. Since `local_date`, `local_datetime`, and `offset_datetime` represent a time point, you can convert them to `std::chrono::system_clock::time_point`. Contrary, `local_time` does not represents a time point because they lack a date information, but it can be converted to `std::chrono::duration` that represents a duration from the beginning of the day, `00:00:00.000`. ```toml date = 2018-12-23 time = 12:30:00 l_dt = 2018-12-23T12:30:00 o_dt = 2018-12-23T12:30:00+09:30 ``` ```cpp const auto data = toml::parse("sample.toml"); const auto date = toml::get(data.at("date")); const auto l_dt = toml::get(data.at("l_dt")); const auto o_dt = toml::get(data.at("o_dt")); const auto time = toml::get(data.at("time")); // 12 * 60 + 30 min ``` toml11 contains datetime as its own struct. You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp). ## Getting with a fallback `toml::get_or` returns a default value if `toml::get` failed. ```cpp toml::value v("foo"); // v contains String const int value = toml::get_or(v, 42); // conversion fails. it returns 42. ``` `toml::get_or` automatically deduces what type you want to get from the default value you passed. To get a reference through this function, take care about the default value. ```cpp toml::value v("foo"); // v contains String toml::integer& i = toml::get_or(v, 42); // does not work because binding `42` // to `integer&` is invalid toml::integer opt = 42; toml::integer& i = toml::get_or(v, opt); // this works. ``` ## Expecting conversion By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`. ```cpp const auto value = toml::expect(data.at("title")); if(value.is_ok()) { std::cout << value.unwrap() << std::endl; } else { std::cout << value.unwrap_err() << std::endl; } ``` Also, you can pass a function object to modify the expected value. ```cpp const auto value = toml::expect(data.at("number")) .map(// function that receives expected type (here, int) [](const int number) -> double { return number * 1.5 + 1.0; }).unwrap_or(/*default value =*/ 3.14); ``` ## Finding a value from a table toml11 provides a utility function to find a value from `toml::value` and `toml::table`. ```cpp const toml::value data = /* ... */; // find a value named "num" from `data`. const auto num = toml::find(data, "num"); ``` When you pass a `toml::value`, `toml::find` first casts it to `toml::table`. If casting failed, `toml::type_error` will be thrown. When the value does not exist, it throws `std::out_of_range` with an error message. By passing a `toml::value`, it shows an informative error message like the following. ```console terminate called after throwing an instance of 'std::out_of_range' what(): [error] key "num" not found --> example.toml 3 | [table] | ~~~~~~~ in this table ``` Contrary, since `toml::table` is just an alias of `std::unordered_map`, you need to pass a name to the function to show the name in the exception with `toml::table`. ```cpp const toml::table data = /* ... */; // you need to pass the name of the table to show it in an error message const auto num = toml::find(data, "num", "[data]"); ``` ```console terminate called after throwing an instance of 'std::out_of_range' what(): [error] key "num" not found in [data] # table name is needed to show this part ^^^^^^ ``` By default (w/o template parameter), `toml::find` returns a `toml::value`. ```cpp const toml::value& subtable = toml::find(table, "subtable"); ``` __NOTE__: A new feature, recursive toml::find was planned to be introduced, but it was found that the change breaks a code that was previously compiled fine. So the change was reverted. The reason is that the overload resolution was ambiguous. To support this, in the next major update, overloads of `toml::find` for `toml::table` possibly be removed. ## Checking value type You can check what type of value does `toml::value` contains by `is_*` function. ```cpp toml::value v = /* ... */; if(v.is_integer()) { std::cout << "value is an integer" << std::endl; } ``` The complete list of the functions is below. ```cpp namespace toml { class value { // ... bool is_boolean() const noexcept; bool is_integer() const noexcept; bool is_floating() const noexcept; bool is_string() const noexcept; bool is_offset_datetime() const noexcept; bool is_local_datetime() const noexcept; bool is_local_date() const noexcept; bool is_local_time() const noexcept; bool is_array() const noexcept; bool is_table() const noexcept; bool is_uninitialized() const noexcept; // ... }; } // toml ``` __NOTE__: `is_float` is marked as deprecated since v2.4.0 to make the function names consistent with snake case typenames. Please use `is_floating` instead. Also, you can get `enum class` value from `toml::value`. ```cpp switch(data.at("something").type()) { case toml::value_t::Integer: /*do some stuff*/ ; break; case toml::value_t::Float : /*do some stuff*/ ; break; case toml::value_t::String : /*do some stuff*/ ; break; default : throw std::runtime_error( "unexpected type : " + toml::stringize(data.at("something").type())); } ``` The complete list of the `enum`s can be found in the section [underlying types](#underlying-types). The `enum`s can be used as a parameter of `toml::value::is` function like the following. ```cpp toml::value v = /* ... */; if(v.is(toml::value_t::Boolean)) // ... ``` ## Casting value So far, `toml::get` is introduced, but if you don't need any type conversion, `as_*` is simpler to use. ```cpp toml::value v = /* ... */; if(v.is_integer() && v.as_integer() == 42) { std::cout << "value is 42" << std::endl; } ``` `as_*` functions internally checks the current contained type for safety and throws `toml::type_error` if the contained value type is different (after toml11 v2.4.0). If you already confirmed that the value has the type you will cast into, you can skip the additional checking by passing `std::nothrow` object to it. ```cpp toml::value v = /* ... */; if(v.is_integer() && v.as_integer(std::nothrow) == 42) // never fail { std::cout << "value is 42" << std::endl; } ``` The full list of the functions is below. ```cpp namespace toml { class value { // ... const boolean& as_boolean() const&; const integer& as_integer() const&; const floating& as_floating() const&; const string& as_string() const&; const offset_datetime& as_offset_datetime() const&; const local_datetime& as_local_datetime() const&; const local_date& as_local_date() const&; const local_time& as_local_time() const&; const array& as_array() const&; const table& as_table() const&; // -------------------------------------------------------- // non-const version boolean& as_boolean() &; // ditto... // -------------------------------------------------------- // rvalue version boolean&& as_boolean() &&; // ditto... // -------------------------------------------------------- // noexcept versions ... const boolean& as_boolean(const std::nothrow_t&) const& noexcept; boolean& as_boolean(const std::nothrow_t&) & noexcept; boolean&& as_boolean(const std::nothrow_t&) && noexcept; // ditto... }; } // toml ``` __NOTE__: `as_float` is marked as deprecated since v2.4.0 to make the function names consistent with snake case typenames. Please use `as_floating` instead. ## Visiting a toml::value toml11 provides `toml::visit` to apply a function to `toml::value` in the same way as `std::variant`. ```cpp const toml::value v(3.14); toml::visit([](const auto& val) -> void { std::cout << val << std::endl; }, v); ``` The function object that would be passed to `toml::visit` must be able to recieve all the possible TOML types. Also, the result types should be the same each other. ## TOML literal toml11 supports `"..."_toml` literal. It accept both a bare value and a file content. ```cpp using namespace toml::literals::toml_literals; // `_toml` can convert a bare value without key const toml::value v = u8"0xDEADBEEF"_toml; // v is an Integer value containing 0xDEADBEEF. // raw string literal (`R"(...)"` is useful for this purpose) const toml::value t = u8R"( title = "this is TOML literal" [table] key = "value" )"_toml; // the literal will be parsed and the result will be contained in t ``` The literal function is defined in the same way as the standard library literals such as `std::literals::string_literals::operator""s`. ```cpp namespace toml { inline namespace literals { inline namespace toml_literals { toml::value operator""_toml(const char* str, std::size_t len); } // toml_literals } // literals } // toml ``` Access to the operator can be gained with `using namespace toml::literals;`, `using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`. Note that a key that is composed only of digits is allowed in TOML. And, unlike the file parser, toml-literal allows a bare value without a key. Thus it is difficult to distinguish arrays having integers and definitions of tables that are named as digits. Currently, literal `[1]` becomes a table named "1". To ensure a literal to be considered as an array with one element, you need to add a comma after the first element (like `[1,]`). ```cpp "[1,2,3]"_toml; // This is an array "[table]"_toml; // This is a table that has an empty table named "table" inside. "[[1,2,3]]"_toml; // This is an array of arrays "[[table]]"_toml; // This is a table that has an array of tables inside. "[[1]]"_toml; // This literal is ambiguous. // Currently, it becomes a table that has array of table "1". "1 = [{}]"_toml; // This is a table that has an array of table named 1. "[[1,]]"_toml; // This is an array of arrays. "[[1],]"_toml; // ditto. ``` ## Conversion between toml value and arbitrary types You can also use `toml::get` and other related functions with the types you defined after you implement some stuff. ```cpp namespace ext { struct foo { int a; double b; std::string c; }; } // ext const auto data = toml::parse("example.toml"); const foo f = toml::get(data.at("foo")); ``` There are 2 ways to use `toml::get` with the types that you defined. The first one is to implement `from_toml(const toml::value&)` member function. ```cpp namespace ext { struct foo { int a; double b; std::string c; void from_toml(const toml::value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); this->c = toml::find(v, "c"); return; } }; } // ext ``` In this way, because `toml::get` first constructs `foo` without arguments, the type should be default-constructible. The second is to implement specialization of `toml::from` for your type. ```cpp namespace ext { struct foo { int a; double b; std::string c; }; } // ext namespace toml { template<> struct from { ext::foo from_toml(const toml::value& v) { ext::foo f; f.a = toml::find(v, "a"); f.b = toml::find(v, "b"); f.c = toml::find(v, "c"); return f; } }; } // toml ``` In this way, since the conversion function is defined outside of the class, you can add conversion between `toml::value` and classes defined in another library. Note that you cannot implement both of the functions because the overload resolution of `toml::get` will be ambiguous. ---- The opposite direction is also supported in a similar way. You can directly pass your type to `toml::value`'s constructor by introducing `into_toml` or `toml::into`. ```cpp namespace ext { struct foo { int a; double b; std::string c; toml::table into_toml() const // you need to mark it const. { return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}}; } }; } // ext ext::foo f{42, 3.14, "foobar"}; toml::value v(f); ``` The definition of `toml::into` is similar to `toml::from`. ```cpp namespace ext { struct foo { int a; double b; std::string c; }; } // ext namespace toml { template<> struct into { toml::table into_toml(const ext::foo& f) { return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}}; } }; } // toml ext::foo f{42, 3.14, "foobar"}; toml::value v(f); ``` Any type that can be converted to `toml::value`, e.g. `toml::table`, `toml::array`, is okay to return from `into_toml`. ## Invalid UTF-8 codepoints toml11 throws `syntax_error` if a value of an escape sequence representing unicode character is not a valid UTF-8 codepoint. ```console what(): [error] toml::read_utf8_codepoint: input codepoint is too large. --> utf8.toml 1 | exceeds_unicode = "\U0011FFFF example" | ^--------- should be in [0x00..0x10FFFF] ``` ## Formatting user-defined error messages When you encounter an error after you read the toml value, you may want to show the error with the value. toml11 provides you a function that formats user-defined error message with related values. With a code like the following, ```cpp const auto value = toml::find(data, "num"); if(value < 0) { std::cerr << toml::format_error("[error] value should be positive", data.at("num"), "positive number required") << std::endl; } ``` you will get an error message like this. ```console [error] value should be positive --> example.toml 3 | num = -42 | ~~~ positive number required ``` When you pass two values to `toml::format_error`, ```cpp const auto min = toml::find(range, "min"); const auto max = toml::find(range, "max"); if(max < min) { std::cerr << toml::format_error("[error] max should be larger than min", data.at("min"), "minimum number here", data.at("max"), "maximum number here"); << std::endl; } ``` you will get an error message like this. ```console [error] max should be larger than min --> example.toml 3 | min = 54 | ~~ minimum number here ... 4 | max = 42 | ~~ maximum number here ``` ## Getting comments Since toml11 keeps a file data until all the values are destructed, you can also extract a comment related to a value by calling `toml::value::comment()`. If there is a comment just after a value (within the same line), you can get the specific comment by `toml::value::comment_inline()`. If there are comments just before a value (without any newline between them), you can get the comments by `toml::value::comment_before()`. `toml::value::comment()` returns the results of both functions after concatenating them. ```toml a = 42 # comment for a. # comment for b. # this is also a comment for b. b = "foo" c = [ # comment for c. 3.14, # this is not a comment for c, but for 3.14. ] # this is also a comment for c. ``` ```cpp // "# comment for a." const std::string com1 = toml::find(data, "a").comment(); // "# comment for b." const std::string com2 = toml::find(data, "b").comment(); // "# comment for c.\n# this is also a comment for c." const std::string com3 = toml::find(data, "c").comment(); // "# this is not a comment for c, but for 3.14." const std::string com3 = toml::find(data, "c").front().comment(); ``` Note that once a data in a value is modified, the related file region information would be deleted. Thus after modifying a data, you cannot find any comments. Also note that currently it does not support any way to set a comment to a value. And currently, serializer does not take comments into account. ## Serializing TOML data toml11 (after v2.1.0) enables you to serialize data into toml format. ```cpp const auto data = toml::table{{"foo", 42}, {"bar", "baz"}}; std::cout << data << std::endl; // bar = "baz" // foo = 42 ``` toml11 automatically makes a small table and small array inline. You can specify the width to make them inline by `std::setw` for streams. ```cpp const auto data = toml::table{ {"qux", toml::table{{"foo", 42}, {"bar", "baz"}}}, {"quux", toml::array{"small", "array", "of", "strings"}}, {"foobar", toml::array{"this", "array", "of", "strings", "is", "too", "long", "to", "print", "into", "single", "line", "isn't", "it?"}}, }; // the threshold becomes 80. std::cout << std::setw(80) << data << std::endl; // foobar = [ // "this","array","of","strings","is","too","long","to","print","into", // "single","line","isn't","it?", // ] // quux = ["small","array","of","strings"] // qux = {bar="baz",foo=42} // the width is 0. nothing become inline. std::cout << std::setw(0) << data << std::endl; // foobar = [ // "this", // ... (snip) // "it?", // ] // quux = [ // "small", // "array", // "of", // "strings", // ] // [qux] // bar = "baz" // foo = 42 ``` It is recommended to set width before printing data. Some I/O functions changes width to 0, and it makes all the stuff (including `toml::array`) multiline. The resulting files becomes too long. To control the precision of floating point numbers, you need to pass `std::setprecision` to stream. ```cpp const auto data = toml::table{ {"pi", 3.141592653589793}, {"e", 2.718281828459045} }; std::cout << std::setprecision(17) << data << std::endl; // e = 2.7182818284590451 // pi = 3.1415926535897931 std::cout << std::setprecision( 7) << data << std::endl; // e = 2.718282 // pi = 3.141593 ``` There is another way to format toml values, `toml::format()`. It returns `std::string` that represents a value. ```cpp const toml::value v{{"a", 42}}; const std::string fmt = toml::format(v); // a = 42 ``` Note that since `toml::format` formats a value, the resulting string may lack the key value. ```cpp const toml::value v{3.14}; const std::string fmt = toml::format(v); // 3.14 ``` To control the width and precision, `toml::format` receives optional second and third arguments to set them. By default, the witdh is 80 and the precision is `std::numeric_limits::max_digit10`. ```cpp const auto serial = toml::format(data, /*width = */ 0, /*prec = */ 17); ``` ## Underlying types The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below. | toml::type | underlying c++ type | enum | | -------------- | -------------------------------------------- | ------------------------------- | | Boolean | `bool` | `toml::value_t::Boolean` | | Integer | `std::int64_t` | `toml::value_t::Integer` | | Float | `double` | `toml::value_t::Float` | | String | `toml::string` | `toml::value_t::String` | | LocalDate | `toml::local_date` | `toml::value_t::LocalDate` | | LocalTime | `toml::local_time` | `toml::value_t::LocalTime` | | LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` | | OffsetDatetime | `toml::offset_datetime` | `toml::value_t::OffsetDatetime` | | Array | `std::vector` | `toml::value_t::Array` | | Table | `std::unordered_map` | `toml::value_t::Table` | `toml::string` is effectively the same as `std::string` but has an additional flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. Although `std::string` is not an exact toml type, still you can get a reference that points to internal `std::string` by using `toml::get()` for convenience. `Datetime` variants are `struct` that are defined in this library. Because `std::chrono::system_clock::time_point` is a __time point__, not capable of representing a Local Time independent from a specific day. It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`. ## Running Tests To run test codes, you need to clone toml-lang/toml repository under `build/` directory because some of the test codes read a file in the repository. ```sh $ mkdir build $ cd build $ git clone https://github.com/toml-lang/toml.git $ cmake .. $ make $ make test ``` To run the language agnostic test suite, you need to compile `tests/check_toml_test.cpp` and pass it to the tester. ## Contributors I appreciate the help of the contributors who introduced the great feature to this library. - Guillaume Fraux (@Luthaf) - Windows support and CI on Appvayor - Intel Compiler support - Quentin Khan (@xaxousis) - Found & Fixed a bug around ODR - Improved error messages for invaild keys to show the location where the parser fails - Petr Beneš (@wbenny) - Fixed warnings on MSVC - Ivan Shynkarenka (@chronoxor) - Fixed Visual Studio 2019 warnings - @khoitd1997 - Fixed warnings while type conversion ## Licensing terms This product is licensed under the terms of the [MIT License](LICENSE). - Copyright (c) 2017-2019 Toru Niina All rights reserved.