Compare commits

...

39 Commits

Author SHA1 Message Date
ToruNiina
dc562ae5cb refactor: remove name of unused arg in func 2024-06-28 00:48:20 +09:00
ToruNiina
83f37a18b1 fix: incorrect function argument 2024-06-28 00:48:06 +09:00
ToruNiina
dc7e443be4 test: add try_get/find 2024-06-28 00:47:37 +09:00
ToruNiina
59921c1eb7 feat: add try_find 2024-06-28 00:19:00 +09:00
ToruNiina
a4d0189df3 refactor: move key_cast to utility 2024-06-28 00:18:46 +09:00
ToruNiina
3320d25abb feat: add try_get 2024-06-28 00:13:07 +09:00
ToruNiina
8efb305e8b feat: add try_from to return result<T, E> 2024-06-28 00:09:29 +09:00
ToruNiina
2baa47ef3d Merge branch 'main' into v4_1_0 2024-06-28 00:08:44 +09:00
ToruNiina
42734ea642 fix: typo in type-trait impl 2024-06-28 00:07:26 +09:00
ToruNiina
8596419e24 feat [skip ci]: update single_include 2024-06-26 15:31:25 +00:00
ToruNiina
932acba7f1 fix #250: bump patch version macro in version.hpp 2024-06-27 00:30:38 +09:00
ToruNiina
b2a93eb267 chore: auto extract version string in cmake 2024-06-27 00:28:40 +09:00
ToruNiina
a0ae1a6bfd fix: access members directly 2024-06-26 22:42:32 +09:00
ToruNiina
044a66210d doc: add reference about try_at 2024-06-26 00:34:54 +09:00
ToruNiina
c4fb41b812 doc: add try_at to docs/features 2024-06-26 00:24:25 +09:00
ToruNiina
a610e75df0 test: add test of try_at for array and table 2024-06-26 00:09:08 +09:00
ToruNiina
0e84d0591b feat: add toml::basic_value::try_at 2024-06-25 23:50:28 +09:00
ToruNiina
e004eaf006 Merge branch 'main' of github.com:ToruNiina/toml11
to merge single_include update
2024-06-24 00:30:38 +09:00
ToruNiina
7c336a52a0 doc: update changelog in docs 2024-06-24 00:26:15 +09:00
ToruNiina
bc7c5cd7a3 feat [skip ci]: update single_include 2024-06-23 14:59:17 +00:00
ToruNiina
3c09d3046c refactor: remove detail::(local|gm)time_s from fwd 2024-06-23 23:25:57 +09:00
ToruNiina
7af0e66c25 refactor: add make_xxx_error funcs 2024-06-23 15:36:50 +09:00
ToruNiina
8c20067493 fix: title in example README 2024-06-23 15:32:50 +09:00
ToruNiina
89921483a8 feat [skip ci]: update single_include 2024-06-20 15:47:00 +00:00
ToruNiina
1bc6ef9fce ci: fix git options 2024-06-21 00:46:01 +09:00
ToruNiina
312996565d ci: add token 2024-06-21 00:27:14 +09:00
ToruNiina
db46c1c64b ci: fix git options 2024-06-21 00:24:03 +09:00
ToruNiina
34bf32a9e5 ci: fix symbol quote in if statement 2024-06-21 00:07:32 +09:00
ToruNiina
137f17f2e9 ci: add gen-single-include 2024-06-21 00:05:48 +09:00
ToruNiina
5e8d8d0243 doc: fix link in README 2024-06-21 00:05:09 +09:00
ToruNiina
9e27fd24b7 Merge branch 'fix-format-location-first-line' 2024-06-20 22:18:44 +09:00
ToruNiina
d023a93e3c fix: do not duplicate empty line in err msg 2024-06-20 21:11:43 +09:00
ToruNiina
995b25efe0 fix: pass empty filename to format_location
not to skip the first empty line
2024-06-20 21:10:42 +09:00
Toru Niina
6ba0772053 Merge pull request #248 from egorpugin/patch-1
Fix incorrect operator<<() argument type that gives build error.
2024-06-20 00:35:38 +09:00
Egor Pugin
a32289dc70 Fix incorrect operator<<() argument type that gives build error. 2024-06-19 17:42:10 +03:00
ToruNiina
7149b3cb29 refactor: rename member vars, adding _ at the end 2024-06-19 01:55:57 +09:00
ToruNiina
013d1e396f feat: use brace init in ctor for raw values 2024-06-19 01:45:58 +09:00
ToruNiina
7cab13f6e5 feat: use brace-init list for raw memvars 2024-06-19 01:45:29 +09:00
ToruNiina
74bea5368a fix #246: use brace-init in major/minor
to avoid name collision with <sys/sysmacros> major and minor
2024-06-19 01:36:40 +09:00
35 changed files with 3153 additions and 492 deletions

33
.github/workflows/single-include.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: gen-singlue-include
on:
push:
branches:
- main
jobs:
generate:
runs-on: Ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
g++-12 -std=c++20 -O2 tools/expand/main.cpp -o expand
./expand include/toml.hpp > single_include.hpp
- name: Check diff
id: check-diff
continue-on-error: true
run: |
diff single_include.hpp single_include/toml.hpp
- name: Commit and Push
if: steps.check-diff.outcome == 'failure'
run: |
mv single_include.hpp single_include/toml.hpp
git config --global user.name "ToruNiina"
git config --global user.email "ToruNiina@users.noreply.github.com"
git add single_include/toml.hpp
git commit -m "feat [skip ci]: update single_include"
git push origin main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,5 +1,18 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(toml11 LANGUAGES CXX VERSION 4.0.0)
# project_source_dir has not been set yet
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_MAJOR_VERSION_STRING
REGEX "#define TOML11_VERSION_MAJOR ([0-9]+)")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_MINOR_VERSION_STRING
REGEX "#define TOML11_VERSION_MINOR ([0-9]+)")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_PATCH_VERSION_STRING
REGEX "#define TOML11_VERSION_PATCH ([0-9]+)")
string(REGEX REPLACE "#define TOML11_VERSION_MAJOR ([0-9]+)" "\\1" TOML11_VERSION_MAJOR "${TOML11_MAJOR_VERSION_STRING}")
string(REGEX REPLACE "#define TOML11_VERSION_MINOR ([0-9]+)" "\\1" TOML11_VERSION_MINOR "${TOML11_MINOR_VERSION_STRING}")
string(REGEX REPLACE "#define TOML11_VERSION_PATCH ([0-9]+)" "\\1" TOML11_VERSION_PATCH "${TOML11_PATCH_VERSION_STRING}")
project(toml11 LANGUAGES CXX VERSION "${TOML11_VERSION_MAJOR}.${TOML11_VERSION_MINOR}.${TOML11_VERSION_PATCH}")
include(CMakeDependentOption) include(CMakeDependentOption)
include(CTest) include(CTest)

View File

