Compare commits

..

13 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
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
28 changed files with 2656 additions and 251 deletions

View File

@@ -53,7 +53,7 @@ jobs:
sudo apt-get install clang-${{ matrix.compiler }}
- name: Configure
run: |
cmake -B build/ -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }}
cmake -B build/ -DCMAKE_VERBOSE_MAKEFILE=1 -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }}
- name: Build
run: |
cmake --build build/

View File

@@ -8,7 +8,7 @@
[日本語版](https://github.com/ToruNiina/toml11/blob/main/README_ja.md)
toml11 is a feature-rich TOML language library for C++11/14/17/20.
toml11 is a feature-rich TOML language library for C++.
- It complies with [the latest TOML language specification](https://toml.io/en/v1.0.0).
- It passes all the standard TOML language [test cases](https://github.com/toml-lang/toml-test).
@@ -124,14 +124,6 @@ $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON -DCMAKE_CXX_STANDARD=11/14/17/20
$ cmake --build ./build/
```
When linking the library, use `target_link_libraries` in CMake
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
or pass `-DTOML11_COMPILE_SOURCES` to the compiler.
### Building Example
To compile the examples in the `examples/` directory, set `-DTOML11_BUILD_EXAMPLES=ON`.

View File

@@ -5,7 +5,7 @@
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
toml11は、C++11,14,17,20のための豊富な機能を持つTOML言語ライブラリです。
toml11は、C++のための豊富な機能を持つTOML言語ライブラリです。
- [TOML言語の最新規格](https://toml.io/ja/v1.0.0)に準拠しています。
- TOML言語標準のテストケースすべてにパスしています。
@@ -122,14 +122,6 @@ $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON -DCMAKE_CXX_STANDARD=11/14/17/20
$ cmake --build ./build/
```
ライブラリをリンクする場合は、CMakeで
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
とするか、コンパイラに`-DTOML11_COMPILE_SOURCES`を渡してください。
### Building example
`-DTOML11_BUILD_EXAMPLES=ON`とすることで、`examples/`をコンパイルできます。

View File

@@ -42,18 +42,10 @@ int main()
}
```
#### Specifying a File with `std::filesystem::path`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can accept a `std::filesystem::path`.
This requires C++17 or later, as it relies on the `<filesystem>` support.
#### Specifying an Input Stream with `std::istream`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can also accept an `std::istream`.
Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
Without the filename information, error messages will display `"unknown file"`. To avoid this, you can pass the filename as a `std::string` in the second argument when using `std::istream`.
You can use streams other than `std::ifstream`, such as `std::istringstream`. Note that the entire content is readable at the time of the call.
@@ -72,12 +64,16 @@ int main()
}
```
#### Specifying a File with `std::filesystem::path`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can accept a `std::filesystem::path`.
This requires C++17 or later, as it relies on the `<filesystem>` support.
#### Specifying a File with `FILE*`
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can also accept a `FILE*`.
Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
As with `std::istream`, you need to provide the filename as a string in the second argument.
When passing a `FILE*`, if the file read fails, `errno` will be reported.

View File

@@ -92,6 +92,32 @@ std::cout << v.at(1);
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)`
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.
### `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()`
Returns the length.

View File

@@ -54,14 +54,6 @@ By defining `-DTOML11_PRECOMPILE=ON` when running cmake, you can precompile some
$ cmake -B ./build/ -DTOML11_PRECOMPILE=ON
```
When linking the library, use `target_link_libraries` in CMake
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
or pass `-DTOML11_COMPILE_SOURCES` to the compiler to suppress header-only features.
However, since toml11 supports multiple C++ versions and may switch types based on the value of `__cplusplus`,
there is a possibility of link failures if the version used during build differs from the version used during usage.
If you encounter issues, set the required version using `CMAKE_CXX_STANDARD` during compilation.

View File

@@ -17,6 +17,23 @@ In case of failure, `toml::syntax_error` is thrown.
The type information of `basic_value` is provided by a `template`, and the TOML language version is specified by `toml::spec`.
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Parses the content of the given `std::istream&`.
The filename information is taken as the third argument. If the filename is not provided, it defaults to `"unknown file"`.
### `parse(std::string filename, toml::spec)`
```cpp
@@ -73,25 +90,6 @@ If reading the file fails, `toml::file_io_error` is thrown.
If parsing fails, `toml::syntax_error` is thrown.
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Parses the content of the given `std::istream&`.
Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
The filename information is taken as the third argument. If the filename is not provided, it defaults to `"unknown file"`.
### `parse(FILE*, std::string filename, toml::spec)`
```cpp
@@ -107,8 +105,6 @@ parse(FILE* fp,
Parses the content of the file pointed to by `FILE*`.
Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
If reading the file fails, `file_io_error` containing `errno` is thrown.
If parsing fails, `syntax_error` is thrown.
@@ -170,6 +166,27 @@ For instance, errors occurring internally within `std::ifstream` or memory exhau
{{< /hint >}}
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Takes a `std::istream&` and parses its content.
The file name information is taken as the second argument. If a file name is not provided, it defaults to `"unknown file"`.
If parsing fails, a `result` holding the error type `std::vector<error_info>` is returned.
If successful, a `result` holding a `basic_value` is returned.
### `try_parse(std::string filename, toml::spec)`
```cpp
@@ -224,29 +241,6 @@ If parsing fails, a `result` holding the error type `std::vector<error_info>` is
If successful, a `result` holding a `basic_value` is returned.
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
Takes a `std::istream&` and parses its content.
Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
The file name information is taken as the second argument. If a file name is not provided, it defaults to `"unknown file"`.
If parsing fails, a `result` holding the error type `std::vector<error_info>` is returned.
If successful, a `result` holding a `basic_value` is returned.
### `try_parse(FILE*, std::string filename, toml::spec)`
```cpp
@@ -262,8 +256,6 @@ try_parse(FILE* fp,
Takes a `FILE*` and parses its content.
Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.
If parsing fails, a `result` holding the error type `std::vector<error_info>` is returned.
If successful, a `result` holding a `basic_value` is returned.

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)`
```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`.
-----
### `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

@@ -44,21 +44,11 @@ int main()
}
```
#### `std::filesystem::path`でファイルを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`std::filesystem::path`を渡すことも可能です。
当然ですが、`<filesystem>`がサポートされるC++17以降でなければ使用できません。
#### `std::istream`で入力ストリームを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`std::istream`を渡すことも可能です。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`std::ios::binary`を使ってバイナリモードで開いてください。
その際、ファイル名の情報がなくなるため、エラーメッセージ中では `"unknown file"` となります。
これを避けるため、 `std::istream` を取る場合は第二引数に `std::string` でファイル名を取ることもできます。
@@ -80,14 +70,18 @@ int main()
}
```
#### `std::filesystem::path`でファイルを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`std::filesystem::path`を渡すことも可能です。
当然ですが、`<filesystem>`がサポートされるC++17以降でなければ使用できません。
#### `FILE*`でファイルを指定する
[`toml::parse`]({{< ref "docs/reference/parser#parse" >}})
には、`FILE*`を渡すことも可能です。
標準ライブラリによる改行文字の自動変換によるファイルサイズと文字数との不整合を避けるため、
`fopen("example.toml", "rb")`のようにしてバイナリモードで開いてください。
この場合も、`std::istream`のときと同様に、第二引数に文字列でファイル名を与える必要があります。
`FILE*`を渡した場合、ファイルの読み込みに失敗した際には`errno`が報告されます。

View File

@@ -96,6 +96,32 @@ std::cout << v.at(1);
格納している型が `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)`
`as_table().at(key)`, `as_table()[key]` と同等です。
@@ -111,6 +137,32 @@ v["a"] = 42;
格納している型が `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()`
長さを返します。

View File

@@ -54,14 +54,6 @@ target_link_libraries(main PRIVATE toml11::toml11)
$ cmake -B ./build/ -DTOML11_PRECOMPILE=ON
```
ライブラリをリンクする場合は、CMakeで
```cmake
target_link_libraries(your_target PUBLIC toml11::toml11)
```
とするか、ヘッダ内の関数の`inline`化を避けるためにコンパイラに`-DTOML11_COMPILE_SOURCES`を渡してください。
ただし、toml11は複数のC++バージョンに対応するため、`__cplusplus`の値などによって型を切り替えることがあります。
そのため、ビルドした際のバージョンと使用時のバージョンが異なる場合、リンクに失敗する可能性があります。
問題が生じた場合は`CMAKE_CXX_STANDARD`によって必要なバージョンを設定してコンパイルしてください。

View File

@@ -17,6 +17,23 @@ type = "docs"
`basic_value`の持つ型情報は`template`で、TOML言語のバージョンは`toml::spec`で指定します。
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
ファイル名の情報は第三引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
### `parse(std::string filename, toml::spec)`
```cpp
@@ -73,25 +90,6 @@ parse(const std::filesystem::path& fpath,
パースに失敗した場合、`syntax_error`が送出されます。
### `parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
basic_value<TC>
parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`std::ios::binary`を使ってバイナリモードで開いてください。
ファイル名の情報は第三引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
### `parse(FILE*, std::string filename, toml::spec)`
@@ -108,9 +106,6 @@ parse(FILE* fp,
`FILE*`が指すファイルを読み込んでパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`fopen`には`"rb"`などを渡してバイナリモードで開いてください。
ファイルの読み込みに失敗した場合、`errno`が含まれた`file_io_error`が送出されます。
パースに失敗した場合、`syntax_error`が送出されます。
@@ -172,6 +167,28 @@ parse_str(std::string content,
{{< /hint >}}
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
ファイル名の情報は第二引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
パースに失敗した場合、エラー型である`std::vector<error_info>`を持つ`result`が返されます。
成功した場合、`basic_value`を持つ`result`が返されます。
### `try_parse(std::string filename, toml::spec)`
```cpp
@@ -227,30 +244,6 @@ try_parse(const std::filesystem::path& fpath,
成功した場合、`basic_value`を持つ`result`が返されます。
### `try_parse(std::istream&, std::string filename, toml::spec)`
```cpp
namespace toml
{
template<typename TC = type_config>
result<basic_value<TC>, std::vector<error_info>>
try_parse(std::istream& is,
std::string fname = "unknown file",
spec s = spec::default_version());
}
```
`std::istream&`を受け取ってその内容をパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`std::ios::binary`を使ってバイナリモードで開いてください。
ファイル名の情報は第二引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。
パースに失敗した場合、エラー型である`std::vector<error_info>`を持つ`result`が返されます。
成功した場合、`basic_value`を持つ`result`が返されます。
### `try_parse(FILE*, std::string filename, toml::spec)`
```cpp
@@ -266,9 +259,6 @@ try_parse(FILE* fp,
`FILE*`を受け取って、そのファイルの内容をパースします。
標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、
`fopen`には`"rb"`などを渡してバイナリモードで開いてください。
パースに失敗した場合、エラー型である`std::vector<error_info>`を持つ`result`が返されます。
成功した場合、`basic_value`を持つ`result`が返されます。

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)`
```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)`
```cpp

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include "get.hpp"
#include "utility.hpp"
#include "value.hpp"
#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)
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...)
// 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, basic_value<TC>> const&

View File

@@ -7,7 +7,18 @@ namespace toml
template<typename T>
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 ...
// }

View File

@@ -3524,7 +3524,7 @@ try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::d
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize), '\0');
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
is.read(reinterpret_cast<char*>(letters.data()), fsize);
return detail::parse_impl<TC>(std::move(letters), std::move(fname), std::move(s));
@@ -3714,15 +3714,7 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
return err(std::vector<error_info>{error_info(
std::string("File size changed: \"") + filename +
std::string("\" make sure that FILE* is in binary mode "
"to avoid LF <-> CRLF conversion"), {}
)});
}
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
return detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
}
@@ -3760,12 +3752,7 @@ parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
throw file_io_error(errno, "File size changed; make sure that "
"FILE* is in binary mode to avoid LF <-> CRLF conversion", filename);
}
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
auto res = detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
if(res.is_ok())

