mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-12-16 03:08:52 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8707d5867 | ||
|
|
2dd0a78c52 | ||
|
|
d7b8c3c78f | ||
|
|
2f0148a2df | ||
|
|
4accc29984 | ||
|
|
19b9af2494 | ||
|
|
0aa50e9439 | ||
|
|
a00a906482 | ||
|
|
19ad7d7c96 | ||
|
|
251e55da42 | ||
|
|
32f1b2060a | ||
|
|
b1c54532df | ||
|
|
38c67f16e8 | ||
|
|
24aefc52a1 | ||
|
|
ba8c205253 | ||
|
|
31193d99ba | ||
|
|
4bbe42d105 | ||
|
|
5bdc022627 | ||
|
|
41e354f1ee | ||
|
|
d1c76709b0 | ||
|
|
64774a8db0 | ||
|
|
51dd3abcae | ||
|
|
825b2c30a1 |
59
PROPOSAL.md
59
PROPOSAL.md
@@ -1,59 +0,0 @@
|
||||
|
||||
### encoding user's data
|
||||
|
||||
You can encode your data to toml format.
|
||||
|
||||
```cpp
|
||||
const toml::value integer(1);
|
||||
const toml::value array{3.1, 3.14, 3.141, 3.1415};
|
||||
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
|
||||
|
||||
std::cout << toml::format("integer", integer) << std::endl;
|
||||
std::cout << toml::format("array", array) << std::endl;
|
||||
std::cout << toml::format("table", table) << std::endl;
|
||||
```
|
||||
|
||||
this program will output as below.
|
||||
|
||||
```toml
|
||||
integer = 1
|
||||
array = [3.1, 3.14, 3.141, 3.1415]
|
||||
[table]
|
||||
answer = 42
|
||||
pi = 3.14
|
||||
string = "foobar"
|
||||
```
|
||||
|
||||
Without key name, you can make string formatted as toml.
|
||||
|
||||
```cpp
|
||||
const std::string integer_ = toml::format(integer); // "1"
|
||||
const std::string array_ = toml::format(array); // "[3.1, 3.14, 3.141, 3.1415]"
|
||||
const std::string table_ = toml::format(table); // "answer = 42\npi=3.14\nstring=foobar"
|
||||
```
|
||||
|
||||
### inlinize
|
||||
|
||||
You can make `toml::Table` inline.
|
||||
|
||||
```cpp
|
||||
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
|
||||
// if the inline-table format length is less than 80, the table will be inlined
|
||||
std::cout << toml::format("table", table, toml::make_inline(80)) << std::endl;
|
||||
// In any case, the table will be inlined.
|
||||
std::cout << toml::format("table", table, toml::forceinline) << std::endl;
|
||||
```
|
||||
|
||||
```toml
|
||||
table = {answer = 42, pi = 3.14, string = "foobar"}
|
||||
```
|
||||
|
||||
And there are some stream manipulators for toml format.
|
||||
|
||||
```cpp
|
||||
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
|
||||
// if the inline-table format length is less than 80, the table will be inlined
|
||||
std::cout << toml::make_inline(80) << table << std::endl;
|
||||
// In any case, the table will be inlined.
|
||||
std::cout << toml::forceinline << table << std::endl;
|
||||
```
|
||||
206
README.md
206
README.md
@@ -9,9 +9,13 @@ toml11
|
||||
|
||||
C++11 header-only toml parser 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.
|
||||
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.
|
||||
|
||||
Are you looking for pre-C++11 compatible toml parser? Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and works with C++98 & Boost.
|
||||
Are you looking for pre-C++11 compatible toml parser?
|
||||
Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)!
|
||||
It has almost the same functionality as this library and works with C++98 & Boost.
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -55,13 +59,16 @@ assert(ifs.good());
|
||||
const auto data = toml::parse(ifs /*, "filename" (optional)*/);
|
||||
```
|
||||
|
||||
To show a better error message, it is recommended to pass a filename with `istream`. See also [in the case of syntax error](#in-the-case-of-syntax-error) and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget).
|
||||
To show a better error message, it is recommended to pass a filename with `istream`.
|
||||
See also [in the case of syntax error](#in-the-case-of-syntax-error)
|
||||
and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget).
|
||||
|
||||
### In the case of syntax error
|
||||
|
||||
If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`.
|
||||
|
||||
toml11 now has clean and informative error messages inspired by Rust and it looks like the following (comment after hash sign are actually not shown).
|
||||
toml11 now has clean and informative error messages inspired by Rust and
|
||||
it looks like the following (comment after hash sign are actually not shown).
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'toml::syntax_error'
|
||||
@@ -71,7 +78,8 @@ terminate called after throwing an instance of 'toml::syntax_error'
|
||||
| ^------ expected newline, but got '='. # error reason
|
||||
```
|
||||
|
||||
If you (mistakenly) duplicate tables and got an error, you may want to see where the other is. toml11 shows both at the same time.
|
||||
If you (mistakenly) duplicate tables and got an error, you may want to see
|
||||
where the other is. toml11 shows both at the same time.
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'toml::syntax_error'
|
||||
@@ -84,11 +92,14 @@ terminate called after throwing an instance of 'toml::syntax_error'
|
||||
| ~~~~~~~ table defined twice
|
||||
```
|
||||
|
||||
Since the error message generation is generally a difficult task, the current status is not ideal. toml11 needs your help. If you encounter a weird error message, please let us know and contribute to improve the quality!
|
||||
Since the error message generation is generally a difficult task, the current
|
||||
status is not ideal. toml11 needs your help. 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` (here, `data`) using `toml::get` function.
|
||||
After parsing successfully, you can obtain the values from the result of
|
||||
`toml::parse` (here, `data`) using `toml::get` function.
|
||||
|
||||
```toml
|
||||
answer = 42
|
||||
@@ -108,18 +119,22 @@ const auto tab = toml::get<toml::Table>(data.at("tab"));
|
||||
const auto key = toml::get<std::string>( tab.at("key"));
|
||||
```
|
||||
|
||||
When you pass an exact TOML type that does not require type conversion, `toml::get` returns also a reference through which you can modify the content.
|
||||
When you pass an exact TOML type that does not require type conversion,
|
||||
`toml::get` returns also a reference through which you can modify the content.
|
||||
|
||||
```cpp
|
||||
toml::get<toml::integer>(data["answer"]) = 6 * 9;
|
||||
std::cout << toml::get<int>(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).
|
||||
If the specified type requires conversion, you can't take a reference to the value.
|
||||
See also [underlying types](#underlying-types).
|
||||
|
||||
#### Passing invalid type to toml::get
|
||||
|
||||
If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error messages. The error message when you choose `int` to get `string` value would be like this.
|
||||
If you choose the invalid type, `toml::type_error` will be thrown.
|
||||
Similar to the `syntax_error`, toml11 also displays informative 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'
|
||||
@@ -129,7 +144,9 @@ terminate called after throwing an instance of 'toml::type_error'
|
||||
| ~~~~~~~~~~~~~~ the actual type is string
|
||||
```
|
||||
|
||||
NOTE: In order to show this kind of error message, all the toml values have 1 shared_ptr that points the corresponding byte sequence and 2 iterators that point the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save memory resources.
|
||||
NOTE: In order to show this kind of error message, all the toml values have 1
|
||||
`shared_ptr` that points the corresponding byte sequence and 2 iterators that point the range.
|
||||
It is recommended to destruct all the `toml::value` classes after configuring your application to save memory resources.
|
||||
|
||||
### Getting arrays
|
||||
|
||||
@@ -164,20 +181,25 @@ const auto aofa = toml::get<
|
||||
>(data.at("aofa"));
|
||||
```
|
||||
|
||||
If you don't know what the type is inside the array, you can use `toml::array`, which is a `std::vector` of `toml::value`, instead.
|
||||
If you don't know what the type is inside the array, you can use `toml::array`,
|
||||
which is a `std::vector` of `toml::value`, instead.
|
||||
|
||||
```cpp
|
||||
const auto aofa = toml::get<toml::array>(data.at("aofa"));
|
||||
const auto first = toml::get<toml::array>(aofa.at(0));
|
||||
```
|
||||
|
||||
See also [expecting conversion](#expecting-conversion) and [checking-value-type](#checking-value-type).
|
||||
See also [expecting conversion](#expecting-conversion)
|
||||
and [checking-value-type](#checking-value-type).
|
||||
|
||||
### Getting tables
|
||||
|
||||
`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 as a result.
|
||||
`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`.
|
||||
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");
|
||||
@@ -187,7 +209,8 @@ if(data.count("title") != 0)
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
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]
|
||||
@@ -203,7 +226,8 @@ 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 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"
|
||||
@@ -227,7 +251,8 @@ const auto color = toml::get<std::string>(physical.at("color"));
|
||||
|
||||
### 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.
|
||||
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
|
||||
array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}]
|
||||
@@ -247,7 +272,9 @@ const auto aot2 = toml::get<std::vector<toml::table>>(data.at("array_of_table"))
|
||||
|
||||
### 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 case this overhead is not ignorable.
|
||||
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 case this overhead is not ignorable.
|
||||
|
||||
By passing the exact types, `toml::get` returns reference that has nealy zero overhead.
|
||||
|
||||
@@ -256,7 +283,8 @@ const auto& tab = toml::get<toml::array>(data.at("tab"));
|
||||
const auto& numbers = toml::get<toml::table>(data.at("numbers"));
|
||||
```
|
||||
|
||||
Unfortunately, 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`.
|
||||
Unfortunately, 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<toml::integer>(numbers.at(0));
|
||||
@@ -266,7 +294,9 @@ const auto& num2 = toml::get<toml::integer>(numbers.at(2));
|
||||
|
||||
### Datetime and its variants
|
||||
|
||||
TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, `local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time` to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`.
|
||||
TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`,
|
||||
`local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time`
|
||||
to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`.
|
||||
|
||||
```toml
|
||||
time = 12:30:00
|
||||
@@ -291,7 +321,8 @@ const auto value = toml::get_or(data, "key", 42); // value => int 42.
|
||||
|
||||
### Expecting conversion
|
||||
|
||||
By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`.
|
||||
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<std::string>(data.at("title"));
|
||||
@@ -314,7 +345,9 @@ const auto value = toml::expect<int>(data.at("number"))
|
||||
|
||||
### Finding value from table
|
||||
|
||||
toml11 provides utility function to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches an `unordered_map` and returns a value if it exists.
|
||||
toml11 provides utility function to find a value from `toml::table`.
|
||||
Of course, you can do this in your own way with `toml::get` because
|
||||
it just searches an `unordered_map` and returns a value if it exists.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::parse("example.toml");
|
||||
@@ -328,7 +361,8 @@ terminate called after throwing an instance of 'std::out_of_range'
|
||||
what(): [error] key "num" not found in example.toml
|
||||
```
|
||||
|
||||
You can use this with a `toml::value` that is expected to be a `toml::table`. It automatically casts the value to table.
|
||||
You can use this with a `toml::value` that is expected to be a `toml::table`.
|
||||
It automatically casts the value to table.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::parse("example.toml");
|
||||
@@ -338,7 +372,8 @@ const auto num = toml::find<int>(data.at("table"), "num");
|
||||
// num = 42
|
||||
```
|
||||
|
||||
In this case, because the value `data.at("table")` knows the locatoin of itself, you don't need to pass where you find the value. `toml::find` will show you a great error message.
|
||||
In this case, because the value `data.at("table")` knows the locatoin of itself,
|
||||
you don't need to pass where you find the value. `toml::find` will show you a great error message.
|
||||
|
||||
```console
|
||||
terminate called after throwing an instance of 'std::out_of_range'
|
||||
@@ -388,9 +423,26 @@ int i = 0;
|
||||
toml::from_toml(i, data.at("something"));
|
||||
```
|
||||
|
||||
### visiting toml::value
|
||||
|
||||
TOML v2.1.0+ 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.
|
||||
|
||||
### Sanitizing UTF-8 codepoints
|
||||
|
||||
toml11 shows warning if a value of an escape sequence used to represent unicode character exceeds the unicode range.
|
||||
toml11 shows warning if a value of an escape sequence used
|
||||
to represent unicode character exceeds the unicode range.
|
||||
|
||||
```console
|
||||
[warning] input codepoint (0011FFFF) is too large to decode as a unicode character. The result may not be able to render to your screen.
|
||||
@@ -462,6 +514,97 @@ you will get an error message like this.
|
||||
| ~~ maximum number here
|
||||
```
|
||||
|
||||
### Serializing TOML data
|
||||
|
||||
toml11 v2.1.0 enables you to serialize data into toml format.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::table{{"foo", 42}, {"bar", "baz"}};
|
||||
|
||||
const std::string serial = toml::format(data);
|
||||
// serial == "{bar=\"baz\",foo=42}"
|
||||
|
||||
std::cout << data << std::endl;
|
||||
// bar = "baz"
|
||||
// foo = 42
|
||||
```
|
||||
|
||||
toml11 automatically makes a tiny table and 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.
|
||||
|
||||
`toml::format` receives optional second argument to set the width.
|
||||
By default, it is 80.
|
||||
|
||||
```cpp
|
||||
const auto data = toml::table{
|
||||
{"qux", toml::table{{"foo", 42}, {"bar", "baz"}}}
|
||||
};
|
||||
|
||||
const std::string serial = toml::format(data, /*width = */ 0);
|
||||
// [qux]
|
||||
// bar = "baz"
|
||||
// foo = 42
|
||||
```
|
||||
|
||||
To control the precision of floating point numbers, you need to pass
|
||||
`std::setprecision` to stream or pass `int` to the optional third argument of
|
||||
`toml::format` (by default, it is `std::numeric_limits<double>::max_digit10`).
|
||||
|
||||
```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
|
||||
|
||||
const std::string 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.
|
||||
@@ -477,11 +620,16 @@ The toml types (can be used as `toml::*` in this library) and corresponding `enu
|
||||
| LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` |
|
||||
| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offsetDatetime` |
|
||||
| Array | `std::vector<toml::value>` | `toml::value_t::Array` |
|
||||
| Table | `std::unordered_map<std::string, toml::key>` | `toml::value_t::Table` |
|
||||
| Table | `std::unordered_map<toml::key, toml::value>` | `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<std::string>()` for convenience.
|
||||
`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<std::string>()` 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.
|
||||
`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`.
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ set(TEST_NAMES
|
||||
test_to_toml
|
||||
test_from_toml
|
||||
test_parse_file
|
||||
test_serialize_file
|
||||
test_parse_unicode
|
||||
test_error_detection
|
||||
)
|
||||
|
||||
54
tests/test_serialize_file.cpp
Normal file
54
tests/test_serialize_file.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#define BOOST_TEST_MODULE "test_serialize_file"
|
||||
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#else
|
||||
#define BOOST_TEST_NO_LIB
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#endif
|
||||
#include <toml.hpp>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_example)
|
||||
{
|
||||
const auto data = toml::parse("toml/tests/example.toml");
|
||||
{
|
||||
std::ofstream ofs("tmp1.toml");
|
||||
ofs << std::setw(80) << data;
|
||||
}
|
||||
|
||||
auto serialized = toml::parse("tmp1.toml");
|
||||
{
|
||||
auto& owner = toml::get<toml::table>(serialized.at("owner"));
|
||||
auto& bio = toml::get<std::string>(owner.at("bio"));
|
||||
const auto CR = std::find(bio.begin(), bio.end(), '\r');
|
||||
if(CR != bio.end())
|
||||
{
|
||||
bio.erase(CR);
|
||||
}
|
||||
}
|
||||
BOOST_CHECK(data == serialized);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_fruit)
|
||||
{
|
||||
const auto data = toml::parse("toml/tests/fruit.toml");
|
||||
{
|
||||
std::ofstream ofs("tmp2.toml");
|
||||
ofs << std::setw(80) << data;
|
||||
}
|
||||
const auto serialized = toml::parse("tmp2.toml");
|
||||
BOOST_CHECK(data == serialized);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_hard_example)
|
||||
{
|
||||
const auto data = toml::parse("toml/tests/hard_example.toml");
|
||||
{
|
||||
std::ofstream ofs("tmp3.toml");
|
||||
ofs << std::setw(80) << data;
|
||||
}
|
||||
const auto serialized = toml::parse("tmp3.toml");
|
||||
BOOST_CHECK(data == serialized);
|
||||
}
|
||||
1
toml.hpp
1
toml.hpp
@@ -34,6 +34,7 @@
|
||||
#endif
|
||||
|
||||
#include "toml/parser.hpp"
|
||||
#include "toml/serializer.hpp"
|
||||
#include "toml/to_toml.hpp"
|
||||
#include "toml/from_toml.hpp"
|
||||
#include "toml/get.hpp"
|
||||
|
||||
@@ -14,6 +14,36 @@
|
||||
namespace toml
|
||||
{
|
||||
|
||||
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
|
||||
// provided in the absolutely same purpose, but C++11 is actually not compatible
|
||||
// with C11. We need to dispatch the function depending on the OS.
|
||||
namespace detail
|
||||
{
|
||||
// TODO: find more sophisticated way to handle this
|
||||
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
const auto result = ::localtime_r(src, &dst);
|
||||
if(!result)
|
||||
{
|
||||
throw std::runtime_error("localtime_r failed.");
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
#else
|
||||
// XXX: On Windows, std::localtime is thread-safe because they uses thread-local
|
||||
// storage to store the instance of std::tm. On the other platforms, it may not
|
||||
// be thread-safe.
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
const auto result = std::localtime(src);
|
||||
if(!result) {throw std::runtime_error("localtime failed.");}
|
||||
return *result;
|
||||
}
|
||||
#endif
|
||||
} // detail
|
||||
|
||||
enum class month_t : std::int8_t
|
||||
{
|
||||
Jan = 0,
|
||||
@@ -50,10 +80,8 @@ struct local_date
|
||||
|
||||
explicit local_date(const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
const auto t = std::chrono::system_clock::to_time_t(tp);
|
||||
const auto tmp = std::localtime(&t); //XXX: not threadsafe!
|
||||
assert(tmp); // if std::localtime fails, tmp is nullptr
|
||||
const std::tm time = *tmp;
|
||||
const auto t = std::chrono::system_clock::to_time_t(tp);
|
||||
const auto time = detail::localtime_s(&t);
|
||||
*this = local_date(time);
|
||||
}
|
||||
|
||||
@@ -330,10 +358,8 @@ struct local_datetime
|
||||
explicit local_datetime(const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
const auto t = std::chrono::system_clock::to_time_t(tp);
|
||||
const auto tmp = std::localtime(&t); //XXX: not threadsafe!
|
||||
assert(tmp); // if std::localtime fails, tmp is nullptr
|
||||
std::tm time = detail::localtime_s(&t);
|
||||
|
||||
std::tm time = *tmp;
|
||||
this->date = local_date(time);
|
||||
this->time = local_time(time);
|
||||
|
||||
@@ -482,10 +508,8 @@ struct offset_datetime
|
||||
static time_offset get_local_offset()
|
||||
{
|
||||
// get current timezone
|
||||
const auto tmp1 = std::time(nullptr);
|
||||
const auto tmp2 = std::localtime(&tmp1); // XXX not threadsafe!
|
||||
assert(tmp2);
|
||||
std::tm t = *tmp2;
|
||||
const auto tmp1 = std::time(nullptr);
|
||||
const auto t = detail::localtime_s(&tmp1);
|
||||
|
||||
std::array<char, 6> buf;
|
||||
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
|
||||
|
||||
511
toml/serializer.hpp
Normal file
511
toml/serializer.hpp
Normal file
@@ -0,0 +1,511 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_SERIALIZER_HPP
|
||||
#define TOML11_SERIALIZER_HPP
|
||||
#include "value.hpp"
|
||||
#include "lexer.hpp"
|
||||
#include <limits>
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
struct serializer
|
||||
{
|
||||
serializer(const std::size_t w = 80,
|
||||
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
|
||||
const bool can_be_inlined = false,
|
||||
std::vector<toml::key> ks = {})
|
||||
: can_be_inlined_(can_be_inlined), float_prec_(float_prec), width_(w),
|
||||
keys_(std::move(ks))
|
||||
{}
|
||||
~serializer() = default;
|
||||
|
||||
std::string operator()(const toml::boolean& b) const
|
||||
{
|
||||
return b ? "true" : "false";
|
||||
}
|
||||
std::string operator()(const integer i) const
|
||||
{
|
||||
return std::to_string(i);
|
||||
}
|
||||
std::string operator()(const toml::floating f) const
|
||||
{
|
||||
std::string token = [=] {
|
||||
// every float value needs decimal point (or exponent).
|
||||
std::ostringstream oss;
|
||||
oss << std::setprecision(float_prec_) << std::showpoint << f;
|
||||
return oss.str();
|
||||
}();
|
||||
|
||||
if(token.back() == '.') // 1. => 1.0
|
||||
{
|
||||
token += '0';
|
||||
}
|
||||
const auto e = std::find_if(token.cbegin(), token.cend(),
|
||||
[](const char c) -> bool {
|
||||
return c == 'E' || c == 'e';
|
||||
});
|
||||
if(e == token.cend())
|
||||
{
|
||||
return token; // there is no exponent part. just return it.
|
||||
}
|
||||
|
||||
// zero-prefix in an exponent is not allowed in TOML.
|
||||
// remove it if it exists.
|
||||
bool sign_exists = false;
|
||||
std::size_t zero_prefix = 0;
|
||||
for(auto iter = std::next(e), iend = token.cend(); iter != iend; ++iter)
|
||||
{
|
||||
if(*iter == '+' || *iter == '-'){sign_exists = true; continue;}
|
||||
if(*iter == '0'){zero_prefix += 1;}
|
||||
else {break;}
|
||||
}
|
||||
if(zero_prefix != 0)
|
||||
{
|
||||
const auto offset = std::distance(token.cbegin(), e) +
|
||||
(sign_exists ? 2 : 1);
|
||||
token.erase(offset, zero_prefix);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
std::string operator()(const string& s) const
|
||||
{
|
||||
if(s.kind == string_t::basic)
|
||||
{
|
||||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
|
||||
{
|
||||
// if linefeed is contained, make it multiline-string.
|
||||
const std::string open("\"\"\"\n");
|
||||
const std::string close("\\\n\"\"\"");
|
||||
return open + this->escape_ml_basic_string(s.str) + close;
|
||||
}
|
||||
|
||||
// no linefeed. try to make it oneline-string.
|
||||
std::string oneline = this->escape_basic_string(s.str);
|
||||
if(oneline.size() + 2 < width_ || width_ < 2)
|
||||
{
|
||||
const std::string quote("\"");
|
||||
return quote + oneline + quote;
|
||||
}
|
||||
|
||||
// the line is too long compared to the specified width.
|
||||
// split it into multiple lines.
|
||||
std::string token("\"\"\"\n");
|
||||
while(!oneline.empty())
|
||||
{
|
||||
if(oneline.size() < width_)
|
||||
{
|
||||
token += oneline;
|
||||
oneline.clear();
|
||||
}
|
||||
else if(oneline.at(width_-2) == '\\')
|
||||
{
|
||||
token += oneline.substr(0, width_-2);
|
||||
token += "\\\n";
|
||||
oneline.erase(0, width_-2);
|
||||
}
|
||||
else
|
||||
{
|
||||
token += oneline.substr(0, width_-1);
|
||||
token += "\\\n";
|
||||
oneline.erase(0, width_-1);
|
||||
}
|
||||
}
|
||||
return token + std::string("\\\n\"\"\"");
|
||||
}
|
||||
else // the string `s` is literal-string.
|
||||
{
|
||||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
|
||||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
|
||||
{
|
||||
const std::string open("'''\n");
|
||||
const std::string close("'''");
|
||||
return open + s.str + close;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string quote("'");
|
||||
return quote + s.str + quote;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string operator()(const local_date& d) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << d;
|
||||
return oss.str();
|
||||
}
|
||||
std::string operator()(const local_time& t) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << t;
|
||||
return oss.str();
|
||||
}
|
||||
std::string operator()(const local_datetime& dt) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << dt;
|
||||
return oss.str();
|
||||
}
|
||||
std::string operator()(const offset_datetime& odt) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << odt;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string operator()(const array& v) const
|
||||
{
|
||||
if(!v.empty() && v.front().is(value_t::Table))// v is an array of tables
|
||||
{
|
||||
// if it's not inlined, we need to add `[[table.key]]`.
|
||||
// but if it can be inlined, we need `table.key = [...]`.
|
||||
if(this->can_be_inlined_)
|
||||
{
|
||||
std::string token;
|
||||
if(!keys_.empty())
|
||||
{
|
||||
token += this->serialize_key(keys_.back());
|
||||
token += " = ";
|
||||
}
|
||||
bool width_exceeds = false;
|
||||
token += "[\n";
|
||||
for(const auto& item : v)
|
||||
{
|
||||
const auto t =
|
||||
this->make_inline_table(item.cast<value_t::Table>());
|
||||
|
||||
if(t.size() + 1 > width_ || // +1 for the last comma {...},
|
||||
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
|
||||
{
|
||||
width_exceeds = true;
|
||||
break;
|
||||
}
|
||||
token += t;
|
||||
token += ",\n";
|
||||
}
|
||||
if(!width_exceeds)
|
||||
{
|
||||
token += "]\n";
|
||||
return token;
|
||||
}
|
||||
// if width_exceeds, serialize it as [[array.of.tables]].
|
||||
}
|
||||
|
||||
std::string token;
|
||||
for(const auto& item : v)
|
||||
{
|
||||
token += "[[";
|
||||
token += this->serialize_dotted_key(keys_);
|
||||
token += "]]\n";
|
||||
token += this->make_multiline_table(item.cast<value_t::Table>());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
if(v.empty())
|
||||
{
|
||||
return std::string("[]");
|
||||
}
|
||||
|
||||
// not an array of tables. normal array. first, try to make it inline.
|
||||
{
|
||||
const auto inl = this->make_inline_array(v);
|
||||
if(inl.size() < this->width_ &&
|
||||
std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend())
|
||||
{
|
||||
return inl;
|
||||
}
|
||||
}
|
||||
|
||||
// if the length exceeds this->width_, print multiline array
|
||||
std::string token;
|
||||
std::string current_line;
|
||||
token += "[\n";
|
||||
for(const auto& item : v)
|
||||
{
|
||||
auto next_elem = toml::visit(*this, item);
|
||||
// newline between array-value and comma is not allowed
|
||||
if(next_elem.back() == '\n'){next_elem.pop_back();}
|
||||
|
||||
if(current_line.size() + next_elem.size() + 1 < this->width_)
|
||||
{
|
||||
current_line += next_elem;
|
||||
current_line += ',';
|
||||
}
|
||||
else if(current_line.empty())
|
||||
{
|
||||
// the next elem cannot be within the width.
|
||||
token += next_elem;
|
||||
token += ",\n";
|
||||
// keep current line empty
|
||||
}
|
||||
else // current_line has some tokens and it exceeds width
|
||||
{
|
||||
assert(current_line.back() == ',');
|
||||
token += current_line;
|
||||
token += '\n';
|
||||
current_line = next_elem;
|
||||
current_line += ',';
|
||||
}
|
||||
}
|
||||
if(!current_line.empty())
|
||||
{
|
||||
if(current_line.back() != '\n') {current_line += '\n';}
|
||||
token += current_line;
|
||||
}
|
||||
token += "]\n";
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string operator()(const table& v) const
|
||||
{
|
||||
if(this->can_be_inlined_)
|
||||
{
|
||||
std::string token;
|
||||
if(!this->keys_.empty())
|
||||
{
|
||||
token += this->serialize_key(this->keys_.back());
|
||||
token += " = ";
|
||||
}
|
||||
token += this->make_inline_table(v);
|
||||
if(token.size() < this->width_)
|
||||
{
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
std::string token;
|
||||
if(!keys_.empty())
|
||||
{
|
||||
token += '[';
|
||||
token += this->serialize_dotted_key(keys_);
|
||||
token += "]\n";
|
||||
}
|
||||
token += this->make_multiline_table(v);
|
||||
return token;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::string serialize_key(const toml::key& key) const
|
||||
{
|
||||
detail::location<toml::key> loc(key, key);
|
||||
detail::lex_unquoted_key::invoke(loc);
|
||||
if(loc.iter() == loc.end())
|
||||
{
|
||||
return key; // all the tokens are consumed. the key is unquoted-key.
|
||||
}
|
||||
std::string token("\"");
|
||||
token += this->escape_basic_string(key);
|
||||
token += "\"";
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string serialize_dotted_key(const std::vector<toml::key>& keys) const
|
||||
{
|
||||
std::string token;
|
||||
if(keys.empty()){return token;}
|
||||
|
||||
for(const auto& k : keys)
|
||||
{
|
||||
token += this->serialize_key(k);
|
||||
token += '.';
|
||||
}
|
||||
token.erase(token.size() - 1, 1); // remove trailing `.`
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string escape_basic_string(const std::string& s) const
|
||||
{
|
||||
//XXX assuming `s` is a valid utf-8 sequence.
|
||||
std::string retval;
|
||||
for(const char c : s)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '\\': {retval += "\\\\"; break;}
|
||||
case '\"': {retval += "\\\""; break;}
|
||||
case '\b': {retval += "\\b"; break;}
|
||||
case '\t': {retval += "\\t"; break;}
|
||||
case '\f': {retval += "\\f"; break;}
|
||||
case '\n': {retval += "\\n"; break;}
|
||||
case '\r': {retval += "\\r"; break;}
|
||||
default : {retval += c; break;}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::string escape_ml_basic_string(const std::string& s) const
|
||||
{
|
||||
std::string retval;
|
||||
for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i)
|
||||
{
|
||||
switch(*i)
|
||||
{
|
||||
case '\\': {retval += "\\\\"; break;}
|
||||
case '\"': {retval += "\\\""; break;}
|
||||
case '\b': {retval += "\\b"; break;}
|
||||
case '\t': {retval += "\\t"; break;}
|
||||
case '\f': {retval += "\\f"; break;}
|
||||
case '\n': {retval += "\n"; break;}
|
||||
case '\r':
|
||||
{
|
||||
if(std::next(i) != e && *std::next(i) == '\n')
|
||||
{
|
||||
retval += "\r\n";
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval += "\\r";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {retval += *i; break;}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::string make_inline_array(const array& v) const
|
||||
{
|
||||
std::string token;
|
||||
token += '[';
|
||||
bool is_first = true;
|
||||
for(const auto& item : v)
|
||||
{
|
||||
if(is_first) {is_first = false;} else {token += ',';}
|
||||
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
|
||||
this->float_prec_, true), item);
|
||||
}
|
||||
token += ']';
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string make_inline_table(const table& v) const
|
||||
{
|
||||
assert(this->can_be_inlined_);
|
||||
std::string token;
|
||||
token += '{';
|
||||
bool is_first = true;
|
||||
for(const auto& kv : v)
|
||||
{
|
||||
// in inline tables, trailing comma is not allowed (toml-lang #569).
|
||||
if(is_first) {is_first = false;} else {token += ',';}
|
||||
token += this->serialize_key(kv.first);
|
||||
token += '=';
|
||||
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
|
||||
this->float_prec_, true), kv.second);
|
||||
}
|
||||
token += '}';
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string make_multiline_table(const table& v) const
|
||||
{
|
||||
std::string token;
|
||||
|
||||
// print non-table stuff first. because after printing [foo.bar], the
|
||||
// remaining non-table values will be assigned into [foo.bar], not [foo]
|
||||
for(const auto kv : v)
|
||||
{
|
||||
if(kv.second.is(value_t::Table) || is_array_of_tables(kv.second))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto key_and_sep = this->serialize_key(kv.first) + " = ";
|
||||
const auto residual_width = (this->width_ > key_and_sep.size()) ?
|
||||
this->width_ - key_and_sep.size() : 0;
|
||||
token += key_and_sep;
|
||||
token += visit(serializer(residual_width, this->float_prec_, true),
|
||||
kv.second);
|
||||
if(token.back() != '\n')
|
||||
{
|
||||
token += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// normal tables / array of tables
|
||||
|
||||
// after multiline table appeared, the other tables cannot be inline
|
||||
// because the table would be assigned into the table.
|
||||
// [foo]
|
||||
// ...
|
||||
// bar = {...} # <- bar will be a member of [foo].
|
||||
bool multiline_table_printed = false;
|
||||
for(const auto& kv : v)
|
||||
{
|
||||
if(!kv.second.is(value_t::Table) && !is_array_of_tables(kv.second))
|
||||
{
|
||||
continue; // other stuff are already serialized. skip them.
|
||||
}
|
||||
|
||||
std::vector<toml::key> ks(this->keys_);
|
||||
ks.push_back(kv.first);
|
||||
|
||||
auto tmp = visit(serializer(
|
||||
this->width_, this->float_prec_, !multiline_table_printed, ks),
|
||||
kv.second);
|
||||
|
||||
if((!multiline_table_printed) &&
|
||||
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
|
||||
{
|
||||
multiline_table_printed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// still inline tables only.
|
||||
tmp += '\n';
|
||||
}
|
||||
token += tmp;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
bool is_array_of_tables(const value& v) const
|
||||
{
|
||||
if(!v.is(value_t::Array)) {return false;}
|
||||
|
||||
const auto& a = v.cast<value_t::Array>();
|
||||
return !a.empty() && a.front().is(value_t::Table);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool can_be_inlined_;
|
||||
int float_prec_;
|
||||
std::size_t width_;
|
||||
std::vector<toml::key> keys_;
|
||||
};
|
||||
|
||||
inline std::string
|
||||
format(const value& v, std::size_t w = 80,
|
||||
int fprec = std::numeric_limits<toml::floating>::max_digits10)
|
||||
{
|
||||
return visit(serializer(w, fprec, true), v);
|
||||
}
|
||||
inline std::string
|
||||
format(const table& t, std::size_t w = 80,
|
||||
int fprec = std::numeric_limits<toml::floating>::max_digits10)
|
||||
{
|
||||
return serializer(w, fprec, true)(t);
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const value& v)
|
||||
{
|
||||
// get status of std::setw().
|
||||
const std::size_t w = os.width();
|
||||
const int fprec = os.precision();
|
||||
os.width(0);
|
||||
// the root object can't be an inline table. so pass `false`.
|
||||
os << visit(serializer(w, fprec, false), v);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // toml
|
||||
#endif// TOML11_SERIALIZER_HPP
|
||||
@@ -820,5 +820,77 @@ inline std::string format_error(const std::string& err_msg,
|
||||
std::move(hints));
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
detail::return_type_of_t<Visitor, const toml::boolean&>
|
||||
visit(Visitor&& visitor, const toml::value& v)
|
||||
{
|
||||
switch(v.type())
|
||||
{
|
||||
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());}
|
||||
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());}
|
||||
case value_t::Float : {return visitor(v.cast<value_t::Float >());}
|
||||
case value_t::String : {return visitor(v.cast<value_t::String >());}
|
||||
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());}
|
||||
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());}
|
||||
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());}
|
||||
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());}
|
||||
case value_t::Array : {return visitor(v.cast<value_t::Array >());}
|
||||
case value_t::Table : {return visitor(v.cast<value_t::Table >());}
|
||||
case value_t::Empty : break;
|
||||
case value_t::Unknown : break;
|
||||
default: break;
|
||||
}
|
||||
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
|
||||
"does not have any valid value.", v, "here"));
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
detail::return_type_of_t<Visitor, toml::boolean&>
|
||||
visit(Visitor&& visitor, toml::value& v)
|
||||
{
|
||||
switch(v.type())
|
||||
{
|
||||
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());}
|
||||
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());}
|
||||
case value_t::Float : {return visitor(v.cast<value_t::Float >());}
|
||||
case value_t::String : {return visitor(v.cast<value_t::String >());}
|
||||
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());}
|
||||
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());}
|
||||
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());}
|
||||
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());}
|
||||
case value_t::Array : {return visitor(v.cast<value_t::Array >());}
|
||||
case value_t::Table : {return visitor(v.cast<value_t::Table >());}
|
||||
case value_t::Empty : break;
|
||||
case value_t::Unknown : break;
|
||||
default: break;
|
||||
}
|
||||
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
|
||||
"does not have any valid value.", v, "here"));
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
detail::return_type_of_t<Visitor, toml::boolean&>
|
||||
visit(Visitor&& visitor, toml::value&& v)
|
||||
{
|
||||
switch(v.type())
|
||||
{
|
||||
case value_t::Boolean : {return visitor(std::move(v.cast<value_t::Boolean >()));}
|
||||
case value_t::Integer : {return visitor(std::move(v.cast<value_t::Integer >()));}
|
||||
case value_t::Float : {return visitor(std::move(v.cast<value_t::Float >()));}
|
||||
case value_t::String : {return visitor(std::move(v.cast<value_t::String >()));}
|
||||
case value_t::OffsetDatetime: {return visitor(std::move(v.cast<value_t::OffsetDatetime>()));}
|
||||
case value_t::LocalDatetime : {return visitor(std::move(v.cast<value_t::LocalDatetime >()));}
|
||||
case value_t::LocalDate : {return visitor(std::move(v.cast<value_t::LocalDate >()));}
|
||||
case value_t::LocalTime : {return visitor(std::move(v.cast<value_t::LocalTime >()));}
|
||||
case value_t::Array : {return visitor(std::move(v.cast<value_t::Array >()));}
|
||||
case value_t::Table : {return visitor(std::move(v.cast<value_t::Table >()));}
|
||||
case value_t::Empty : break;
|
||||
case value_t::Unknown : break;
|
||||
default: break;
|
||||
}
|
||||
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
|
||||
"does not have any valid value.", v, "here"));
|
||||
}
|
||||
|
||||
}// toml
|
||||
#endif// TOML11_VALUE
|
||||
|
||||
Reference in New Issue
Block a user