@@ -81,7 +81,7 @@ For more details, please refer to the [documentation](https://toruniina.github.i
- [comments](#comments) - [comments](#comments)
- [error messages](#error-messages) - [error messages](#error-messages)
- [serialization](#serialization) - [serialization](#serialization)
- [Breaking Changes from v3](breaking-changes-from-v3) - [Breaking Changes from v3](#changes-from-v3)
- [Contributors](#contributors) - [Contributors](#contributors)
- [Licensing Terms](#licensing-terms) - [Licensing Terms](#licensing-terms)
@@ -332,7 +332,7 @@ For details on possible formatting specifications, please refer to the [document
### configuring types ### configuring types
The examples directory provides usage examples, such as using [The examples directory](https://github.com/ToruNiina/toml11/tree/main/examples) provides usage examples, such as using
arbitrary-precision integers, normalizing Unicode, and integrating with an external reflection library. arbitrary-precision integers, normalizing Unicode, and integrating with an external reflection library.
Please refer to these examples for implementation guidance in such scenarios. Please refer to these examples for implementation guidance in such scenarios.

View File

@@ -78,7 +78,7 @@ int main()
- [comments](#comments) - [comments](#comments)
- [error messages](#error-messages) - [error messages](#error-messages)
- [serialization](#serialization) - [serialization](#serialization)
- [Breaking Changes from v3](breaking-changes-from-v3) - [Breaking Changes from v3](#changes-from-v3)
- [Contributors](#contributors) - [Contributors](#contributors)
- [Licensing Terms](#licensing-terms) - [Licensing Terms](#licensing-terms)
@@ -331,7 +331,7 @@ std::cout << toml::format(input) << std::endl;
### configuring types ### configuring types
`examples`ディレクトリには、多倍長整数を使用する場合やユニコードを正規化する場合、 [`examples`ディレクトリ](https://github.com/ToruNiina/toml11/tree/main/examples)には、多倍長整数を使用する場合やユニコードを正規化する場合、
外部のリフレクションライブラリと連携する場合などの複雑な使用例を用意しています。 外部のリフレクションライブラリと連携する場合などの複雑な使用例を用意しています。
そのような状況での実装例として参照してください。 そのような状況での実装例として参照してください。

View File

@@ -6,7 +6,22 @@ weight = 4
# Change Log # Change Log
# Changes from v3 # v4.0.1
## Fixed
- Resolved naming conflict of `sematic_version::{major, minor}` with macros defined in `<sys/sysmacro.h>`
- Fixed the definition of `operator<<` in `discard_comments`
- Fixed the issue where the first blank line was not output in `format_location`
- Fixed the issue where error messages pointing to `source_location` referring to lines containing only newline characters were displayed in two lines
- Corrected links in the README
- Corrected the title of the README in `example/unicode`
## Added
- Configured CI to automatically update `single_include/toml.hpp` when changes are made to `main`
# Changes from v3 to v4
## Breaking Changes ## Breaking Changes

View File

@@ -92,6 +92,32 @@ std::cout << v.at(1);
If the stored type is not `array_type`, a `type_error` is thrown. If the stored type is not `array_type`, a `type_error` is thrown.
### `try_at(std::size_t i)`
Performs the same operation as `at(i)`, but instead of throwing an exception on failure, it always returns a [`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}}). It does not throw an exception on failure.
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
std::cout << res1.unwrap() << std::endl;
auto res5 = v.try_at(5);
assert(res1.is_err());
std::cout << toml::format_error(res1.unwrap_err()) << std::endl;
```
Additionally, since this `toml::result` holds a reference, it is possible to update the value.
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
res1.unwrap() = 42;
```
#### `at(std::string key)`, `operator[](std::string key)` #### `at(std::string key)`, `operator[](std::string key)`
These are equivalent to `as_table().at(key)` and `as_table()[key]`. These are equivalent to `as_table().at(key)` and `as_table()[key]`.
@@ -105,6 +131,32 @@ v["a"] = 42;
If the stored type is not `table_type`, a `type_error` is thrown. If the stored type is not `table_type`, a `type_error` is thrown.
### `try_at(std::string key)`
Performs the same operation as `at(key)`, but instead of throwing an exception on failure, it always returns a [`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}}). It does not throw an exception on failure.
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
std::cout << res_a.unwrap() << std::endl;
auto res_c = v.try_at("c");
assert(res_c.is_err());
std::cout << toml::format_error(res_c.unwrap_err()) << std::endl;
```
Additionally, since this `toml::result` holds a reference, it is possible to update the value.
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
res_a.unwrap() = 6 * 9;
```
#### `size()` #### `size()`
Returns the length. Returns the length.

View File

@@ -585,6 +585,27 @@ Throws `std::out_of_range` if the `table` does not contain the specified element
----- -----
### `try_at(key)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const key_type& key) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const key_type& key) const noexcept;
```
#### Return Value
After casting the current `value` to a `table`, it returns the element specified by the `key`.
If successful, it returns a `reference_wrapper` holding a reference to that element.
If unsuccessful, it returns an `error_info` corresponding to `type_error` or `out_of_range`.
#### Exceptions
Does not throw.
-----
#### `operator[](key)` #### `operator[](key)`
```cpp ```cpp
@@ -652,6 +673,26 @@ Throws `toml::type_error` if the stored value is not an `array`.
Throws `std::out_of_range` if the specified element does not exist in the `array`. Throws `std::out_of_range` if the specified element does not exist in the `array`.
-----
### `try_at(idx)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const std::size_t idx) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const std::size_t idx) const noexcept;
```
#### Return Value
After casting the current `value` to an `array`, it returns the element specified by the `idx`.
If successful, it returns a `reference_wrapper` holding a reference to that element.
If unsuccessful, it returns an `error_info` corresponding to `type_error` or `out_of_range`.
#### Exceptions
Does not throw.
----- -----

View File

@@ -6,7 +6,22 @@ weight = 4
# Change Log # Change Log
# v3からの変化 # v4.0.1
## Fixed
- `sematic_version::{major, minor}``<sys/sysmacro.h>` 内で定義されるマクロの衝突を解消
- `discard_comments``operator<<` の定義を修正
- `format_location`を使用した際に最初の空行が出ない問題を解決
- 改行文字のみを含む行を指す`source_location`でエラーメッセージを生成した際に、同じ行が二回表示される問題を解決
- `README.md`内のリンクを修正
- `example/unicode`のREADMEのタイトルを修正
## Added
- `main`に変更があったとき、`single_include/toml.hpp`を自動生成するようCIを設定
# v3からv4への変化
## 破壊的変更 ## 破壊的変更

View File

@@ -96,6 +96,32 @@ std::cout << v.at(1);
格納している型が `array_type` ではなかった場合、 `type_error` を送出します。 格納している型が `array_type` ではなかった場合、 `type_error` を送出します。
#### `try_at(std::size_t i)`
`at(i)`と同様の操作をしますが、失敗時に例外を投げる代わりに、常に[`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}})を返します。失敗時に例外は投げません。
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
std::cout << res1.unwrap() << std::endl;
auto res5 = v.try_at(5);
assert(res1.is_err());
std::cout << toml::format_error(res1.unwrap_err()) << std::endl;
```
また、この`toml::result`は成功値として参照を持つので、値を更新することも可能です。
```cpp
toml::value v(toml::array{1,2,3});
auto res1 = v.try_at(1);
assert(res1.is_ok());
res1.unwrap() = 42;
```
#### `at(std::string key)`, `operator[](std::string key)` #### `at(std::string key)`, `operator[](std::string key)`
`as_table().at(key)`, `as_table()[key]` と同等です。 `as_table().at(key)`, `as_table()[key]` と同等です。
@@ -111,6 +137,32 @@ v["a"] = 42;
格納している型が `table_type` ではなかった場合、 `type_error` を送出します。 格納している型が `table_type` ではなかった場合、 `type_error` を送出します。
#### `try_at(std::string key)`
`at(key)`と同様の操作をしますが、失敗時に例外を投げる代わりに、常に[`toml::result<T&, error_info>`]({{<ref "docs/reference/result">}})を返します。失敗時に例外は投げません。
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
std::cout << res_a.unwrap() << std::endl;
auto res_c = v.try_at("c");
assert(res_c.is_err());
std::cout << toml::format_error(res_c.unwrap_err()) << std::endl;
```
また、この`toml::result`は成功値として参照を持つので、値を更新することも可能です。
```cpp
toml::value v(toml::table{ {"a", 42}, {"b", "foo"} });
auto res_a = v.try_at("a");
assert(res_a.is_ok());
res_a.unwrap() = 6 * 9;
```
#### `size()` #### `size()`
長さを返します。 長さを返します。

View File

@@ -583,6 +583,27 @@ value_type const& at(const key_type& key) const;
----- -----
### `try_at(key)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const key_type& key) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const key_type& key) const noexcept;
```
#### 戻り値
今の`value``table`にキャストしたあと、`key`によって指定される要素を返します。
成功した場合、その要素への参照を持つ`reference_wrapper`を返します。
失敗した場合、 `type_error` または `out_of_range` に対応する `error_info` を返します。
#### 例外
投げません。
-----
#### `operator[](key)` #### `operator[](key)`
```cpp ```cpp
@@ -652,6 +673,27 @@ value_type const& at(const std::size_t idx) const;
----- -----
### `try_at(idx)`
```cpp
result<std::reference_wrapper<value_type>, error_info> try_at(const std::size_t idx) noexcept;
result<std::reference_wrapper<const value_type>, error_info> try_at(const std::size_t idx) const noexcept;
```
#### 戻り値
今の`value``array`にキャストしたあと、`idx`によって指定される要素を返します。
成功した場合、その要素への参照を持つ`reference_wrapper`を返します。
失敗した場合、 `type_error` または `out_of_range` に対応する `error_info` を返します。
#### 例外
投げません。
-----
### `operator[](idx)` ### `operator[](idx)`
```cpp ```cpp

View File

@@ -1,4 +1,4 @@
# reflect # unicode
Compare TOML key after NFC canonicalization. Compare TOML key after NFC canonicalization.

View File

@@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include "get.hpp" #include "get.hpp"
#include "utility.hpp"
#include "value.hpp" #include "value.hpp"
#if defined(TOML11_HAS_STRING_VIEW) #if defined(TOML11_HAS_STRING_VIEW)
@@ -101,54 +102,7 @@ find(basic_value<TC>&& v, const std::size_t idx)
} }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys) // toml::find(toml::value, toml::key, Ts&& ... keys) w/o conversion
namespace detail
{
// It suppresses warnings by -Wsign-conversion when we pass integer literal
// to toml::find. integer literal `0` is deduced as an int, and will be
// converted to std::size_t. This causes sign-conversion.
template<typename TC>
std::size_t key_cast(const std::size_t& v) noexcept
{
return v;
}
template<typename TC, typename T>
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
key_cast(const T& v) noexcept
{
return static_cast<std::size_t>(v);
}
// for string-like (string, string literal, string_view)
template<typename TC>
typename basic_value<TC>::key_type const&
key_cast(const typename basic_value<TC>::key_type& v) noexcept
{
return v;
}
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const typename basic_value<TC>::key_type::value_type* v)
{
return typename basic_value<TC>::key_type(v);
}
#if defined(TOML11_HAS_STRING_VIEW)
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const std::string_view v)
{
return typename basic_value<TC>::key_type(v);
}
#endif // string_view
} // detail
// ----------------------------------------------------------------------------
// find(v, keys...)
template<typename TC, typename K1, typename K2, typename ... Ks> template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const& cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&

View File

@@ -7,7 +7,18 @@ namespace toml
template<typename T> template<typename T>
struct from; struct from;
// { // {
// static T from_toml(const toml::value& v) // template<typename TC>
// static T from_toml(const toml::basic_value<TC>& v)
// {
// // User-defined conversions ...
// }
// };
template<typename T>
struct try_from;
// {
// template<typename TC>
// static result<T, error_info> try_from_toml(const toml::basic_value<TC>& v) noexcept
// { // {
// // User-defined conversions ... // // User-defined conversions ...
// } // }

View File

@@ -444,7 +444,7 @@ inline bool operator>=(const discard_comments&, const discard_comments&) noexcep
inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
inline std::ostream& operator<<(std::ostream&& os, const discard_comments&) {return os;} inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;}
} // toml11 } // toml11
#endif // TOML11_COMMENTS_FWD_HPP #endif // TOML11_COMMENTS_FWD_HPP

View File

@@ -12,17 +12,6 @@
namespace toml 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
{
std::tm localtime_s(const std::time_t* src);
std::tm gmtime_s(const std::time_t* src);
} // detail
// ----------------------------------------------------------------------------
enum class month_t : std::uint8_t enum class month_t : std::uint8_t
{ {
Jan = 0, Jan = 0,
@@ -48,15 +37,15 @@ struct local_date
std::uint8_t day{0}; // [1, 31] std::uint8_t day{0}; // [1, 31]
local_date(int y, month_t m, int d) local_date(int y, month_t m, int d)
: year (static_cast<std::int16_t>(y)), : year {static_cast<std::int16_t>(y)},
month(static_cast<std::uint8_t>(m)), month{static_cast<std::uint8_t>(m)},
day (static_cast<std::uint8_t>(d)) day {static_cast<std::uint8_t>(d)}
{} {}
explicit local_date(const std::tm& t) explicit local_date(const std::tm& t)
: year (static_cast<std::int16_t>(t.tm_year + 1900)), : year {static_cast<std::int16_t>(t.tm_year + 1900)},
month(static_cast<std::uint8_t>(t.tm_mon)), month{static_cast<std::uint8_t>(t.tm_mon)},
day (static_cast<std::uint8_t>(t.tm_mday)) day {static_cast<std::uint8_t>(t.tm_mday)}
{} {}
explicit local_date(const std::chrono::system_clock::time_point& tp); explicit local_date(const std::chrono::system_clock::time_point& tp);
@@ -95,19 +84,19 @@ struct local_time
local_time(int h, int m, int s, local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0) int ms = 0, int us = 0, int ns = 0)
: hour (static_cast<std::uint8_t>(h)), : hour {static_cast<std::uint8_t>(h)},
minute(static_cast<std::uint8_t>(m)), minute{static_cast<std::uint8_t>(m)},
second(static_cast<std::uint8_t>(s)), second{static_cast<std::uint8_t>(s)},
millisecond(static_cast<std::uint16_t>(ms)), millisecond{static_cast<std::uint16_t>(ms)},
microsecond(static_cast<std::uint16_t>(us)), microsecond{static_cast<std::uint16_t>(us)},
nanosecond (static_cast<std::uint16_t>(ns)) nanosecond {static_cast<std::uint16_t>(ns)}
{} {}
explicit local_time(const std::tm& t) explicit local_time(const std::tm& t)
: hour (static_cast<std::uint8_t>(t.tm_hour)), : hour {static_cast<std::uint8_t>(t.tm_hour)},
minute(static_cast<std::uint8_t>(t.tm_min)), minute{static_cast<std::uint8_t>(t.tm_min )},
second(static_cast<std::uint8_t>(t.tm_sec)), second{static_cast<std::uint8_t>(t.tm_sec )},
millisecond(0), microsecond(0), nanosecond(0) millisecond{0}, microsecond{0}, nanosecond{0}
{} {}
template<typename Rep, typename Period> template<typename Rep, typename Period>
@@ -160,8 +149,8 @@ struct time_offset
std::int8_t minute{0}; // [-59, 59] std::int8_t minute{0}; // [-59, 59]
time_offset(int h, int m) time_offset(int h, int m)
: hour (static_cast<std::int8_t>(h)), : hour {static_cast<std::int8_t>(h)},
minute(static_cast<std::int8_t>(m)) minute{static_cast<std::int8_t>(m)}
{} {}
operator std::chrono::minutes() const; operator std::chrono::minutes() const;
@@ -192,9 +181,9 @@ struct local_datetime
local_date date{}; local_date date{};
local_time time{}; local_time time{};
local_datetime(local_date d, local_time t): date(d), time(t) {} local_datetime(local_date d, local_time t): date{d}, time{t} {}
explicit local_datetime(const std::tm& t): date(t), time(t){} explicit local_datetime(const std::tm& t): date{t}, time{t}{}
explicit local_datetime(const std::chrono::system_clock::time_point& tp); explicit local_datetime(const std::chrono::system_clock::time_point& tp);
explicit local_datetime(const std::time_t t); explicit local_datetime(const std::time_t t);
@@ -230,10 +219,10 @@ struct offset_datetime
time_offset offset{}; time_offset offset{};
offset_datetime(local_date d, local_time t, time_offset o) offset_datetime(local_date d, local_time t, time_offset o)
: date(d), time(t), offset(o) : date{d}, time{t}, offset{o}
{} {}
offset_datetime(const local_datetime& dt, time_offset o) offset_datetime(const local_datetime& dt, time_offset o)
: date(dt.date), time(dt.time), offset(o) : date{dt.date}, time{dt.time}, offset{o}
{} {}
// use the current local timezone offset // use the current local timezone offset
explicit offset_datetime(const local_datetime& ld); explicit offset_datetime(const local_datetime& ld);

View File

@@ -232,12 +232,12 @@ struct value_with_format
value_with_format& operator=(value_with_format&&) = default; value_with_format& operator=(value_with_format&&) = default;
value_with_format(value_type v, format_type f) value_with_format(value_type v, format_type f)
: value(std::move(v)), format(std::move(f)) : value{std::move(v)}, format{std::move(f)}
{} {}
template<typename U> template<typename U>
value_with_format(value_with_format<U, format_type> other) value_with_format(value_with_format<U, format_type> other)
: value(std::move(other.value)), format(std::move(other.format)) : value{std::move(other.value)}, format{std::move(other.format)}
{} {}
value_type value; value_type value;

View File

@@ -107,10 +107,8 @@ std::string format_location(
{ {
const auto lnw = detail::line_width(loc, msg, tail...); const auto lnw = detail::line_width(loc, msg, tail...);
std::ostringstream oss; const std::string f(""); // at the 1st iteration, no prev_filename is given
detail::format_filename(oss, loc); return detail::format_location_rec(lnw, f, loc, msg, tail...);
return oss.str() + detail::format_location_rec(lnw, loc.file_name(), loc, msg, tail...);
} }
} // toml } // toml

View File

@@ -78,7 +78,7 @@ TOML11_INLINE local_date::local_date(const std::chrono::system_clock::time_point
} }
TOML11_INLINE local_date::local_date(const std::time_t t) TOML11_INLINE local_date::local_date(const std::time_t t)
: local_date(std::chrono::system_clock::from_time_t(t)) : local_date{std::chrono::system_clock::from_time_t(t)}
{} {}
TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const
@@ -294,7 +294,7 @@ TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::ti
} }
TOML11_INLINE local_datetime::local_datetime(const std::time_t t) TOML11_INLINE local_datetime::local_datetime(const std::time_t t)
: local_datetime(std::chrono::system_clock::from_time_t(t)) : local_datetime{std::chrono::system_clock::from_time_t(t)}
{} {}
TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const
@@ -380,11 +380,11 @@ TOML11_INLINE std::string to_string(const local_datetime& dt)
TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld) TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld)
: date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) : date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)}
// use the current local timezone offset // use the current local timezone offset
{} {}
TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp) TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset(0, 0) // use gmtime : offset{0, 0} // use gmtime
{ {
const auto timet = std::chrono::system_clock::to_time_t(tp); const auto timet = std::chrono::system_clock::to_time_t(tp);
const auto tm = detail::gmtime_s(&timet); const auto tm = detail::gmtime_s(&timet);
@@ -392,14 +392,14 @@ TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::
this->time = local_time(tm); this->time = local_time(tm);
} }
TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t) TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t)
: offset(0, 0) // use gmtime : offset{0, 0} // use gmtime
{ {
const auto tm = detail::gmtime_s(&t); const auto tm = detail::gmtime_s(&t);
this->date = local_date(tm); this->date = local_date(tm);
this->time = local_time(tm); this->time = local_time(tm);
} }
TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t) TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t)
: offset(0, 0) // assume gmtime : offset{0, 0} // assume gmtime
{ {
this->date = local_date(t); this->date = local_date(t);
this->time = local_time(t); this->time = local_time(t);

View File

@@ -126,16 +126,53 @@ TOML11_INLINE std::vector<std::string> region::as_lines() const
assert(this->is_ok()); assert(this->is_ok());
if(this->length_ == 0) if(this->length_ == 0)
{ {
return {""}; return std::vector<std::string>{""};
} }
const auto begin = std::next(this->source_->cbegin(), static_cast<difference_type>(this->first_)); // Consider the following toml file
const auto end = std::next(this->source_->cbegin(), static_cast<difference_type>(this->last_ )); // ```
// array = [
// ] # comment
// ```
// and the region represnets
// ```
// [
// ]
// ```
// but we want to show the following.
// ```
// array = [
// ] # comment
// ```
// So we need to find LFs before `begin` and after `end`.
//
// But, if region ends with LF, it should not include the next line.
// ```
// a = 42
// ^^^- with the last LF
// ```
// So we start from `end-1` when looking for LF.
const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')); const auto begin_idx = static_cast<difference_type>(this->first_);
const auto end_idx = static_cast<difference_type>(this->last_) - 1;
// length_ != 0, so begin < end. then begin <= end-1
assert(begin_idx <= end_idx);
const auto begin = std::next(this->source_->cbegin(), begin_idx);
const auto end = std::next(this->source_->cbegin(), end_idx);
const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')).base();
const auto line_end = std::find(end, this->source_->cend(), char_type('\n')); const auto line_end = std::find(end, this->source_->cend(), char_type('\n'));
std::istringstream iss(make_string(line_begin.base(), line_end)); const auto reg_lines = make_string(line_begin, line_end);
if(reg_lines == "") // the region is an empty line that only contains LF
{
return std::vector<std::string>{""};
}
std::istringstream iss(reg_lines);
std::vector<std::string> lines; std::vector<std::string> lines;
std::string line; std::string line;

View File

@@ -183,27 +183,27 @@ struct result
using value_type = typename success_type::value_type; using value_type = typename success_type::value_type;
using error_type = typename failure_type::value_type; using error_type = typename failure_type::value_type;
result(success_type s): is_ok_(true), succ(std::move(s)) {} result(success_type s): is_ok_(true), succ_(std::move(s)) {}
result(failure_type f): is_ok_(false), fail(std::move(f)) {} result(failure_type f): is_ok_(false), fail_(std::move(f)) {}
template<typename U, cxx::enable_if_t<cxx::conjunction< template<typename U, cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>, cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
std::is_convertible<cxx::remove_cvref_t<U>, value_type> std::is_convertible<cxx::remove_cvref_t<U>, value_type>
>::value, std::nullptr_t> = nullptr> >::value, std::nullptr_t> = nullptr>
result(success<U> s): is_ok_(true), succ(std::move(s.value)) {} result(success<U> s): is_ok_(true), succ_(std::move(s.value)) {}
template<typename U, cxx::enable_if_t<cxx::conjunction< template<typename U, cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, error_type>>, cxx::negation<std::is_same<cxx::remove_cvref_t<U>, error_type>>,
std::is_convertible<cxx::remove_cvref_t<U>, error_type> std::is_convertible<cxx::remove_cvref_t<U>, error_type>
>::value, std::nullptr_t> = nullptr> >::value, std::nullptr_t> = nullptr>
result(failure<U> f): is_ok_(false), fail(std::move(f.value)) {} result(failure<U> f): is_ok_(false), fail_(std::move(f.value)) {}
result& operator=(success_type s) result& operator=(success_type s)
{ {
this->cleanup(); this->cleanup();
this->is_ok_ = true; this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
return *this; return *this;
} }
@@ -211,8 +211,8 @@ struct result
{ {
this->cleanup(); this->cleanup();
this->is_ok_ = false; this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
return *this; return *this;
} }
@@ -222,8 +222,8 @@ struct result
{ {
this->cleanup(); this->cleanup();
this->is_ok_ = true; this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s.value));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
return *this; return *this;
} }
@@ -232,8 +232,8 @@ struct result
{ {
this->cleanup(); this->cleanup();
this->is_ok_ = false; this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f.value));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
return *this; return *this;
} }
@@ -244,14 +244,14 @@ struct result
{ {
if(other.is_ok()) if(other.is_ok())
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_);
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
} }
} }
@@ -259,14 +259,14 @@ struct result
{ {
if(other.is_ok()) if(other.is_ok())
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
} }
} }
@@ -276,14 +276,14 @@ struct result
this->cleanup(); this->cleanup();
if(other.is_ok()) if(other.is_ok())
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_);
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
} }
is_ok_ = other.is_ok(); is_ok_ = other.is_ok();
@@ -294,14 +294,14 @@ struct result
this->cleanup(); this->cleanup();
if(other.is_ok()) if(other.is_ok())
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
} }
is_ok_ = other.is_ok(); is_ok_ = other.is_ok();
@@ -318,14 +318,14 @@ struct result
{ {
if(other.is_ok()) if(other.is_ok())
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
} }
} }
@@ -341,14 +341,14 @@ struct result
this->cleanup(); this->cleanup();
if(other.is_ok()) if(other.is_ok())
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ_));
(void)tmp; (void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail_));
(void)tmp; (void)tmp;
} }
is_ok_ = other.is_ok(); is_ok_ = other.is_ok();
@@ -366,7 +366,7 @@ struct result
{ {
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
} }
return this->succ.get(); return this->succ_.get();
} }
value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const
{ {
@@ -374,18 +374,18 @@ struct result
{ {
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
} }
return this->succ.get(); return this->succ_.get();
} }
value_type& unwrap_or(value_type& opt) noexcept value_type& unwrap_or(value_type& opt) noexcept
{ {
if(this->is_err()) {return opt;} if(this->is_err()) {return opt;}
return this->succ.get(); return this->succ_.get();
} }
value_type const& unwrap_or(value_type const& opt) const noexcept value_type const& unwrap_or(value_type const& opt) const noexcept
{ {
if(this->is_err()) {return opt;} if(this->is_err()) {return opt;}
return this->succ.get(); return this->succ_.get();
} }
error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()) error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current())
@@ -394,7 +394,7 @@ struct result
{ {
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
} }
return this->fail.get(); return this->fail_.get();
} }
error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const
{ {
@@ -402,29 +402,29 @@ struct result
{ {
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
} }
return this->fail.get(); return this->fail_.get();
} }
value_type& as_ok() noexcept value_type& as_ok() noexcept
{ {
assert(this->is_ok()); assert(this->is_ok());
return this->succ.get(); return this->succ_.get();
} }
value_type const& as_ok() const noexcept value_type const& as_ok() const noexcept
{ {
assert(this->is_ok()); assert(this->is_ok());
return this->succ.get(); return this->succ_.get();
} }
error_type& as_err() noexcept error_type& as_err() noexcept
{ {
assert(this->is_err()); assert(this->is_err());
return this->fail.get(); return this->fail_.get();
} }
error_type const& as_err() const noexcept error_type const& as_err() const noexcept
{ {
assert(this->is_err()); assert(this->is_err());
return this->fail.get(); return this->fail_.get();
} }
private: private:
@@ -436,8 +436,8 @@ struct result
#pragma GCC diagnostic ignored "-Wduplicated-branches" #pragma GCC diagnostic ignored "-Wduplicated-branches"
#endif #endif
if(this->is_ok_) {this->succ.~success_type();} if(this->is_ok_) {this->succ_.~success_type();}
else {this->fail.~failure_type();} else {this->fail_.~failure_type();}
#if defined(__GNUC__) && ! defined(__clang__) #if defined(__GNUC__) && ! defined(__clang__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
@@ -450,8 +450,8 @@ struct result
bool is_ok_; bool is_ok_;
union union
{ {
success_type succ; success_type succ_;
failure_type fail; failure_type fail_;
}; };
}; };