View File

@@ -244,13 +244,13 @@ struct result
{
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_));
(void)tmp;
}
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_));
(void)tmp;
}
@@ -259,13 +259,13 @@ struct result
{
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_));
(void)tmp;
}
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_));
(void)tmp;
}
@@ -276,13 +276,13 @@ struct result
this->cleanup();
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_));
(void)tmp;
}
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_));
(void)tmp;
}
@@ -294,13 +294,13 @@ struct result
this->cleanup();
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_));
(void)tmp;
}
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_));
(void)tmp;
}

View File

@@ -90,6 +90,13 @@ struct has_specialized_from_impl
template<typename T, std::size_t S = sizeof(::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
{
template<typename T>
@@ -130,6 +137,8 @@ template<typename T>
struct has_specialized_from: decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T>
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
#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);
}
// ---------------------------------------------------------------------------
// 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 toml
#endif // TOML11_UTILITY_HPP

View File

@@ -50,6 +50,9 @@ error_info make_type_error(const basic_value<TC>&, const std::string&, const val
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>
void change_region_of_value(basic_value<TC>&, const basic_value<TC>&);
@@ -1613,6 +1616,44 @@ class basic_value
assert(found->first == k);
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)
{
if(this->is_empty())
@@ -1688,6 +1729,41 @@ class basic_value
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
{
// no check...
@@ -2092,6 +2168,19 @@ error_info make_not_found_error(const basic_value<TC>& v, const std::string& fna
}
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) \
template<typename TC> \

View File

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

View File

@@ -3,7 +3,7 @@
#define TOML11_VERSION_MAJOR 4
#define TOML11_VERSION_MINOR 0
#define TOML11_VERSION_PATCH 2
#define TOML11_VERSION_PATCH 1
#ifndef __cplusplus
# error "__cplusplus is not defined"
@@ -3584,7 +3584,7 @@ struct has_specialized_into_impl
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::into<T>*);
static std::true_type check(::toml::from<T>*);
};
@@ -15063,7 +15063,7 @@ try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::d
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize), '\0');
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
is.read(reinterpret_cast<char*>(letters.data()), fsize);
return detail::parse_impl<TC>(std::move(letters), std::move(fname), std::move(s));
@@ -15253,15 +15253,7 @@ try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
return err(std::vector<error_info>{error_info(
std::string("File size changed: \"") + filename +
std::string("\" make sure that FILE* is in binary mode "
"to avoid LF <-> CRLF conversion"), {}
)});
}
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
return detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
}
@@ -15299,12 +15291,7 @@ parse(FILE* fp, std::string filename, spec s = spec::default_version())
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
if(actual != static_cast<std::size_t>(fsize))
{
throw file_io_error(errno, "File size changed; make sure that "
"FILE* is in binary mode to avoid LF <-> CRLF conversion", filename);
}
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
auto res = detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
if(res.is_ok())

View File

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

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"});
// at
CHECK_UNARY(x.at(0).is_boolean());
CHECK_UNARY(x.at(1).is_integer());
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(1).is_integer());
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(1).as_integer(), 42);
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[1].is_integer());
@@ -1086,6 +1154,8 @@ TEST_CASE("testing array-accessors")
CHECK_EQ(x[1].as_integer(), 42);
CHECK_EQ(x[2].as_string(), std::string("hoge"));
// -----------------------------------------------------------------------
const toml::value v1(3.14);
toml::value v2(2.71);
@@ -1143,13 +1213,83 @@ TEST_CASE("testing table-accessors")
CHECK_EQ(x.count("d"), 0);
CHECK_EQ(x.count("e"), 0);
// at
CHECK_UNARY(x.at("a").is_boolean());
CHECK_UNARY(x.at("b").is_integer());
CHECK_UNARY(x.at("c").is_string());
CHECK_EQ(x.at("a").as_boolean(), true);
CHECK_EQ(x.at("b").as_integer(), 42);
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["b"].is_integer());