View File

@@ -12,7 +12,7 @@ namespace toml
struct semantic_version struct semantic_version
{ {
constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept
: major(mjr), minor(mnr), patch(p) : major{mjr}, minor{mnr}, patch{p}
{} {}
std::uint32_t major; std::uint32_t major;
@@ -87,17 +87,17 @@ struct spec
} }
constexpr explicit spec(const semantic_version& semver) noexcept constexpr explicit spec(const semantic_version& semver) noexcept
: version(semver), : version{semver},
v1_1_0_allow_control_characters_in_comments (semantic_version(1, 1, 0) <= semver), v1_1_0_allow_control_characters_in_comments {semantic_version{1, 1, 0} <= semver},
v1_1_0_allow_newlines_in_inline_tables (semantic_version(1, 1, 0) <= semver), v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver},
v1_1_0_allow_trailing_comma_in_inline_tables(semantic_version(1, 1, 0) <= semver), v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver},
v1_1_0_allow_non_english_in_bare_keys (semantic_version(1, 1, 0) <= semver), v1_1_0_allow_non_english_in_bare_keys {semantic_version{1, 1, 0} <= semver},
v1_1_0_add_escape_sequence_e (semantic_version(1, 1, 0) <= semver), v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver},
v1_1_0_add_escape_sequence_x (semantic_version(1, 1, 0) <= semver), v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver},
v1_1_0_make_seconds_optional (semantic_version(1, 1, 0) <= semver), v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver},
ext_hex_float (false), ext_hex_float {false},
ext_num_suffix(false), ext_num_suffix{false},
ext_null_value(false) ext_null_value{false}
{} {}
semantic_version version; // toml version semantic_version version; // toml version

View File

@@ -23,25 +23,25 @@ struct storage
{ {
using value_type = T; using value_type = T;
explicit storage(value_type v): ptr(cxx::make_unique<T>(std::move(v))) {} explicit storage(value_type v): ptr_(cxx::make_unique<T>(std::move(v))) {}
~storage() = default; ~storage() = default;
storage(const storage& rhs): ptr(cxx::make_unique<T>(*rhs.ptr)) {} storage(const storage& rhs): ptr_(cxx::make_unique<T>(*rhs.ptr_)) {}
storage& operator=(const storage& rhs) storage& operator=(const storage& rhs)
{ {
this->ptr = cxx::make_unique<T>(*rhs.ptr); this->ptr_ = cxx::make_unique<T>(*rhs.ptr_);
return *this; return *this;
} }
storage(storage&&) = default; storage(storage&&) = default;
storage& operator=(storage&&) = default; storage& operator=(storage&&) = default;
bool is_ok() const noexcept {return static_cast<bool>(ptr);} bool is_ok() const noexcept {return static_cast<bool>(ptr_);}
value_type& get() const noexcept {return *ptr;} value_type& get() const noexcept {return *ptr_;}
private: private:
std::unique_ptr<value_type> ptr; std::unique_ptr<value_type> ptr_;
}; };
} // detail } // detail

View File

@@ -90,12 +90,19 @@ struct has_specialized_from_impl
template<typename T, std::size_t S = sizeof(::toml::from<T>)> template<typename T, std::size_t S = sizeof(::toml::from<T>)>
static std::true_type check(::toml::from<T>*); static std::true_type check(::toml::from<T>*);
}; };
struct has_specialized_try_from_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::try_from<T>)>
static std::true_type check(::toml::try_from<T>*);
};
struct has_specialized_into_impl struct has_specialized_into_impl
{ {
template<typename T> template<typename T>
static std::false_type check(...); static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)> template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*); static std::true_type check(::toml::into<T>*);
}; };
@@ -130,6 +137,8 @@ template<typename T>
struct has_specialized_from: decltype(has_specialized_from_impl::check<T>(nullptr)){}; struct has_specialized_from: decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T> template<typename T>
struct has_specialized_into: decltype(has_specialized_into_impl::check<T>(nullptr)){}; struct has_specialized_into: decltype(has_specialized_into_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_try_from: decltype(has_specialized_try_from_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER #ifdef __INTEL_COMPILER
#undef decltype #undef decltype

228
include/toml11/try_find.hpp Normal file
View File

@@ -0,0 +1,228 @@
#ifndef TOML11_TRY_FIND_HPP
#define TOML11_TRY_FIND_HPP
#include <algorithm>
#include "try_get.hpp"
#include "utility.hpp"
#include "value.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif
namespace toml
{
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC> const&>()))
try_find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) noexcept
{
const auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&>()))
try_find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) noexcept
{
auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&&>()))
try_find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky) noexcept
{
auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(std::move(res.as_ok()));
}
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC> const&>()))
try_find(const basic_value<TC>& v, const std::size_t idx) noexcept
{
const auto res = v.try_at(idx);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&>()))
try_find(basic_value<TC>& v, const std::size_t idx) noexcept
{
auto res = v.try_at(idx);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(res.as_ok());
}
template<typename T, typename TC>
decltype(try_get<T>(std::declval<basic_value<TC>&&>()))
try_find(basic_value<TC>&& v, const std::size_t idx) noexcept
{
auto res = v.try_at(idx);
if(res.is_err())
{
return err(res.as_err());
}
return try_get<T>(std::move(res.as_ok()));
}
// ----------------------------------------------------------------------------
// find(value, key/idx), w/o conversion
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<basic_value<TC>>, error_info>>
try_find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) noexcept
{
return v.try_at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<const basic_value<TC>>, error_info>>
try_find(basic_value<TC> const& v, const typename basic_value<TC>::key_type& ky) noexcept
{
return v.try_at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<basic_value<TC>, error_info>>
try_find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky) noexcept
{
auto res = v.try_at(ky);
if(res.is_err())
{
return err(res.as_err());
}
return ok(basic_value<TC>(std::move(res.as_ok())));
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<basic_value<TC>>, error_info>>
try_find(basic_value<TC>& v, const std::size_t idx) noexcept
{
return v.try_at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<const basic_value<TC>>, error_info>>
try_find(basic_value<TC> const& v, const std::size_t idx) noexcept
{
return v.try_at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<basic_value<TC>, error_info>>
try_find(basic_value<TC>&& v, const std::size_t idx) noexcept
{
auto res = v.try_at(idx);
if(res.is_err())
{
return err(std::move(res.as_err()));
}
return ok(basic_value<TC>(std::move(res.as_ok())));
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys) w/o conversion
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<const basic_value<TC>>, error_info>>
try_find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<std::reference_wrapper<basic_value<TC>>, error_info>>
try_find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value,
result<basic_value<TC>, error_info>>
try_find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find(std::move(res.as_ok()), detail::key_cast<TC>(k2), ks...);
}
// ----------------------------------------------------------------------------
// find<T>(v, keys...)
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::try_get<T>(std::declval<const basic_value<TC>&>()))
try_find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find<T>(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::try_get<T>(std::declval<basic_value<TC>&>()))
try_find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find<T>(res.as_ok(), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::try_get<T>(std::declval<basic_value<TC>&&>()))
try_find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks) noexcept
{
auto res = v.try_at(detail::key_cast<TC>(k1));
if(res.is_err())
{
return err(res.as_err());
}
return try_find<T>(std::move(res.as_ok()), detail::key_cast<TC>(k2), ks...);
}
} // toml
#endif // TOML11_FIND_HPP

472
include/toml11/try_get.hpp Normal file
View File

@@ -0,0 +1,472 @@
#ifndef TOML11_TRY_GET_HPP
#define TOML11_TRY_GET_HPP
#include <algorithm>
#include "error_info.hpp"
#include "from.hpp"
#include "types.hpp"
#include "value.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif // string_view
namespace toml
{
// ============================================================================
// T is toml::value; identity transformation.
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value,
result<std::reference_wrapper<T>, error_info>>
try_get(basic_value<TC>& v) noexcept
{
return ok(std::ref(v));
}
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value,
result<std::reference_wrapper<const T>, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
return ok(std::cref(v));
}
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value,
result<T, error_info>>
try_get(basic_value<TC>&& v) noexcept
{
return ok(basic_value<TC>(std::move(v)));
}
// ============================================================================
// exact toml::* type
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
result<std::reference_wrapper<T>, error_info>>
try_get(basic_value<TC>& v) noexcept
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
if(v.type() == ty)
{
return ok(std::ref(detail::getter<TC, ty>::get_nothrow(v)));
}
else
{
return err(detail::make_type_error(v, "toml::try_get()", ty));
}
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
result<std::reference_wrapper<const T>, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
if(v.type() == ty)
{
return ok(std::cref(detail::getter<TC, ty>::get_nothrow(v)));
}
else
{
return err(detail::make_type_error(v, "toml::try_get()", ty));
}
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
result<T, error_info>>
try_get(basic_value<TC>&& v) noexcept
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
if(v.type() == ty)
{
return ok(detail::getter<TC, ty>::get_nothrow(std::move(v)));
}
else
{
return err(detail::make_type_error(v, "toml::try_get()", ty));
}
}
// ============================================================================
// T is toml::basic_value<U>
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_basic_value<T>,
cxx::negation<std::is_same<T, basic_value<TC>>>
>::value, result<T, error_info>>
try_get(basic_value<TC> v) noexcept
{
return ok(T(std::move(v)));
}
// ============================================================================
// integer convertible from toml::value::integer_type
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_integral<T>,
cxx::negation<std::is_same<T, bool>>,
detail::is_not_toml_type<T, basic_value<TC>>,
cxx::negation<detail::has_from_toml_method<T, TC>>,
cxx::negation<detail::has_specialized_from<T>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_integer())
{
return ok(static_cast<T>(v.as_integer(std::nothrow)));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::integer));
}
}
// ============================================================================
// floating point convertible from toml::value::floating_type
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_floating_point<T>,
detail::is_not_toml_type<T, basic_value<TC>>,
cxx::negation<detail::has_from_toml_method<T, TC>>,
cxx::negation<detail::has_specialized_from<T>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_floating())
{
return ok(static_cast<T>(v.as_floating(std::nothrow)));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::floating));
}
}
// ============================================================================
// std::string_view
#if defined(TOML11_HAS_STRING_VIEW)
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, std::string_view>::value,
result<std::string_view, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_string())
{
return ok(std::string_view(v.as_string(std::nothrow)));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::string));
}
}
#endif // string_view
// ============================================================================
// std::chrono::duration from toml::local_time
template<typename T, typename TC>
cxx::enable_if_t<detail::is_chrono_duration<T>::value,
result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.is_local_time())
{
return ok(std::chrono::duration_cast<T>(
std::chrono::nanoseconds(v.as_local_time(std::nothrow))));
}
else
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::local_time));
}
}
// ============================================================================
// std::chrono::system_clock::time_point from toml::datetime variants
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<std::chrono::system_clock::time_point, T>::value,
result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
switch(v.type())
{
case value_t::local_date:
{
return ok(std::chrono::system_clock::time_point(v.as_local_date(std::nothrow)));
}
case value_t::local_datetime:
{
return ok(std::chrono::system_clock::time_point(v.as_local_datetime(std::nothrow)));
}
case value_t::offset_datetime:
{
return ok(std::chrono::system_clock::time_point(v.as_offset_datetime(std::nothrow)));
}
default:
{
const auto loc = v.location();
return err(make_error_info("toml::try_get(): bad_cast to "
"std::chrono::system_clock::time_point", loc,
"the actual type is " + to_string(v.type())));
}
}
}
// ============================================================================
// array-like types; most likely STL container, like std::vector, etc.
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_container<T>, // T is a container
detail::has_push_back_method<T>, // .push_back() works
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using value_type = typename T::value_type;
const auto& a = v.as_array(std::nothrow);
T container;
detail::try_reserve(container, a.size()); // if T has .reserve(), call it
for(const auto& elem : a)
{
auto converted = try_get<value_type>(elem);
if(converted.is_err())
{
return err(converted.as_err());
}
container.push_back(std::move(converted.as_ok()));
}
return ok(container);
}
// ============================================================================
// std::array
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_array<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using value_type = typename T::value_type;
const auto& a = v.as_array(std::nothrow);
T container;
if(a.size() != container.size())
{
const auto loc = v.location();
return err(make_error_info("toml::try_get: while converting to an array: "
" array size is " + std::to_string(container.size()) +
" but there are " + std::to_string(a.size()) + " elements in toml array.",
loc, "here"));
}
for(std::size_t i=0; i<a.size(); ++i)
{
auto converted = try_get<value_type>(a[i]);
if(converted.is_err())
{
return err(converted.as_err());
}
container[i] = std::move(converted.as_ok());
}
return ok(container);
}
// ============================================================================
// std::forward_list
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_forward_list<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using value_type = typename T::value_type;
T container;
for(const auto& elem : v.as_array(std::nothrow))
{
auto converted = try_get<value_type>(elem);
if(converted.is_err())
{
return err(converted.as_err());
}
container.push_front(std::move(converted.as_ok()));
}
container.reverse();
return ok(container);
}
// ============================================================================
// std::pair
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_pair<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
using first_type = typename T::first_type;
using second_type = typename T::second_type;
const auto& ar = v.as_array(std::nothrow);
if(ar.size() != 2)
{
const auto loc = v.location();
return err(make_error_info("toml::try_get: while converting std::pair: "
" but there are " + std::to_string(ar.size()) +
" > 2 elements in toml array.",
loc, "here"));
}
auto first_result = try_get<first_type>(ar[0]);
if(first_result.is_err())
{
return err(first_result.as_err());
}
auto second_result = try_get<second_type>(ar[1]);
if(second_result.is_err())
{
return err(second_result.as_err());
}
return ok(std::make_pair(std::move(first_result.as_ok()),
std::move(second_result.as_ok())));
}
// ============================================================================
// std::tuple.
namespace detail
{
template<typename T, std::size_t I, std::size_t N>
struct try_get_tuple_impl
{
template<typename Array, typename U>
static result<T, error_info> invoke(const Array& a, U curr) noexcept
{
assert(I < a.size());
using value_type = typename std::tuple_element<I, T>::type;
auto converted = try_get<value_type>(a[I]);
if(converted.is_err())
{
return err(converted.as_err());
}
return try_get_tuple_impl<T, I+1, N>::invoke(a, std::tuple_cat(
std::move(curr), std::make_tuple(std::move(converted.as_ok()))));
}
};
template<typename T, std::size_t I>
struct try_get_tuple_impl<T, I, I>
{
template<typename Array>
static result<T, error_info> invoke(const Array&, T x) noexcept
{
return ok(std::move(x));
}
};
} // detail
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_tuple<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
if(v.type() != toml::value_t::array)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::array));
}
const auto& ar = v.as_array(std::nothrow);
if(ar.size() != std::tuple_size<T>::value)
{
const auto loc = v.location();
return err(make_error_info("toml::try_get: while converting std::tuple: "
" there are " + std::to_string(ar.size()) + " > " +
std::to_string(std::tuple_size<T>::value) + " elements in toml array.",
loc, "here"));
}
return detail::try_get_tuple_impl<T, 0, std::tuple_size<T>::value>::invoke(
ar, std::make_tuple());
}
// ============================================================================
// map-like types; most likely STL map, like std::map or std::unordered_map.
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_map<T>, // T is map
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
using key_type = typename T::key_type;
using mapped_type = typename T::mapped_type;
static_assert(
std::is_convertible<typename basic_value<TC>::key_type, key_type>::value,
"toml::get only supports map type of which key_type is "
"convertible from toml::basic_value::key_type.");
if(v.type() != toml::value_t::table)
{
return err(detail::make_type_error(v, "try_get()", toml::value_t::table));
}
T m;
for(const auto& kv : v.as_table(std::nothrow))
{
auto converted = try_get<mapped_type>(kv.second);
if(converted.is_err())
{
return err(converted.as_err());
}
m.emplace(key_type(kv.first), std::move(converted.as_ok()));
}
return ok(m);
}
// ============================================================================
// user-defined type that defines `try_from<T>`.
template<typename T, typename TC>
cxx::enable_if_t<detail::has_specialized_try_from<T>::value, result<T, error_info>>
try_get(const basic_value<TC>& v) noexcept
{
return ::toml::try_from<T>::try_from_toml(v);
}
} // toml
#endif // TOML11_TRY_GET_HPP

View File

@@ -99,6 +99,47 @@ inline std::string make_string(std::size_t len, char c)
return std::string(len, c); return std::string(len, c);
} }
// ---------------------------------------------------------------------------
// It suppresses warnings by -Wsign-conversion when we pass integer literal
// to toml::find. integer literal `0` is deduced as an int, and will be
// converted to std::size_t. This causes sign-conversion.
template<typename TC>
std::size_t key_cast(const std::size_t& v) noexcept
{
return v;
}
template<typename TC, typename T>
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
key_cast(const T& v) noexcept
{
return static_cast<std::size_t>(v);
}
// for string-like (string, string literal, string_view)
template<typename TC>
typename basic_value<TC>::key_type const&
key_cast(const typename basic_value<TC>::key_type& v) noexcept
{
return v;
}
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const typename basic_value<TC>::key_type::value_type* v)
{
return typename basic_value<TC>::key_type(v);
}
#if defined(TOML11_HAS_STRING_VIEW)
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const std::string_view v)
{
return typename basic_value<TC>::key_type(v);
}
#endif // string_view
} // namespace detail } // namespace detail
} // namespace toml } // namespace toml
#endif // TOML11_UTILITY_HPP #endif // TOML11_UTILITY_HPP

View File

@@ -21,6 +21,8 @@
namespace toml namespace toml
{ {
template<typename TypeConfig>
class basic_value;
struct type_error final : public ::toml::exception struct type_error final : public ::toml::exception
{ {
@@ -42,6 +44,15 @@ struct type_error final : public ::toml::exception
// only for internal use // only for internal use
namespace detail namespace detail
{ {
template<typename TC>
error_info make_type_error(const basic_value<TC>&, const std::string&, const value_t);
template<typename TC>
error_info make_not_found_error(const basic_value<TC>&, const std::string&, const std::string&);
template<typename TC>
error_info make_not_found_error(const basic_value<TC>&, const std::string&, const std::size_t);
template<typename TC> template<typename TC>
void change_region_of_value(basic_value<TC>&, const basic_value<TC>&); void change_region_of_value(basic_value<TC>&, const basic_value<TC>&);
@@ -1192,7 +1203,7 @@ class basic_value
{ {
if(this->type_ != value_t::boolean) if(this->type_ != value_t::boolean)
{ {
this->throw_bad_cast("toml::value::as_boolean(): ", value_t::boolean); this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean);
} }
return this->boolean_.value; return this->boolean_.value;
} }
@@ -1200,7 +1211,7 @@ class basic_value
{ {
if(this->type_ != value_t::integer) if(this->type_ != value_t::integer)
{ {
this->throw_bad_cast("toml::value::as_integer(): ", value_t::integer); this->throw_bad_cast("toml::value::as_integer()", value_t::integer);
} }
return this->integer_.value; return this->integer_.value;
} }
@@ -1208,7 +1219,7 @@ class basic_value
{ {
if(this->type_ != value_t::floating) if(this->type_ != value_t::floating)
{ {
this->throw_bad_cast("toml::value::as_floating(): ", value_t::floating); this->throw_bad_cast("toml::value::as_floating()", value_t::floating);
} }
return this->floating_.value; return this->floating_.value;
} }
@@ -1216,7 +1227,7 @@ class basic_value
{ {
if(this->type_ != value_t::string) if(this->type_ != value_t::string)
{ {
this->throw_bad_cast("toml::value::as_string(): ", value_t::string); this->throw_bad_cast("toml::value::as_string()", value_t::string);
} }
return this->string_.value; return this->string_.value;
} }
@@ -1224,7 +1235,7 @@ class basic_value
{ {
if(this->type_ != value_t::offset_datetime) if(this->type_ != value_t::offset_datetime)
{ {
this->throw_bad_cast("toml::value::as_offset_datetime(): ", value_t::offset_datetime); this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime);
} }
return this->offset_datetime_.value; return this->offset_datetime_.value;
} }
@@ -1232,7 +1243,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_datetime) if(this->type_ != value_t::local_datetime)
{ {
this->throw_bad_cast("toml::value::as_local_datetime(): ", value_t::local_datetime); this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime);
} }
return this->local_datetime_.value; return this->local_datetime_.value;
} }
@@ -1240,7 +1251,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_date) if(this->type_ != value_t::local_date)
{ {
this->throw_bad_cast("toml::value::as_local_date(): ", value_t::local_date); this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date);
} }
return this->local_date_.value; return this->local_date_.value;
} }
@@ -1248,7 +1259,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_time) if(this->type_ != value_t::local_time)
{ {
this->throw_bad_cast("toml::value::as_local_time(): ", value_t::local_time); this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time);
} }
return this->local_time_.value; return this->local_time_.value;
} }
@@ -1256,7 +1267,7 @@ class basic_value
{ {
if(this->type_ != value_t::array) if(this->type_ != value_t::array)
{ {
this->throw_bad_cast("toml::value::as_array(): ", value_t::array); this->throw_bad_cast("toml::value::as_array()", value_t::array);
} }
return this->array_.value.get(); return this->array_.value.get();
} }
@@ -1264,7 +1275,7 @@ class basic_value
{ {
if(this->type_ != value_t::table) if(this->type_ != value_t::table)
{ {
this->throw_bad_cast("toml::value::as_table(): ", value_t::table); this->throw_bad_cast("toml::value::as_table()", value_t::table);
} }
return this->table_.value.get(); return this->table_.value.get();
} }
@@ -1276,7 +1287,7 @@ class basic_value
{ {
if(this->type_ != value_t::boolean) if(this->type_ != value_t::boolean)
{ {
this->throw_bad_cast("toml::value::as_boolean(): ", value_t::boolean); this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean);
} }
return this->boolean_.value; return this->boolean_.value;
} }
@@ -1284,7 +1295,7 @@ class basic_value
{ {
if(this->type_ != value_t::integer) if(this->type_ != value_t::integer)
{ {
this->throw_bad_cast("toml::value::as_integer(): ", value_t::integer); this->throw_bad_cast("toml::value::as_integer()", value_t::integer);
} }
return this->integer_.value; return this->integer_.value;
} }
@@ -1292,7 +1303,7 @@ class basic_value
{ {
if(this->type_ != value_t::floating) if(this->type_ != value_t::floating)
{ {
this->throw_bad_cast("toml::value::as_floating(): ", value_t::floating); this->throw_bad_cast("toml::value::as_floating()", value_t::floating);
} }
return this->floating_.value; return this->floating_.value;
} }
@@ -1300,7 +1311,7 @@ class basic_value
{ {
if(this->type_ != value_t::string) if(this->type_ != value_t::string)
{ {
this->throw_bad_cast("toml::value::as_string(): ", value_t::string); this->throw_bad_cast("toml::value::as_string()", value_t::string);
} }
return this->string_.value; return this->string_.value;
} }
@@ -1308,7 +1319,7 @@ class basic_value
{ {
if(this->type_ != value_t::offset_datetime) if(this->type_ != value_t::offset_datetime)
{ {
this->throw_bad_cast("toml::value::as_offset_datetime(): ", value_t::offset_datetime); this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime);
} }
return this->offset_datetime_.value; return this->offset_datetime_.value;
} }
@@ -1316,7 +1327,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_datetime) if(this->type_ != value_t::local_datetime)
{ {
this->throw_bad_cast("toml::value::as_local_datetime(): ", value_t::local_datetime); this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime);
} }
return this->local_datetime_.value; return this->local_datetime_.value;
} }
@@ -1324,7 +1335,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_date) if(this->type_ != value_t::local_date)
{ {
this->throw_bad_cast("toml::value::as_local_date(): ", value_t::local_date); this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date);
} }
return this->local_date_.value; return this->local_date_.value;
} }
@@ -1332,7 +1343,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_time) if(this->type_ != value_t::local_time)
{ {
this->throw_bad_cast("toml::value::as_local_time(): ", value_t::local_time); this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time);
} }
return this->local_time_.value; return this->local_time_.value;
} }
@@ -1340,7 +1351,7 @@ class basic_value
{ {
if(this->type_ != value_t::array) if(this->type_ != value_t::array)
{ {
this->throw_bad_cast("toml::value::as_array(): ", value_t::array); this->throw_bad_cast("toml::value::as_array()", value_t::array);
} }
return this->array_.value.get(); return this->array_.value.get();
} }
@@ -1348,7 +1359,7 @@ class basic_value
{ {
if(this->type_ != value_t::table) if(this->type_ != value_t::table)
{ {
this->throw_bad_cast("toml::value::as_table(): ", value_t::table); this->throw_bad_cast("toml::value::as_table()", value_t::table);
} }
return this->table_.value.get(); return this->table_.value.get();
} }
@@ -1411,7 +1422,7 @@ class basic_value
{ {
if(this->type_ != value_t::boolean) if(this->type_ != value_t::boolean)
{ {
this->throw_bad_cast("toml::value::as_boolean_fmt(): ", value_t::boolean); this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean);
} }
return this->boolean_.format; return this->boolean_.format;
} }
@@ -1419,7 +1430,7 @@ class basic_value
{ {
if(this->type_ != value_t::integer) if(this->type_ != value_t::integer)
{ {
this->throw_bad_cast("toml::value::as_integer_fmt(): ", value_t::integer); this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer);
} }
return this->integer_.format; return this->integer_.format;
} }
@@ -1427,7 +1438,7 @@ class basic_value
{ {
if(this->type_ != value_t::floating) if(this->type_ != value_t::floating)
{ {
this->throw_bad_cast("toml::value::as_floating_fmt(): ", value_t::floating); this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating);
} }
return this->floating_.format; return this->floating_.format;
} }
@@ -1435,7 +1446,7 @@ class basic_value
{ {
if(this->type_ != value_t::string) if(this->type_ != value_t::string)
{ {
this->throw_bad_cast("toml::value::as_string_fmt(): ", value_t::string); this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string);
} }
return this->string_.format; return this->string_.format;
} }
@@ -1443,7 +1454,7 @@ class basic_value
{ {
if(this->type_ != value_t::offset_datetime) if(this->type_ != value_t::offset_datetime)
{ {
this->throw_bad_cast("toml::value::as_offset_datetime_fmt(): ", value_t::offset_datetime); this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime);
} }
return this->offset_datetime_.format; return this->offset_datetime_.format;
} }
@@ -1451,7 +1462,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_datetime) if(this->type_ != value_t::local_datetime)
{ {
this->throw_bad_cast("toml::value::as_local_datetime_fmt(): ", value_t::local_datetime); this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime);
} }
return this->local_datetime_.format; return this->local_datetime_.format;
} }
@@ -1459,7 +1470,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_date) if(this->type_ != value_t::local_date)
{ {
this->throw_bad_cast("toml::value::as_local_date_fmt(): ", value_t::local_date); this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date);
} }
return this->local_date_.format; return this->local_date_.format;
} }
@@ -1467,7 +1478,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_time) if(this->type_ != value_t::local_time)
{ {
this->throw_bad_cast("toml::value::as_local_time_fmt(): ", value_t::local_time); this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time);
} }
return this->local_time_.format; return this->local_time_.format;
} }
@@ -1475,7 +1486,7 @@ class basic_value
{ {
if(this->type_ != value_t::array) if(this->type_ != value_t::array)
{ {
this->throw_bad_cast("toml::value::as_array_fmt(): ", value_t::array); this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array);
} }
return this->array_.format; return this->array_.format;
} }
@@ -1483,7 +1494,7 @@ class basic_value
{ {
if(this->type_ != value_t::table) if(this->type_ != value_t::table)
{ {
this->throw_bad_cast("toml::value::as_table_fmt(): ", value_t::table); this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table);
} }
return this->table_.format; return this->table_.format;
} }
@@ -1495,7 +1506,7 @@ class basic_value
{ {
if(this->type_ != value_t::boolean) if(this->type_ != value_t::boolean)
{ {
this->throw_bad_cast("toml::value::as_boolean_fmt(): ", value_t::boolean); this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean);
} }
return this->boolean_.format; return this->boolean_.format;
} }
@@ -1503,7 +1514,7 @@ class basic_value
{ {
if(this->type_ != value_t::integer) if(this->type_ != value_t::integer)
{ {
this->throw_bad_cast("toml::value::as_integer_fmt(): ", value_t::integer); this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer);
} }
return this->integer_.format; return this->integer_.format;
} }
@@ -1511,7 +1522,7 @@ class basic_value
{ {
if(this->type_ != value_t::floating) if(this->type_ != value_t::floating)
{ {
this->throw_bad_cast("toml::value::as_floating_fmt(): ", value_t::floating); this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating);
} }
return this->floating_.format; return this->floating_.format;
} }
@@ -1519,7 +1530,7 @@ class basic_value
{ {
if(this->type_ != value_t::string) if(this->type_ != value_t::string)
{ {
this->throw_bad_cast("toml::value::as_string_fmt(): ", value_t::string); this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string);
} }
return this->string_.format; return this->string_.format;
} }
@@ -1527,7 +1538,7 @@ class basic_value
{ {
if(this->type_ != value_t::offset_datetime) if(this->type_ != value_t::offset_datetime)
{ {
this->throw_bad_cast("toml::value::as_offset_datetime_fmt(): ", value_t::offset_datetime); this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime);
} }
return this->offset_datetime_.format; return this->offset_datetime_.format;
} }
@@ -1535,7 +1546,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_datetime) if(this->type_ != value_t::local_datetime)
{ {
this->throw_bad_cast("toml::value::as_local_datetime_fmt(): ", value_t::local_datetime); this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime);
} }
return this->local_datetime_.format; return this->local_datetime_.format;
} }
@@ -1543,7 +1554,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_date) if(this->type_ != value_t::local_date)
{ {
this->throw_bad_cast("toml::value::as_local_date_fmt(): ", value_t::local_date); this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date);
} }
return this->local_date_.format; return this->local_date_.format;
} }
@@ -1551,7 +1562,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_time) if(this->type_ != value_t::local_time)
{ {
this->throw_bad_cast("toml::value::as_local_time_fmt(): ", value_t::local_time); this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time);
} }
return this->local_time_.format; return this->local_time_.format;
} }
@@ -1559,7 +1570,7 @@ class basic_value
{ {
if(this->type_ != value_t::array) if(this->type_ != value_t::array)
{ {
this->throw_bad_cast("toml::value::as_array_fmt(): ", value_t::array); this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array);
} }
return this->array_.format; return this->array_.format;
} }
@@ -1567,7 +1578,7 @@ class basic_value
{ {
if(this->type_ != value_t::table) if(this->type_ != value_t::table)
{ {
this->throw_bad_cast("toml::value::as_table_fmt(): ", value_t::table); this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table);
} }
return this->table_.format; return this->table_.format;
} }
@@ -1579,13 +1590,13 @@ class basic_value
{ {
if(!this->is_table()) if(!this->is_table())
{ {
this->throw_bad_cast("toml::value::at(key_type): ", value_t::table); this->throw_bad_cast("toml::value::at(key_type)", value_t::table);
} }
auto& table = this->as_table(std::nothrow); auto& table = this->as_table(std::nothrow);
const auto found = table.find(k); const auto found = table.find(k);
if(found == table.end()) if(found == table.end())
{ {
this->throw_key_not_found_error(k); this->throw_key_not_found_error("toml::value::at", k);
} }
assert(found->first == k); assert(found->first == k);
return found->second; return found->second;
@@ -1594,17 +1605,55 @@ class basic_value
{ {
if(!this->is_table()) if(!this->is_table())
{ {
this->throw_bad_cast("toml::value::at(key_type): ", value_t::table); this->throw_bad_cast("toml::value::at(key_type)", value_t::table);
} }
const auto& table = this->as_table(std::nothrow); const auto& table = this->as_table(std::nothrow);
const auto found = table.find(k); const auto found = table.find(k);
if(found == table.end()) if(found == table.end())
{ {
this->throw_key_not_found_error(k); this->throw_key_not_found_error("toml::value::at", k);
} }
assert(found->first == k); assert(found->first == k);
return found->second; return found->second;
} }
result<std::reference_wrapper<value_type>, error_info>
try_at(const key_type& k) noexcept
{
if(!this->is_table())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::table));
}
auto& table = this->as_table(std::nothrow);
const auto found = table.find(k);
if(found == table.end())
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(key_type)", k));
}
assert(found->first == k);
return ok(std::ref(found->second));
}
result<std::reference_wrapper<const value_type>, error_info>
try_at(const key_type& k) const noexcept
{
if(!this->is_table())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::table));
}
const auto& table = this->as_table(std::nothrow);
const auto found = table.find(k);
if(found == table.end())
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(key_type)", k));
}
assert(found->first == k);
return ok(std::cref(found->second));
}
value_type& operator[](const key_type& k) value_type& operator[](const key_type& k)
{ {
if(this->is_empty()) if(this->is_empty())
@@ -1613,7 +1662,7 @@ class basic_value
} }
else if( ! this->is_table()) // initialized, but not a table else if( ! this->is_table()) // initialized, but not a table
{ {
this->throw_bad_cast("toml::value::operator[](key_type): ", value_t::table); this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table);
} }
return (this->as_table(std::nothrow))[k]; return (this->as_table(std::nothrow))[k];
} }
@@ -1621,7 +1670,7 @@ class basic_value
{ {
if(!this->is_table()) if(!this->is_table())
{ {
this->throw_bad_cast("toml::value::count(key_type): ", value_t::table); this->throw_bad_cast("toml::value::count(key_type)", value_t::table);
} }
return this->as_table(std::nothrow).count(k); return this->as_table(std::nothrow).count(k);
} }
@@ -1629,7 +1678,7 @@ class basic_value
{ {
if(!this->is_table()) if(!this->is_table())
{ {
this->throw_bad_cast("toml::value::contains(key_type): ", value_t::table); this->throw_bad_cast("toml::value::contains(key_type)", value_t::table);
} }
const auto& table = this->as_table(std::nothrow); const auto& table = this->as_table(std::nothrow);
return table.find(k) != table.end(); return table.find(k) != table.end();
@@ -1642,7 +1691,7 @@ class basic_value
{ {
if(!this->is_array()) if(!this->is_array())
{ {
this->throw_bad_cast("toml::value::at(idx): ", value_t::array); this->throw_bad_cast("toml::value::at(idx)", value_t::array);
} }
auto& ar = this->as_array(std::nothrow); auto& ar = this->as_array(std::nothrow);
@@ -1662,7 +1711,7 @@ class basic_value
{ {
if(!this->is_array()) if(!this->is_array())
{ {
this->throw_bad_cast("toml::value::at(idx): ", value_t::array); this->throw_bad_cast("toml::value::at(idx)", value_t::array);
} }
const auto& ar = this->as_array(std::nothrow); const auto& ar = this->as_array(std::nothrow);
@@ -1680,6 +1729,41 @@ class basic_value
return ar.at(idx); return ar.at(idx);
} }
result<std::reference_wrapper<value_type>, error_info>
try_at(const std::size_t& idx) noexcept
{
if(!this->is_array())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::array));
}
auto& ar = this->as_array(std::nothrow);
if(ar.size() <= idx)
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(idx)", idx));
}
return ok(std::ref(ar[idx]));
}
result<std::reference_wrapper<const value_type>, error_info>
try_at(const std::size_t idx) const noexcept
{
if(!this->is_array())
{
return err(detail::make_type_error(*this,
"toml::value::try_at(key_type)", value_t::array));
}
const auto& ar = this->as_array(std::nothrow);
if(ar.size() <= idx)
{
return err(detail::make_not_found_error(*this,
"toml::value::try_at(idx)", idx));
}
return ok(std::cref(ar[idx]));
}
value_type& operator[](const std::size_t idx) noexcept value_type& operator[](const std::size_t idx) noexcept
{ {
// no check... // no check...
@@ -1695,7 +1779,7 @@ class basic_value
{ {
if(!this->is_array()) if(!this->is_array())
{ {
this->throw_bad_cast("toml::value::push_back(idx): ", value_t::array); this->throw_bad_cast("toml::value::push_back(idx)", value_t::array);
} }
this->as_array(std::nothrow).push_back(x); this->as_array(std::nothrow).push_back(x);
return; return;
@@ -1704,7 +1788,7 @@ class basic_value
{ {
if(!this->is_array()) if(!this->is_array())
{ {
this->throw_bad_cast("toml::value::push_back(idx): ", value_t::array); this->throw_bad_cast("toml::value::push_back(idx)", value_t::array);
} }
this->as_array(std::nothrow).push_back(std::move(x)); this->as_array(std::nothrow).push_back(std::move(x));
return; return;
@@ -1715,7 +1799,7 @@ class basic_value
{ {
if(!this->is_array()) if(!this->is_array())
{ {
this->throw_bad_cast("toml::value::emplace_back(idx): ", value_t::array); this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array);
} }
auto& ar = this->as_array(std::nothrow); auto& ar = this->as_array(std::nothrow);
ar.emplace_back(std::forward<Ts>(args) ...); ar.emplace_back(std::forward<Ts>(args) ...);
@@ -1793,52 +1877,15 @@ class basic_value
[[noreturn]] [[noreturn]]
void throw_bad_cast(const std::string& funcname, const value_t ty) const void throw_bad_cast(const std::string& funcname, const value_t ty) const
{ {
throw type_error(format_error(make_error_info( throw type_error(format_error(detail::make_type_error(*this, funcname, ty)),
funcname + "bad_cast to " + to_string(ty),
this->location(), "the actual type is " + to_string(this->type()))),
this->location()); this->location());
} }
[[noreturn]] [[noreturn]]
void throw_key_not_found_error(const key_type& key) const void throw_key_not_found_error(const std::string& funcname, const key_type& key) const
{ {
const auto loc = this->location(); throw std::out_of_range(format_error(
detail::make_not_found_error(*this, funcname, key)));
std::ostringstream oss;
oss << color::red << "[error] " << color::reset
<< "key \"" << key << "\" not found";
// the value is not from a file. no information can be appended.
if( ! loc.is_ok())
{
throw std::out_of_range(oss.str());
}
// The top-level table has its region at the 0th character of the file.
// That means that, in the case when a key is not found in the top-level
// table, the error message points to the first character. If the file has
// the first table at the first line, the error message would be like this.
// ```console
// [error] key "a" not found
// --> example.toml
// |
// 1 | [table]
// | ^------ in this table
// ```
// It actually points to the top-level table at the first character, not
// `[table]`. But it is too confusing. To avoid the confusion, the error
// message should explicitly say "key not found in the top-level table".
if(loc.first_line_number() == 1 && loc.length() == 0)
{
oss << " at the top-level table in \"" << loc.file_name() << "\"";
throw std::out_of_range(oss.str());
}
else
{
// normal table.
oss << format_location(loc, "in this table");
throw std::out_of_range(oss.str());
}
} }
template<typename TC> template<typename TC>
@@ -2079,6 +2126,62 @@ std::string format_error(std::string title,
namespace detail namespace detail
{ {
template<typename TC>
error_info make_type_error(const basic_value<TC>& v, const std::string& fname, const value_t ty)
{
return make_error_info(fname + ": bad_cast to " + to_string(ty),
v.location(), "the actual type is " + to_string(v.type()));
}
template<typename TC>
error_info make_not_found_error(const basic_value<TC>& v, const std::string& fname, const std::string& key)
{
const auto loc = v.location();
const std::string title = fname + ": key \"" + key + "\" not found";
std::vector<std::pair<source_location, std::string>> locs;
if( ! loc.is_ok())
{
return error_info(title, locs);
}
if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1)
{
// The top-level table has its region at the 0th character of the file.
// That means that, in the case when a key is not found in the top-level
// table, the error message points to the first character. If the file has
// the first table at the first line, the error message would be like this.
// ```console
// [error] key "a" not found
// --> example.toml
// |
// 1 | [table]
// | ^------ in this table
// ```
// It actually points to the top-level table at the first character, not
// `[table]`. But it is too confusing. To avoid the confusion, the error
// message should explicitly say "key not found in the top-level table".
locs.emplace_back(v.location(), "at the top-level table");
}
else
{
locs.emplace_back(v.location(), "in this table");
}
return error_info(title, locs);
}
template<typename TC>
error_info make_not_found_error(const basic_value<TC>& v, const std::string& fname, const std::size_t idx)
{
if( ! v.is_array())
{
return make_type_error(v, fname, toml::value_t::array);
}
std::ostringstream oss;
oss << "actual length (" << v.as_array(std::nothrow).size()
<< ") is shorter than the specified index (" << idx << ").";
return make_error_info(fname + ": no element corresponding to the index",
v, oss.str());
}
#define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \ #define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \
template<typename TC> \ template<typename TC> \
struct getter<TC, value_t::ty> \ struct getter<TC, value_t::ty> \

View File

@@ -3,7 +3,7 @@
#define TOML11_VERSION_MAJOR 4 #define TOML11_VERSION_MAJOR 4
#define TOML11_VERSION_MINOR 0 #define TOML11_VERSION_MINOR 0
#define TOML11_VERSION_PATCH 0 #define TOML11_VERSION_PATCH 1
#ifndef __cplusplus #ifndef __cplusplus
# error "__cplusplus is not defined" # error "__cplusplus is not defined"

File diff suppressed because it is too large Load Diff

View File

@@ -2,11 +2,13 @@ set(TOML11_TEST_NAMES
test_comments test_comments
test_datetime test_datetime
test_find test_find
test_try_find
test_find_or test_find_or
test_format_integer test_format_integer
test_format_floating test_format_floating
test_format_table test_format_table
test_get test_get
test_try_get
test_get_or test_get_or
test_location test_location
test_literal test_literal

View File

@@ -17,6 +17,15 @@
#include <string_view> #include <string_view>
#endif #endif
namespace toml
{
namespace detail
{
std::tm localtime_s(const std::time_t* src);
std::tm gmtime_s(const std::time_t* src);
} // detail
} // toml
TEST_CASE("testing toml::find with toml type") TEST_CASE("testing toml::find with toml type")
{ {
using value_type = toml::value; using value_type = toml::value;

View File

@@ -17,6 +17,15 @@
#include <string_view> #include <string_view>
#endif #endif
namespace toml
{
namespace detail
{
std::tm localtime_s(const std::time_t* src);
std::tm gmtime_s(const std::time_t* src);
} // detail
} // toml
TEST_CASE("testing toml::get with toml types") TEST_CASE("testing toml::get with toml types")
{ {
using value_type = toml::value; using value_type = toml::value;

829
tests/test_try_find.cpp Normal file
View File

@@ -0,0 +1,829 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include "utility.hpp"
#include <toml11/value.hpp>
#include <toml11/try_find.hpp>
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#include <unordered_map>
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif
namespace toml
{
namespace detail
{
std::tm localtime_s(const std::time_t* src);
std::tm gmtime_s(const std::time_t* src);
} // detail
} // toml
TEST_CASE("testing toml::try_find with toml type")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
using floating_type = typename value_type::floating_type ;
using string_type = typename value_type::string_type ;
using local_time_type = typename value_type::local_time_type ;
using local_date_type = typename value_type::local_date_type ;
using local_datetime_type = typename value_type::local_datetime_type ;
using offset_datetime_type = typename value_type::offset_datetime_type;
using array_type = typename value_type::array_type ;
using table_type = typename value_type::table_type ;
{
value_type v(toml::table{{"a", true}});
CHECK_EQ(true, toml::try_find<boolean_type>(v, "a").unwrap());
toml::try_find<boolean_type>(v, "a").unwrap() = false;
CHECK_EQ(false, toml::try_find<boolean_type>(v, "a").unwrap());
boolean_type x = toml::try_find<boolean_type>(std::move(v), "a").unwrap();
CHECK_EQ(false, x);
}
{
value_type v(toml::table{{"a", 42}});
CHECK_EQ(integer_type(42), toml::try_find<integer_type>(v, "a").unwrap());
toml::try_find<integer_type>(v, "a").unwrap() = 54;
CHECK_EQ(integer_type(54), toml::try_find<integer_type>(v, "a").unwrap());
integer_type x = toml::try_find<integer_type>(std::move(v), "a").unwrap();
CHECK_EQ(integer_type(54), x);
}
{
value_type v(toml::table{{"a", 3.14}});
CHECK_EQ(floating_type(3.14), toml::try_find<floating_type>(v, "a").unwrap());
toml::try_find<floating_type>(v, "a").unwrap() = 2.71;
CHECK_EQ(floating_type(2.71), toml::try_find<floating_type>(v, "a").unwrap());
floating_type x = toml::try_find<floating_type>(std::move(v), "a").unwrap();
CHECK_EQ(floating_type(2.71), x);
}
{
value_type v(toml::table{{"a", "foo"}});
CHECK_EQ("foo", toml::try_find<string_type>(v, "a").unwrap());
toml::try_find<string_type>(v, "a").unwrap() += "bar";
CHECK_EQ("foobar", toml::try_find<string_type>(v, "a").unwrap());
string_type x = toml::try_find<string_type>(std::move(v), "a").unwrap();
CHECK_EQ("foobar", x);
}
{
local_date_type d(2018, toml::month_t::Apr, 22);
value_type v(toml::table{{"a", d}});
CHECK_EQ(d, toml::try_find<local_date_type>(v, "a").unwrap());
toml::try_find<local_date_type>(v, "a").unwrap().year = 2017;
d.year = 2017;
CHECK_EQ(d, toml::try_find<local_date_type>(v, "a").unwrap());
local_date_type x = toml::try_find<local_date_type>(std::move(v), "a").unwrap();
CHECK_EQ(d, x);
}
{
local_time_type t(12, 30, 45);
value_type v(toml::table{{"a", t}});
CHECK_EQ(t, toml::try_find<local_time_type>(v, "a").unwrap());
toml::try_find<local_time_type>(v, "a").unwrap().hour = 9;
t.hour = 9;
CHECK_EQ(t, toml::try_find<local_time_type>(v, "a").unwrap());
local_time_type x = toml::try_find<local_time_type>(std::move(v), "a").unwrap();
CHECK_EQ(t, x);
}
{
local_datetime_type dt(toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45));
value_type v(toml::table{{"a", dt}});
CHECK_EQ(dt, toml::try_find<local_datetime_type>(v, "a").unwrap());
toml::try_find<local_datetime_type>(v, "a").unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_find<local_datetime_type>(v, "a").unwrap());
toml::local_datetime x = toml::try_find<local_datetime_type>(std::move(v), "a").unwrap();
CHECK_EQ(dt, x);
}
{
offset_datetime_type dt(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45)), toml::time_offset(9, 0));
value_type v(toml::table{{"a", dt}});
CHECK_EQ(dt, toml::try_find<offset_datetime_type>(v, "a").unwrap());
toml::try_find<toml::offset_datetime>(v, "a").unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_find<offset_datetime_type>(v, "a").unwrap());
offset_datetime_type x = toml::try_find<offset_datetime_type>(std::move(v), "a").unwrap();
CHECK_EQ(dt, x);
}
{
array_type vec;
vec.push_back(value_type(42));
vec.push_back(value_type(54));
value_type v(toml::table{{"a", vec}});
CHECK_EQ(vec, toml::try_find<array_type>(v, "a").unwrap());
toml::try_find<array_type>(v, "a").unwrap().push_back(value_type(123));
vec.push_back(value_type(123));
CHECK_EQ(vec, toml::try_find<array_type>(v, "a").unwrap());
array_type x = toml::try_find<array_type>(std::move(v), "a").unwrap();
CHECK_EQ(vec, x);
}
{
table_type tab;
tab["key1"] = value_type(42);
tab["key2"] = value_type(3.14);
value_type v(toml::table{{"a", tab}});
CHECK_EQ(tab, toml::try_find<table_type>(v, "a").unwrap());
toml::try_find<table_type>(v, "a").unwrap()["key3"] = value_type(123);
tab["key3"] = value_type(123);
CHECK_EQ(tab, toml::try_find<table_type>(v, "a").unwrap());
table_type x = toml::try_find<table_type>(std::move(v), "a").unwrap();
CHECK_EQ(tab, x);
}
{
value_type v1(toml::table{{"a", 42}});
CHECK_EQ(toml::value(42), toml::try_find(v1, "a").unwrap());
value_type v2(54);
toml::try_find(v1, "a").unwrap() = v2;
CHECK_EQ(v2, toml::try_find(v1, "a").unwrap());
value_type x = toml::try_find(std::move(v1), "a").unwrap();
CHECK_EQ(v2, x);
}
}
TEST_CASE("testing try_find fails")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
// -----------------------------------------------------------------------
// const-reference version
{
// value is not a table
const toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not the expected type
const toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not found
const toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<integer_type>(v, "different_key").is_err());
}
{
// the positive control.
const toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<int>(v, "key").is_ok());
}
// -----------------------------------------------------------------------
// reference version
{
// value is not a table
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<boolean_type>(v, "key").is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<integer_type>(v, "different_key").is_err());
}
{
// the positive control.
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<int>(v, "key").is_ok());
}
// -----------------------------------------------------------------------
// move version
{
// value is not a table
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), "key").is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), "key").is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<integer_type>(std::move(v), "different_key").is_err());
}
{
// the positive control.
toml::value v = toml::table{{"key", 42}};
CHECK_UNARY(toml::try_find<int>(std::move(v), "key").is_ok());
}
}
TEST_CASE("testing toml::try_find(v, idx) throws")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
// -----------------------------------------------------------------------
// const-reference version
{
// value is not an array
const toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not the expected type
const toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not found
const toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<integer_type>(v, 6).is_err());
}
{
// the positive control.
const toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<int>(v, 2).is_ok());
}
// -----------------------------------------------------------------------
// non-const reference version
{
// value is not an array
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<boolean_type>(v, 0).is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<integer_type>(v, 6).is_err());
}
{
// the positive control.
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<int>(v, 2).is_ok());
}
// -----------------------------------------------------------------------
// move version
{
// value is not an array
toml::value v(true);
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), 0).is_err());
}
{
// the value corresponding to the key is not the expected type
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<boolean_type>(std::move(v), 0).is_err());
}
{
// the value corresponding to the key is not found
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<integer_type>(std::move(v), 6).is_err());
}
{
// the positive control.
toml::value v = toml::array{1, 2, 3, 4, 5};
CHECK_UNARY(toml::try_find<int>(std::move(v), 2).is_ok());
}
}
TEST_CASE("testing toml::try_find with recursive table/array")
{
using value_type = toml::value;
using integer_type = typename value_type::integer_type ;
// recursively search tables
{
toml::value v = toml::table{
{"a", toml::table{
{"b", toml::table{
{"c", toml::table{
{"d", 42}
}}
}}
}}
};
CHECK_EQ(42, toml::try_find<int>(v, "a", "b", "c", "d").unwrap());
// reference that can be used to modify the content
auto num = toml::try_find<integer_type>(v, "a", "b", "c", "d");
CHECK_UNARY(num.is_ok());
num.unwrap() = 54;
CHECK_EQ(54, toml::try_find<int>(v, "a", "b", "c", "d").unwrap());
const std::string a("a"), b("b"), c("c"), d("d");
auto num2 = toml::try_find<integer_type>(v, a, b, c, d);
CHECK_UNARY(num2.is_ok());
num2.unwrap() = 42;
CHECK_EQ(42, toml::try_find<int>(v, a, b, c, d).unwrap());
auto num3 = toml::try_find<integer_type>(v, a, "b", c, "d");
CHECK_EQ(42, num3.unwrap());
auto num4 = toml::try_find<integer_type>(std::move(v), a, b, c, d);
CHECK_EQ(42, num4.unwrap());
}
// recursively search arrays
{
toml::value v = toml::array{
toml::array{"array", "of", "string"},
toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}}
};
CHECK_EQ("array" , toml::try_find<std::string>(v, 0, 0).unwrap());
CHECK_EQ("of" , toml::try_find<std::string>(v, 0, 1).unwrap());
CHECK_EQ("string", toml::try_find<std::string>(v, 0, 2).unwrap());
CHECK_EQ(1, toml::try_find<int>(v, 1, 0, 0).unwrap());
CHECK_EQ(2, toml::try_find<int>(v, 1, 0, 1).unwrap());
CHECK_EQ(3, toml::try_find<int>(v, 1, 0, 2).unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, 1, 1, 0).unwrap());
CHECK_EQ(2.71, toml::try_find<double>(v, 1, 1, 1).unwrap());
// reference that can be used to modify the content
auto num = toml::try_find<integer_type>(v, 1, 0, 2);
num.unwrap() = 42;
CHECK_EQ( 1, toml::try_find<int>(v, 1, 0, 0).unwrap());
CHECK_EQ( 2, toml::try_find<int>(v, 1, 0, 1).unwrap());
CHECK_EQ(42, toml::try_find<int>(v, 1, 0, 2).unwrap());
// move value
auto num2 = toml::try_find<integer_type>(std::move(v), 1, 0, 2);
CHECK_EQ(42, num2.unwrap());
}
// recursively search mixtures
{
toml::value v = toml::table{{"array", toml::array{
toml::array{1, 2, 3},
toml::array{
toml::table{{"foo", "bar"}, {"baz", "qux"}},
toml::table{{"pi", 3.14}, {"e", 2.71}}
}}
}};
CHECK_EQ(1, toml::try_find<int>(v, "array", 0, 0).unwrap());
CHECK_EQ(2, toml::try_find<int>(v, "array", 0, 1).unwrap());
CHECK_EQ(3, toml::try_find<int>(v, "array", 0, 2).unwrap());
CHECK_EQ("bar", toml::try_find<std::string>(v, "array", 1, 0, "foo").unwrap());
CHECK_EQ("qux", toml::try_find<std::string>(v, "array", 1, 0, "baz").unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, "array", 1, 1, "pi").unwrap());
CHECK_EQ(2.71, toml::try_find<double>(v, "array", 1, 1, "e" ).unwrap());
const std::string ar("array");
const auto ar_c = "array";
const std::string pi("pi");
const auto pi_c = "pi";
CHECK_EQ(3.14, toml::try_find<double>(v, ar, 1, 1, "pi").unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar, 1, 1, pi) .unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar, 1, 1, pi_c).unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar_c, 1, 1, "pi").unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar_c, 1, 1, pi) .unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, ar_c, 1, 1, pi_c).unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, "array", 1, 1, pi) .unwrap());
CHECK_EQ(3.14, toml::try_find<double>(v, "array", 1, 1, pi_c).unwrap());
}
}
TEST_CASE("testing toml::try_find integer conversion")
{
using value_type = toml::value;
{
value_type v = toml::table{{"key", 42}};
CHECK_EQ(int(42) , toml::try_find<int >(v, "key").unwrap());
CHECK_EQ(short(42) , toml::try_find<short >(v, "key").unwrap());
CHECK_EQ(char(42) , toml::try_find<char >(v, "key").unwrap());
CHECK_EQ(unsigned(42) , toml::try_find<unsigned >(v, "key").unwrap());
CHECK_EQ(long(42) , toml::try_find<long >(v, "key").unwrap());
CHECK_EQ(std::int64_t(42) , toml::try_find<std::int64_t >(v, "key").unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_find<std::uint64_t>(v, "key").unwrap());
CHECK_EQ(std::int16_t(42) , toml::try_find<std::int16_t >(v, "key").unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_find<std::uint16_t>(v, "key").unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_find<std::uint16_t>(std::move(v), "key").unwrap());
}
}
TEST_CASE("testing toml::try_find floating conversion")
{
using value_type = toml::value;
{
value_type v = toml::table{{"key", 3.14}};
const double ref(3.14);
CHECK_EQ(static_cast<float >(ref), toml::try_find<float >(v, "key").unwrap());
CHECK_EQ( ref , toml::try_find<double >(v, "key").unwrap());
CHECK_EQ(static_cast<long double>(ref), toml::try_find<long double>(v, "key").unwrap());
CHECK_EQ(static_cast<float >(ref), toml::try_find<float >(std::move(v), "key").unwrap());
}
}
TEST_CASE("testing toml::try_find string conversion")
{
using value_type = toml::value;
{
value_type v = toml::table{{"key", "foo"}};
CHECK_EQ("foo", toml::try_find<std::string>(v, "key").unwrap());
toml::try_find<std::string>(v, "key").unwrap() += "bar";
CHECK_EQ("foobar", toml::try_find<std::string>(v, "key").unwrap());
}
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
{
value_type v = toml::table{{"key", "foo"}};
CHECK_EQ("foo", toml::try_find<std::string_view>(v, "key").unwrap());
}
#endif
}
TEST_CASE("testing toml::try_find array conversion")
{
using value_type = toml::value;
value_type v = toml::table{{"key", toml::array{42, 54, 69, 72}}};
const std::vector<int> vec = toml::try_find<std::vector<int>>(v, "key").unwrap();
const std::list<short> lst = toml::try_find<std::list<short>>(v, "key").unwrap();
const std::deque<std::int64_t> deq = toml::try_find<std::deque<std::int64_t>>(v, "key").unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
CHECK_EQ(static_cast<short>(42), *(iter++));
CHECK_EQ(static_cast<short>(54), *(iter++));
CHECK_EQ(static_cast<short>(69), *(iter++));
CHECK_EQ(static_cast<short>(72), *(iter++));
CHECK_EQ(static_cast<std::int64_t>(42), deq.at(0));
CHECK_EQ(static_cast<std::int64_t>(54), deq.at(1));
CHECK_EQ(static_cast<std::int64_t>(69), deq.at(2));
CHECK_EQ(static_cast<std::int64_t>(72), deq.at(3));
std::array<int, 4> ary = toml::try_find<std::array<int, 4>>(v, "key").unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::try_find<std::tuple<int, short, unsigned, long>>(v, "key").unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
value_type p = toml::table{{"key", toml::array{3.14, 2.71}}};
std::pair<double, double> pr = toml::try_find<std::pair<double, double> >(p, "key").unwrap();
CHECK_EQ(3.14, pr.first);
CHECK_EQ(2.71, pr.second);
}
TEST_CASE("testing toml::try_find array move conversion")
{
using value_type = toml::value;
value_type v1 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v2 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v3 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v4 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
value_type v5 = toml::table{{"key", toml::array{42, 54, 69, 72}}};
const std::vector<int> vec = toml::try_find<std::vector<int>>(std::move(v1), "key").unwrap();
const std::list<short> lst = toml::try_find<std::list<short>>(std::move(v2), "key").unwrap();
const std::deque<std::int64_t> deq = toml::try_find<std::deque<std::int64_t>>(std::move(v3), "key").unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
CHECK_EQ(static_cast<short>(42), *(iter++));
CHECK_EQ(static_cast<short>(54), *(iter++));
CHECK_EQ(static_cast<short>(69), *(iter++));
CHECK_EQ(static_cast<short>(72), *(iter++));
CHECK_EQ(static_cast<std::int64_t>(42), deq.at(0));
CHECK_EQ(static_cast<std::int64_t>(54), deq.at(1));
CHECK_EQ(static_cast<std::int64_t>(69), deq.at(2));
CHECK_EQ(static_cast<std::int64_t>(72), deq.at(3));
std::array<int, 4> ary = toml::try_find<std::array<int, 4>>(std::move(v4), "key").unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::try_find<std::tuple<int, short, unsigned, long>>(std::move(v5), "key").unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
value_type p = toml::table{{"key", toml::array{3.14, 2.71}}};
std::pair<double, double> pr = toml::try_find<std::pair<double, double> >(std::move(p), "key").unwrap();
CHECK_EQ(3.14, pr.first);
CHECK_EQ(2.71, pr.second);
}
TEST_CASE("testing toml::try_find array of array conversion")
{
using value_type = toml::value;
value_type v1 = toml::array{42, 54, 69, 72};
value_type v2 = toml::array{"foo", "bar", "baz"};
value_type v = toml::table{{"key", toml::array{v1, v2}}};
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_find<std::pair<std::vector<int>, std::vector<std::string>>>(v, "key").unwrap();
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::try_find<std::tuple<std::vector<int>, std::vector<std::string>>>(v, "key").unwrap();
CHECK_EQ(std::get<0>(t).at(0), 42);
CHECK_EQ(std::get<0>(t).at(1), 54);
CHECK_EQ(std::get<0>(t).at(2), 69);
CHECK_EQ(std::get<0>(t).at(3), 72);
CHECK_EQ(std::get<1>(t).at(0), "foo");
CHECK_EQ(std::get<1>(t).at(1), "bar");
CHECK_EQ(std::get<1>(t).at(2), "baz");
}
TEST_CASE("testing toml::try_find array of array move conversion")
{
using value_type = toml::value;
value_type a1 = toml::array{42, 54, 69, 72};
value_type a2 = toml::array{"foo", "bar", "baz"};
value_type v1 = toml::table{{"key", toml::array{a1, a2}}};
value_type v2 = toml::table{{"key", toml::array{a1, a2}}};
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_find<std::pair<std::vector<int>, std::vector<std::string>>>(std::move(v1), "key").unwrap();
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::try_find<std::tuple<std::vector<int>, std::vector<std::string>>>(std::move(v2), "key").unwrap();
CHECK_EQ(std::get<0>(t).at(0), 42);
CHECK_EQ(std::get<0>(t).at(1), 54);
CHECK_EQ(std::get<0>(t).at(2), 69);
CHECK_EQ(std::get<0>(t).at(3), 72);
CHECK_EQ(std::get<1>(t).at(0), "foo");
CHECK_EQ(std::get<1>(t).at(1), "bar");
CHECK_EQ(std::get<1>(t).at(2), "baz");
}
TEST_CASE("testing toml::try_find table conversion")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::table{
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::try_find<std::map<std::string, int>>(v1, "key").unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
{
value_type v1 = toml::table{{"key", toml::table{
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::try_find<std::map<std::string, int>>(std::move(v1), "key").unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
}
TEST_CASE("testing toml::try_find local_date")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
{
value_type v1 = toml::table{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(std::move(v1), "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
}
TEST_CASE("testing toml::try_find local_time")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::try_find<std::chrono::seconds>(v1, "key").unwrap();
CHECK_EQ(time, std::chrono::hours(12) +
std::chrono::minutes(30) + std::chrono::seconds(45));
}
{
value_type v1 = toml::table{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::try_find<std::chrono::seconds>(std::move(v1), "key").unwrap();
CHECK_EQ(time, std::chrono::hours(12) +
std::chrono::minutes(30) + std::chrono::seconds(45));
}
}
TEST_CASE("testing toml::try_find local_datetime")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::local_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 45})}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 12;
t.tm_min = 30;
t.tm_sec = 45;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
{
value_type v1 = toml::table{{"key", toml::local_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 45})}};
const auto date = std::chrono::system_clock::to_time_t(
toml::try_find<std::chrono::system_clock::time_point>(std::move(v1), "key").unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 12;
t.tm_min = 30;
t.tm_sec = 45;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
}
TEST_CASE("testing toml::try_find offset_datetime")
{
using value_type = toml::value;
{
value_type v1 = toml::table{{"key", toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{9, 0})}};
// 2018-04-01T12:30:00+09:00
//, 2018-04-01T03:30:00Z
const auto date = toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 3);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
{
value_type v1 = toml::table{{"key", toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{-8, 0})}};
// 2018-04-01T12:30:00-08:00
//, 2018-04-01T20:30:00Z
const auto date = toml::try_find<std::chrono::system_clock::time_point>(v1, "key").unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 20);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
{
value_type v1 = toml::table{{"key", toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{-8, 0})}};
// 2018-04-01T12:30:00-08:00
//, 2018-04-01T20:30:00Z
const auto date = toml::try_find<std::chrono::system_clock::time_point>(std::move(v1), "key").unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 20);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
}

529
tests/test_try_get.cpp Normal file
View File

@@ -0,0 +1,529 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include "utility.hpp"
#include <toml11/value.hpp>
#include <toml11/try_get.hpp>
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#include <unordered_map>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
namespace toml
{
namespace detail
{
std::tm localtime_s(const std::time_t* src);
std::tm gmtime_s(const std::time_t* src);
} // detail
} // toml
TEST_CASE("testing toml::try_get with toml types")
{
using value_type = toml::value;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
using floating_type = typename value_type::floating_type ;
using string_type = typename value_type::string_type ;
using local_time_type = typename value_type::local_time_type ;
using local_date_type = typename value_type::local_date_type ;
using local_datetime_type = typename value_type::local_datetime_type ;
using offset_datetime_type = typename value_type::offset_datetime_type;
using array_type = typename value_type::array_type ;
using table_type = typename value_type::table_type ;
{
value_type v(true);
CHECK_EQ(true, toml::try_get<boolean_type>(v).unwrap());
toml::try_get<boolean_type>(v).unwrap() = false;
CHECK_EQ(false, toml::try_get<boolean_type>(v).unwrap());
boolean_type x = toml::try_get<boolean_type>(std::move(v)).unwrap();
CHECK_EQ(false, x);
}
{
value_type v(42);
CHECK_EQ(integer_type(42), toml::try_get<integer_type>(v).unwrap());
toml::try_get<integer_type>(v).unwrap() = 54;
CHECK_EQ(integer_type(54), toml::try_get<integer_type>(v).unwrap());
integer_type x = toml::try_get<integer_type>(std::move(v)).unwrap();
CHECK_EQ(integer_type(54), x);
}
{
value_type v(3.14);
CHECK_EQ(floating_type(3.14), toml::try_get<floating_type>(v).unwrap());
toml::try_get<floating_type>(v).unwrap() = 2.71;
CHECK_EQ(floating_type(2.71), toml::try_get<floating_type>(v).unwrap());
floating_type x = toml::try_get<floating_type>(std::move(v)).unwrap();
CHECK_EQ(floating_type(2.71), x);
}
{
value_type v("foo");
CHECK_EQ("foo", toml::try_get<string_type>(v).unwrap());
toml::try_get<string_type>(v).unwrap() += "bar";
CHECK_EQ("foobar", toml::try_get<string_type>(v).unwrap());
string_type x = toml::try_get<string_type>(std::move(v)).unwrap();
CHECK_EQ("foobar", x);
}
{
local_date_type d(2018, toml::month_t::Apr, 22);
value_type v(d);
CHECK_EQ(d, toml::try_get<local_date_type>(v).unwrap());
toml::try_get<local_date_type>(v).unwrap().year = 2017;
d.year = 2017;
CHECK_EQ(d, toml::try_get<local_date_type>(v).unwrap());
local_date_type x = toml::try_get<local_date_type>(std::move(v)).unwrap();
CHECK_EQ(d, x);
}
{
local_time_type t(12, 30, 45);
value_type v(t);
CHECK_EQ(t, toml::try_get<local_time_type>(v).unwrap());
toml::try_get<local_time_type>(v).unwrap().hour = 9;
t.hour = 9;
CHECK_EQ(t, toml::try_get<local_time_type>(v).unwrap());
local_time_type x = toml::try_get<local_time_type>(std::move(v)).unwrap();
CHECK_EQ(t, x);
}
{
local_datetime_type dt(toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45));
value_type v(dt);
CHECK_EQ(dt, toml::try_get<local_datetime_type>(v).unwrap());
toml::try_get<local_datetime_type>(v).unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_get<local_datetime_type>(v).unwrap());
toml::local_datetime x = toml::try_get<local_datetime_type>(std::move(v)).unwrap();
CHECK_EQ(dt, x);
}
{
offset_datetime_type dt(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 30, 45)), toml::time_offset(9, 0));
value_type v(dt);
CHECK_EQ(dt, toml::try_get<offset_datetime_type>(v).unwrap());
toml::try_get<toml::offset_datetime>(v).unwrap().date.year = 2017;
dt.date.year = 2017;
CHECK_EQ(dt, toml::try_get<offset_datetime_type>(v).unwrap());
offset_datetime_type x = toml::try_get<offset_datetime_type>(std::move(v)).unwrap();
CHECK_EQ(dt, x);
}
{
array_type vec;
vec.push_back(value_type(42));
vec.push_back(value_type(54));
value_type v(vec);
CHECK_EQ(vec, toml::try_get<array_type>(v).unwrap());
toml::try_get<array_type>(v).unwrap().push_back(value_type(123));
vec.push_back(value_type(123));
CHECK_EQ(vec, toml::try_get<array_type>(v).unwrap());
array_type x = toml::try_get<array_type>(std::move(v)).unwrap();
CHECK_EQ(vec, x);
}
{
table_type tab;
tab["key1"] = value_type(42);
tab["key2"] = value_type(3.14);
value_type v(tab);
CHECK_EQ(tab, toml::try_get<table_type>(v).unwrap());
toml::try_get<table_type>(v).unwrap()["key3"] = value_type(123);
tab["key3"] = value_type(123);
CHECK_EQ(tab, toml::try_get<table_type>(v).unwrap());
table_type x = toml::try_get<table_type>(std::move(v)).unwrap();
CHECK_EQ(tab, x);
}
{
value_type v1(42);
CHECK_EQ(v1, toml::try_get<value_type>(v1).unwrap());
value_type v2(54);
toml::try_get<value_type>(v1).unwrap() = v2;
CHECK_EQ(v2, toml::try_get<value_type>(v1).unwrap());
value_type x = toml::try_get<value_type>(std::move(v1)).unwrap();
CHECK_EQ(v2, x);
}
}
TEST_CASE("testing toml::try_get<integer-like>")
{
using value_type = toml::value;
{
value_type v(42);
CHECK_EQ(int(42), toml::try_get<int >(v).unwrap());
CHECK_EQ(short(42), toml::try_get<short >(v).unwrap());
CHECK_EQ(char(42), toml::try_get<char >(v).unwrap());
CHECK_EQ(unsigned(42), toml::try_get<unsigned >(v).unwrap());
CHECK_EQ(long(42), toml::try_get<long >(v).unwrap());
CHECK_EQ(std::int64_t(42), toml::try_get<std::int64_t >(v).unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_get<std::uint64_t>(v).unwrap());
CHECK_EQ(std::int16_t(42), toml::try_get<std::int16_t >(v).unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_get<std::uint16_t>(v).unwrap());
CHECK_EQ(int(42), toml::try_get<int >(as_const(v)).unwrap());
CHECK_EQ(short(42), toml::try_get<short >(as_const(v)).unwrap());
CHECK_EQ(char(42), toml::try_get<char >(as_const(v)).unwrap());
CHECK_EQ(unsigned(42), toml::try_get<unsigned >(as_const(v)).unwrap());
CHECK_EQ(long(42), toml::try_get<long >(as_const(v)).unwrap());
CHECK_EQ(std::int64_t(42), toml::try_get<std::int64_t >(as_const(v)).unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_get<std::uint64_t>(as_const(v)).unwrap());
CHECK_EQ(std::int16_t(42), toml::try_get<std::int16_t >(as_const(v)).unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_get<std::uint16_t>(as_const(v)).unwrap());
value_type v1(v);
value_type v2(v);
value_type v3(v);
value_type v4(v);
value_type v5(v);
value_type v6(v);
value_type v7(v);
value_type v8(v);
value_type v9(v);
CHECK_EQ(int(42), toml::try_get<int >(v1).unwrap());
CHECK_EQ(short(42), toml::try_get<short >(v2).unwrap());
CHECK_EQ(char(42), toml::try_get<char >(v3).unwrap());
CHECK_EQ(unsigned(42), toml::try_get<unsigned >(v4).unwrap());
CHECK_EQ(long(42), toml::try_get<long >(v5).unwrap());
CHECK_EQ(std::int64_t(42), toml::try_get<std::int64_t >(v6).unwrap());
CHECK_EQ(std::uint64_t(42), toml::try_get<std::uint64_t>(v7).unwrap());
CHECK_EQ(std::int16_t(42), toml::try_get<std::int16_t >(v8).unwrap());
CHECK_EQ(std::uint16_t(42), toml::try_get<std::uint16_t>(v9).unwrap());
}
}
TEST_CASE("testing toml::try_get<floating-like>")
{
using value_type = toml::value;
{
const double ref(3.14);
value_type v(ref);
CHECK_EQ(static_cast<float >(ref), toml::try_get<float >(v).unwrap());
CHECK_EQ( ref , toml::try_get<double >(v).unwrap());
CHECK_EQ(static_cast<long double>(ref), toml::try_get<long double>(v).unwrap());
value_type v1(ref);
value_type v2(ref);
value_type v3(ref);
CHECK_EQ(static_cast<float >(ref), toml::try_get<float >(std::move(v1)).unwrap());
CHECK_EQ( ref , toml::try_get<double >(std::move(v2)).unwrap());
CHECK_EQ(static_cast<long double>(ref), toml::try_get<long double>(std::move(v3)).unwrap());
}
}
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
TEST_CASE("testing toml::try_get<string-like>")
{
using value_type = toml::value;
{
value_type v("foo");
CHECK_EQ("foo", toml::try_get<std::string_view>(v).unwrap());
}
}
#endif
TEST_CASE("testing toml::try_get<array-like>")
{
using value_type = toml::value;
{
const value_type v(toml::array{42, 54, 69, 72});
const std::vector<int> vec = toml::try_get<std::vector<int>>(v).unwrap();
const std::list<short> lst = toml::try_get<std::list<short>>(v).unwrap();
const std::deque<std::int64_t> deq = toml::try_get<std::deque<std::int64_t>>(v).unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
CHECK_EQ(static_cast<short>(42), *(iter++));
CHECK_EQ(static_cast<short>(54), *(iter++));
CHECK_EQ(static_cast<short>(69), *(iter++));
CHECK_EQ(static_cast<short>(72), *(iter++));
CHECK_EQ(static_cast<std::int64_t>(42), deq.at(0));
CHECK_EQ(static_cast<std::int64_t>(54), deq.at(1));
CHECK_EQ(static_cast<std::int64_t>(69), deq.at(2));
CHECK_EQ(static_cast<std::int64_t>(72), deq.at(3));
std::array<int, 4> ary = toml::try_get<std::array<int, 4>>(v).unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::try_get<std::tuple<int, short, unsigned, long>>(v).unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
const value_type p(toml::array{3.14, 2.71});
std::pair<double, double> pr = toml::try_get<std::pair<double, double> >(p).unwrap();
CHECK_EQ(3.14, pr.first);
CHECK_EQ(2.71, pr.second);
}
{
value_type v(toml::array{42, 54, 69, 72});
const std::vector<int> vec = toml::try_get<std::vector<int>>(std::move(v)).unwrap();
CHECK_EQ(42, vec.at(0));
CHECK_EQ(54, vec.at(1));
CHECK_EQ(69, vec.at(2));
CHECK_EQ(72, vec.at(3));
}
{
value_type v(toml::array{42, 54, 69, 72});
const std::deque<int> deq = toml::try_get<std::deque<int>>(std::move(v)).unwrap();
CHECK_EQ(42, deq.at(0));
CHECK_EQ(54, deq.at(1));
CHECK_EQ(69, deq.at(2));
CHECK_EQ(72, deq.at(3));
}
{
value_type v(toml::array{42, 54, 69, 72});
const std::list<int> lst = toml::try_get<std::list<int>>(std::move(v)).unwrap();
std::list<int>::const_iterator iter = lst.begin();
CHECK_EQ(42, *(iter++));
CHECK_EQ(54, *(iter++));
CHECK_EQ(69, *(iter++));
CHECK_EQ(72, *(iter++));
}
{
value_type v(toml::array{42, 54, 69, 72});
std::array<int, 4> ary = toml::try_get<std::array<int, 4>>(std::move(v)).unwrap();
CHECK_EQ(42, ary.at(0));
CHECK_EQ(54, ary.at(1));
CHECK_EQ(69, ary.at(2));
CHECK_EQ(72, ary.at(3));
}
{
value_type v(toml::array{42, 54, 69, 72});
std::tuple<int, short, unsigned, long> tpl =
toml::try_get<std::tuple<int, short, unsigned, long>>(std::move(v)).unwrap();
CHECK_EQ( 42 , std::get<0>(tpl));
CHECK_EQ(static_cast<short >(54), std::get<1>(tpl));
CHECK_EQ(static_cast<unsigned>(69), std::get<2>(tpl));
CHECK_EQ(static_cast<long >(72), std::get<3>(tpl));
}
}
TEST_CASE("testing toml::try_get<array-of-arrays>")
{
using value_type = toml::value;
{
const value_type v1(toml::array{42, 54, 69, 72});
const value_type v2(toml::array{"foo", "bar", "baz"});
const value_type v (toml::array{v1, v2});
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_get<std::pair<std::vector<int>, std::vector<std::string>>>(v).unwrap();
CHECK_EQ(p.first.size(), 4u);
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.size(), 3u);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::try_get<std::tuple<std::vector<int>, std::vector<std::string>>>(v).unwrap();
CHECK_EQ(std::get<0>(t).at(0), 42);
CHECK_EQ(std::get<0>(t).at(1), 54);
CHECK_EQ(std::get<0>(t).at(2), 69);
CHECK_EQ(std::get<0>(t).at(3), 72);
CHECK_EQ(std::get<1>(t).at(0), "foo");
CHECK_EQ(std::get<1>(t).at(1), "bar");
CHECK_EQ(std::get<1>(t).at(2), "baz");
}
{
const value_type v1(toml::array{42, 54, 69, 72});
const value_type v2(toml::array{"foo", "bar", "baz"});
value_type v (toml::array{v1, v2});
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::try_get<std::pair<std::vector<int>, std::vector<std::string>>>(std::move(v)).unwrap();
CHECK_EQ(p.first.size(), 4u);
CHECK_EQ(p.first.at(0), 42);
CHECK_EQ(p.first.at(1), 54);
CHECK_EQ(p.first.at(2), 69);
CHECK_EQ(p.first.at(3), 72);
CHECK_EQ(p.second.size(), 3u);
CHECK_EQ(p.second.at(0), "foo");
CHECK_EQ(p.second.at(1), "bar");
CHECK_EQ(p.second.at(2), "baz");
}
}
TEST_CASE("testing toml::try_get<table-like>")
{
using value_type = toml::value;
{
const value_type v1(toml::table{
{"key1", 1},
{"key2", 2},
{"key3", 3},
{"key4", 4}
});
const auto v = toml::try_get<std::map<std::string, int>>(v1).unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
{
value_type v1(toml::table{
{"key1", 1},
{"key2", 2},
{"key3", 3},
{"key4", 4}
});
const auto v = toml::try_get<std::map<std::string, int>>(std::move(v1)).unwrap();
CHECK_EQ(v.at("key1"), 1);
CHECK_EQ(v.at("key2"), 2);
CHECK_EQ(v.at("key3"), 3);
CHECK_EQ(v.at("key4"), 4);
}
}
TEST_CASE("testing toml::try_get<time_point>(local_date)")
{
using value_type = toml::value;
value_type v1(toml::local_date{2018, toml::month_t::Apr, 1});
const auto date = std::chrono::system_clock::to_time_t(
toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 0;
t.tm_min = 0;
t.tm_sec = 0;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
TEST_CASE("testing toml::try_get<duration>")
{
using value_type = toml::value;
value_type v1(toml::local_time{12, 30, 45});
const auto time = toml::try_get<std::chrono::seconds>(v1).unwrap();
const bool result = time == std::chrono::hours(12) +
std::chrono::minutes(30) +
std::chrono::seconds(45);
CHECK_UNARY(result);
}
TEST_CASE("testing toml::try_get<time_point>(local_datetime)")
{
using value_type = toml::value;
value_type v1(toml::local_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 45}));
const auto date = std::chrono::system_clock::to_time_t(
toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap());
std::tm t;
t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1;
t.tm_mday = 1;
t.tm_hour = 12;
t.tm_min = 30;
t.tm_sec = 45;
t.tm_isdst = -1;
const auto c = std::mktime(&t);
CHECK_EQ(c, date);
}
TEST_CASE("testing toml::try_get<time_point>(offset_datetime)")
{
using value_type = toml::value;
{
value_type v1(toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{9, 0}));
// 2018-04-01T12:30:00+09:00
// == 2018-04-01T03:30:00Z
const auto date = toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 3);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
{
value_type v1(toml::offset_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 0},
toml::time_offset{-8, 0}));
// 2018-04-01T12:30:00-08:00
//, 2018-04-01T20:30:00Z
const auto date = toml::try_get<std::chrono::system_clock::time_point>(v1).unwrap();
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tm = toml::detail::gmtime_s(std::addressof(timet));
CHECK_EQ(tm.tm_year + 1900, 2018);
CHECK_EQ(tm.tm_mon + 1, 4);
CHECK_EQ(tm.tm_mday, 1);
CHECK_EQ(tm.tm_hour, 20);
CHECK_EQ(tm.tm_min, 30);
CHECK_EQ(tm.tm_sec, 0);
}
}

View File

@@ -1062,21 +1062,89 @@ TEST_CASE("testing array-accessors")
{ {
toml::value x({true, 42, "hoge"}); toml::value x({true, 42, "hoge"});
// at
CHECK_UNARY(x.at(0).is_boolean()); CHECK_UNARY(x.at(0).is_boolean());
CHECK_UNARY(x.at(1).is_integer()); CHECK_UNARY(x.at(1).is_integer());
CHECK_UNARY(x.at(2).is_string()); CHECK_UNARY(x.at(2).is_string());
CHECK_EQ(x.at(0).as_boolean(), true);
CHECK_EQ(x.at(1).as_integer(), 42);
CHECK_EQ(x.at(2).as_string(), std::string("hoge"));
CHECK_THROWS_AS(x.at(3), std::out_of_range);
CHECK_UNARY(as_const(x).at(0).is_boolean()); CHECK_UNARY(as_const(x).at(0).is_boolean());
CHECK_UNARY(as_const(x).at(1).is_integer()); CHECK_UNARY(as_const(x).at(1).is_integer());
CHECK_UNARY(as_const(x).at(2).is_string()); CHECK_UNARY(as_const(x).at(2).is_string());
CHECK_EQ(x.at(0).as_boolean(), true);
CHECK_EQ(x.at(1).as_integer(), 42);
CHECK_EQ(x.at(2).as_string(), std::string("hoge"));
CHECK_EQ(as_const(x).at(0).as_boolean(), true); CHECK_EQ(as_const(x).at(0).as_boolean(), true);
CHECK_EQ(as_const(x).at(1).as_integer(), 42); CHECK_EQ(as_const(x).at(1).as_integer(), 42);
CHECK_EQ(as_const(x).at(2).as_string(), std::string("hoge")); CHECK_EQ(as_const(x).at(2).as_string(), std::string("hoge"));
CHECK_THROWS_AS(as_const(x).at(3), std::out_of_range);
// update through at
x.at(0) = false;
x.at(1) = 6 * 9;
x.at(2) = 3.14;
CHECK_UNARY(x.at(0).is_boolean());
CHECK_UNARY(x.at(1).is_integer());
CHECK_UNARY(x.at(2).is_floating());
CHECK_EQ(x.at(0).as_boolean(), false);
CHECK_EQ(x.at(1).as_integer(), 6*9);
CHECK_EQ(x.at(2).as_floating(), 3.14);
x.at(0) = true;
x.at(1) = 42;
x.at(2) = "hoge";
// try_at
CHECK_UNARY(x.try_at(0).is_ok());
CHECK_UNARY(x.try_at(1).is_ok());
CHECK_UNARY(x.try_at(2).is_ok());
CHECK_UNARY(x.try_at(0).as_ok().is_boolean());
CHECK_UNARY(x.try_at(1).as_ok().is_integer());
CHECK_UNARY(x.try_at(2).as_ok().is_string());
CHECK_UNARY_FALSE(x.try_at(0).is_err());
CHECK_UNARY_FALSE(x.try_at(1).is_err());
CHECK_UNARY_FALSE(x.try_at(2).is_err());
CHECK_EQ(x.try_at(0).as_ok().as_boolean(), true);
CHECK_EQ(x.try_at(1).as_ok().as_integer(), 42);
CHECK_EQ(x.try_at(2).as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(x.try_at(3).is_err());
CHECK_UNARY(as_const(x).try_at(0).is_ok());
CHECK_UNARY(as_const(x).try_at(1).is_ok());
CHECK_UNARY(as_const(x).try_at(2).is_ok());
CHECK_UNARY(as_const(x).try_at(0).as_ok().is_boolean());
CHECK_UNARY(as_const(x).try_at(1).as_ok().is_integer());
CHECK_UNARY(as_const(x).try_at(2).as_ok().is_string());
CHECK_UNARY_FALSE(as_const(x).try_at(0).is_err());
CHECK_UNARY_FALSE(as_const(x).try_at(1).is_err());
CHECK_UNARY_FALSE(as_const(x).try_at(2).is_err());
CHECK_EQ(as_const(x).try_at(0).as_ok().as_boolean(), true);
CHECK_EQ(as_const(x).try_at(1).as_ok().as_integer(), 42);
CHECK_EQ(as_const(x).try_at(2).as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(as_const(x).try_at(3).is_err());
// update through try_at
x.try_at(0).as_ok() = false;
x.try_at(1).as_ok() = 6 * 9;
x.try_at(2).as_ok() = 3.14;
CHECK_UNARY(x.at(0).is_boolean());
CHECK_UNARY(x.at(1).is_integer());
CHECK_UNARY(x.at(2).is_floating());
CHECK_EQ(x.at(0).as_boolean(), false);
CHECK_EQ(x.at(1).as_integer(), 6*9);
CHECK_EQ(x.at(2).as_floating(), 3.14);
x.try_at(0).as_ok() = true;
x.try_at(1).as_ok() = 42;
x.try_at(2).as_ok() = "hoge";
// operator[]
CHECK_UNARY(x[0].is_boolean()); CHECK_UNARY(x[0].is_boolean());
CHECK_UNARY(x[1].is_integer()); CHECK_UNARY(x[1].is_integer());
@@ -1086,6 +1154,8 @@ TEST_CASE("testing array-accessors")
CHECK_EQ(x[1].as_integer(), 42); CHECK_EQ(x[1].as_integer(), 42);
CHECK_EQ(x[2].as_string(), std::string("hoge")); CHECK_EQ(x[2].as_string(), std::string("hoge"));
// -----------------------------------------------------------------------
const toml::value v1(3.14); const toml::value v1(3.14);
toml::value v2(2.71); toml::value v2(2.71);
@@ -1143,13 +1213,83 @@ TEST_CASE("testing table-accessors")
CHECK_EQ(x.count("d"), 0); CHECK_EQ(x.count("d"), 0);
CHECK_EQ(x.count("e"), 0); CHECK_EQ(x.count("e"), 0);
// at
CHECK_UNARY(x.at("a").is_boolean()); CHECK_UNARY(x.at("a").is_boolean());
CHECK_UNARY(x.at("b").is_integer()); CHECK_UNARY(x.at("b").is_integer());
CHECK_UNARY(x.at("c").is_string()); CHECK_UNARY(x.at("c").is_string());
CHECK_EQ(x.at("a").as_boolean(), true); CHECK_EQ(x.at("a").as_boolean(), true);
CHECK_EQ(x.at("b").as_integer(), 42); CHECK_EQ(x.at("b").as_integer(), 42);
CHECK_EQ(x.at("c").as_string(), std::string("hoge")); CHECK_EQ(x.at("c").as_string(), std::string("hoge"));
CHECK_THROWS_AS(x.at("d"), std::out_of_range);
CHECK_UNARY(as_const(x).at("a").is_boolean());
CHECK_UNARY(as_const(x).at("b").is_integer());
CHECK_UNARY(as_const(x).at("c").is_string());
CHECK_EQ(as_const(x).at("a").as_boolean(), true);
CHECK_EQ(as_const(x).at("b").as_integer(), 42);
CHECK_EQ(as_const(x).at("c").as_string(), std::string("hoge"));
CHECK_THROWS_AS(as_const(x).at("d"), std::out_of_range);
// rewrite using at
x.at("a") = false;
x.at("b") = 6*9;
x.at("c") = 3.14;
CHECK_UNARY(x.at("a").is_boolean());
CHECK_UNARY(x.at("b").is_integer());
CHECK_UNARY(x.at("c").is_floating());
CHECK_EQ(x.at("a").as_boolean(), false);
CHECK_EQ(x.at("b").as_integer(), 6*9);
CHECK_EQ(x.at("c").as_floating(), 3.14);
x.at("a") = true;
x.at("b") = 42;
x.at("c") = "hoge";
// try_at
CHECK_UNARY(x.try_at("a").is_ok());
CHECK_UNARY(x.try_at("b").is_ok());
CHECK_UNARY(x.try_at("c").is_ok());
CHECK_UNARY(x.try_at("a").as_ok().is_boolean());
CHECK_UNARY(x.try_at("b").as_ok().is_integer());
CHECK_UNARY(x.try_at("c").as_ok().is_string());
CHECK_EQ(x.try_at("a").as_ok().as_boolean(), true);
CHECK_EQ(x.try_at("b").as_ok().as_integer(), 42);
CHECK_EQ(x.try_at("c").as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(x.try_at("d").is_err());
CHECK_UNARY(as_const(x).try_at("a").is_ok());
CHECK_UNARY(as_const(x).try_at("b").is_ok());
CHECK_UNARY(as_const(x).try_at("c").is_ok());
CHECK_UNARY(as_const(x).try_at("a").as_ok().is_boolean());
CHECK_UNARY(as_const(x).try_at("b").as_ok().is_integer());
CHECK_UNARY(as_const(x).try_at("c").as_ok().is_string());
CHECK_EQ(as_const(x).try_at("a").as_ok().as_boolean(), true);
CHECK_EQ(as_const(x).try_at("b").as_ok().as_integer(), 42);
CHECK_EQ(as_const(x).try_at("c").as_ok().as_string(), std::string("hoge"));
CHECK_UNARY(as_const(x).try_at("d").is_err());
// rewrite using try_at
x.try_at("a").as_ok() = false;
x.try_at("b").as_ok() = 6*9;
x.try_at("c").as_ok() = 3.14;
CHECK_UNARY(x.at("a").is_boolean());
CHECK_UNARY(x.at("b").is_integer());
CHECK_UNARY(x.at("c").is_floating());
CHECK_EQ(x.at("a").as_boolean(), false);
CHECK_EQ(x.at("b").as_integer(), 6*9);
CHECK_EQ(x.at("c").as_floating(), 3.14);
x.try_at("a").as_ok() = true;
x.try_at("b").as_ok() = 42;
x.try_at("c").as_ok() = "hoge";
// operator[]
CHECK_UNARY(x["a"].is_boolean()); CHECK_UNARY(x["a"].is_boolean());
CHECK_UNARY(x["b"].is_integer()); CHECK_UNARY(x["b"].is_integer());