Compare commits

..

9 Commits

Author SHA1 Message Date
ToruNiina
3bf4ac0965 doc: update Fuzzy search section 2019-09-01 14:50:01 +09:00
ToruNiina
d53026837a test: add test for find<T> and check throw 2019-09-01 14:46:46 +09:00
ToruNiina
6d31cccc5b feat: throw if multiple keys match to the key 2019-09-01 14:45:56 +09:00
ToruNiina
99e46813f4 doc: add Fuzzy search section to README 2019-08-31 20:11:31 +09:00
ToruNiina
c0df39ca49 test: add test for find_fuzzy 2019-08-31 19:30:19 +09:00
ToruNiina
3e6747cfeb feat: add find_fuzzy and suggeston
Add the following
- find_fuzzy<T>(val, "key")
- find<T>(val, "key", FuzzyMatcher) to suggest typo
- levenstein_matcher
2019-08-31 19:28:50 +09:00
ToruNiina
4cebd660fd refactor: use as_xxx to cast toml value
and store rvalue reference as a value instead of lvalue
2019-08-31 18:22:36 +09:00
ToruNiina
43907de365 refactor: check key types in find(v, k1, k2, ...)
ks should be convertible to toml::key
2019-08-31 17:28:07 +09:00
ToruNiina
9b43171b65 refactor: split get.hpp to get/find.hpp 2019-08-31 14:49:00 +09:00
39 changed files with 1877 additions and 3580 deletions

View File

@@ -12,7 +12,7 @@ jobs:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_toml_test.cpp -o check_toml_test
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test
go get github.com/BurntSushi/toml-test
$GOPATH/bin/toml-test ./check_toml_test
test_serialization:
@@ -24,7 +24,7 @@ jobs:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_serialization.cpp -o check_serialization
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check_serialization.cpp -o check_serialization
git clone https://github.com/BurntSushi/toml-test.git
cp check_serialization toml-test/tests/valid
cd toml-test/tests/valid
@@ -47,7 +47,7 @@ jobs:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check.cpp -o check
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check.cpp -o check
git clone https://github.com/BurntSushi/toml-test.git
cp check toml-test/tests/invalid
cp check toml-test/tests/valid

View File

@@ -9,7 +9,7 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-5
@@ -21,7 +21,7 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-6
@@ -33,7 +33,7 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-7
@@ -45,31 +45,7 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=11 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=14
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
@@ -81,19 +57,7 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
@@ -101,16 +65,16 @@ matrix:
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-3.9" CXX_STANDARD=11
env: COMPILER="clang++-3.7" CXX_STANDARD=11
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.7
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-3.9
packages:
- g++-8
- clang-3.9
- clang-3.7
- boost1.70
- os: linux
language: cpp
@@ -119,9 +83,9 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-4.0
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- clang-4.0
@@ -133,9 +97,9 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-5.0
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- clang-5.0
@@ -147,9 +111,9 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- clang-6.0
@@ -161,9 +125,9 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- clang-7
@@ -175,41 +139,13 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- clang-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- g++-8
- clang-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=14
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
@@ -217,51 +153,9 @@ matrix:
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_ASAN=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_UBSAN=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
@@ -270,15 +164,6 @@ matrix:
language: cpp
compiler: clang
env: CXX_STANDARD=11
cache:
directories:
- $HOME/Library/Caches/Homebrew
addons:
homebrew:
update: true
packages:
- cmake
- boost
script:
- |
@@ -287,33 +172,13 @@ script:
travis_retry wget "https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz"
tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1
export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH}
else
brew upgrade cmake boost
fi
- |
if [[ "${TOML_HEAD}" != "ON" ]]; then
export TOML_HEAD="OFF"
fi
- echo "TOML_HEAD = ${TOML_HEAD}"
- |
if [[ "${WITH_ASAN}" != "ON" ]]; then
export WITH_ASAN="OFF"
fi
- echo "WITH_ASAN = ${WITH_ASAN}"
- |
if [[ "${WITH_UBSAN}" != "ON" ]]; then
export WITH_UBSAN="OFF"
fi
- echo "WITH_UBSAN = ${WITH_UBSAN}"
- cmake --version
- mkdir build
- cd build
- git clone https://github.com/toml-lang/toml.git
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} ..
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD ..
- make
- ctest --output-on-failure
# https://stackoverflow.com/a/53331571
before_cache:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew cleanup
fi

View File

@@ -4,21 +4,21 @@ enable_testing()
project(toml11)
set(toml11_VERSION_MAYOR 3)
set(toml11_VERSION_MINOR 3)
set(toml11_VERSION_MINOR 0)
set(toml11_VERSION_PATCH 1)
set(toml11_VERSION
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
)
option(toml11_BUILD_TEST "Build toml tests" ON)
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)
option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF)
include(CheckCXXCompilerFlag)
if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
set(CMAKE_CXX_STANDARD 11 CACHE STRING "The C++ standard whose features are requested to build all targets.")
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Boolean describing whether the value of CXX_STANDARD is a requirement.")
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Boolean specifying whether compiler specific extensions are requested.")
set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
set(CXX_STANDARD_REQUIRED ON)
else()
# Manually check for C++11 compiler flag.
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)

497
README.md
View File

@@ -68,12 +68,8 @@ int main()
- [TOML literal](#toml-literal)
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
- [Formatting user-defined error messages](#formatting-user-defined-error-messages)
- [Obtaining location information](#obtaining-location-information)
- [Exceptions](#exceptions)
- [Colorize Error Messages](#colorize-error-messages)
- [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types)
- [Unreleased TOML features](#unreleased-toml-features)
- [Breaking Changes from v2](#breaking-changes-from-v2)
- [Running Tests](#running-tests)
- [Contributors](#contributors)
@@ -274,75 +270,6 @@ const auto color = toml::find<std::string>(data, "fruit", "physical", "color");
const auto shape = toml::find<std::string>(data, "fruit", "physical", "shape");
```
### Finding a value in an array
You can find n-th value in an array by `toml::find`.
```toml
values = ["foo", "bar", "baz"]
```
``` cpp
const auto data = toml::parse("sample.toml");
const auto values = toml::find(data, "values");
const auto bar = toml::find<std::string>(values, 1);
```
`toml::find` can also search array recursively.
```cpp
const auto data = toml::parse("fruit.toml");
const auto bar = toml::find<std::string>(data, "values", 1);
```
Before calling `toml::find`, you can check if a value corresponding to a key
exists. You can use both `bool toml::value::contains(const key&) const` and
`std::size_t toml::value::count(const key&) const`. Those behaves like the
`std::map::contains` and `std::map::count`.
```cpp
const auto data = toml::parse("fruit.toml");
if(data.contains("fruit") && data.at("fruit").count("physical") != 0)
{
// ...
}
```
### In case of error
If the value does not exist, `toml::find` throws `std::out_of_range` with the
location of the table.
```console
terminate called after throwing an instance of 'std::out_of_range'
what(): [error] key "answer" not found
--> example.toml
6 | [tab]
| ~~~~~ in this table
```
----
If the specified type differs from the actual value contained, it throws
`toml::type_error` that inherits `std::exception`.
Similar to the case of syntax error, toml11 also displays clean error messages.
The error message when you choose `int` to get `string` value would be like this.
```console
terminate called after throwing an instance of 'toml::type_error'
what(): [error] toml::value bad_cast to integer
--> example.toml
3 | title = "TOML Example"
| ~~~~~~~~~~~~~~ the actual type is string
```
**NOTE**: In order to show this kind of error message, all the toml values have
a pointer to represent its range in a file. The entire contents of a file is
shared by `toml::value`s and remains on the heap memory. It is recommended to
destruct all the `toml::value` classes after configuring your application
if you have a large TOML file compared to the memory resource.
### Dotted keys
TOML v0.5.0 has a new feature named "dotted keys".
@@ -383,6 +310,116 @@ The above code works with the following toml file.
# NOT {"physical": {"color": "orange"}}.
```
### In case of error
If the value does not exist, `toml::find` throws an error with the location of
the table.
```console
terminate called after throwing an instance of 'std::out_of_range'
what(): [error] key "answer" not found
--> example.toml
6 | [tab]
| ~~~~~ in this table
```
**Note**: It is recommended to find a table as `toml::value` because it has much information
compared to `toml::table`, which is an alias of
`std::unordered_map<std::string, toml::value>`. Since `toml::table` does not have
any information about toml file, such as where the table was defined in the file.
----
If the specified type differs from the actual value contained, it throws
`toml::type_error` that inherits `std::exception`.
Similar to the case of syntax error, toml11 also displays clean error messages.
The error message when you choose `int` to get `string` value would be like this.
```console
terminate called after throwing an instance of 'toml::type_error'
what(): [error] toml::value bad_cast to integer
--> example.toml
3 | title = "TOML Example"
| ~~~~~~~~~~~~~~ the actual type is string
```
**NOTE**: In order to show this kind of error message, all the toml values have
a pointer to represent its range in a file. The entire contents of a file is
shared by `toml::value`s and remains on the heap memory. It is recommended to
destruct all the `toml::value` classes after configuring your application
if you have a large TOML file compared to the memory resource.
### Fuzzy Search
To find a value, you can use `find_fuzzy` instead of `find`.
```toml
[foobar]
# typo!
anseer = 42
```
```cpp
const auto data = toml::parse("sample.toml");
const auto foobar = toml::find(data, "foobar");
const auto answer = toml::find_fuzzy<int>(data, "answer"); // it finds "anseer".
```
When the specified key is not found, `toml::find_fuzzy` calculates
[levenstein distance](https://en.wikipedia.org/wiki/Levenshtein_distance)
between the specified key and other keys.
If it finds a key that is 1 away from the specified key by the Levenstein
distance, it returns the corresponding value.
To allow a more distant string, you can explicitly pass `toml::levenstein_matcher`
to `find_fuzzy`.
```cpp
toml::levenstein_matcher lev(2); // allow distance <= 2
const auto answer = toml::find_fuzzy<int>(data, "answer", lev);
```
You can also use your own distance metric. Implement your `fuzzy_matcher` that
has `operator()` that takes two strings and returns true if two strings resemble
each other.
```cpp
struct fuzzy_matcher
{
bool operator()(const std::string& lhs, const std::string& rhs) const
{
// return true if lhs matches with rhs.
}
};
```
If there are multiple keys that meets the condition, it throws `runtime_error`.
However, in many cases, rather than just allowing typographical errors,
you will want to suggest it and encouledge users to correct it.
If you pass a `fuzzy_matcher` to `toml::find`, a suggestion will be displayed
in the error message.
```cpp
toml::levenstein_matcher lev(1); // finds keys within distance <= 1
const auto answer = toml::find<int>(data, "answer", lev); // it throws!
```
```console
terminate called after throwing an instance of 'std::out_of_range'
what(): [error] key "answer" not found.
--> hoge.toml
1 | [foobar]
| ~~~~~~~~ in this table
...
2 | anseer = 42
| ~~ did you mean this here?
```
Note: Currently, `find_fuzzy` and `find(value, key, matcher)` take only one key.
The codes like `find_fuzzy(value, key1, key2, key3)` do not work.
## Casting a toml value
@@ -487,60 +524,6 @@ class value {
} // toml
```
### `at()`
You can access to the element of a table and an array by `toml::basic_value::at`.
```cpp
const toml::value v{1,2,3,4,5};
std::cout << v.at(2).as_integer() << std::endl; // 3
const toml::value v{{"foo", 42}, {"bar", 3.14}};
std::cout << v.at("foo").as_integer() << std::endl; // 42
```
If an invalid key (integer for a table, string for an array), it throws
`toml::type_error` for the conversion. If the provided key is out-of-range,
it throws `std::out_of_range`.
Note that, although `std::string` has `at()` member function, `toml::value::at`
throws if the contained type is a string. Because `std::string` does not
contain `toml::value`.
### `operator[]`
You can also access to the element of a table and an array by
`toml::basic_value::operator[]`.
```cpp
const toml::value v{1,2,3,4,5};
std::cout << v[2].as_integer() << std::endl; // 3
const toml::value v{{"foo", 42}, {"bar", 3.14}};
std::cout << v["foo"].as_integer() << std::endl; // 42
```
When you access to a `toml::value` that is not initialized yet via
`operator[](const std::string&)`, the `toml::value` will be a table,
just like the `std::map`.
```cpp
toml::value v; // not initialized as a table.
v["foo"] = 42; // OK. `v` will be a table.
```
Contrary, if you access to a `toml::value` that contains an array via `operator[]`,
it does not check anything. It converts `toml::value` without type check and then
access to the n-th element without boundary check, just like the `std::vector::operator[]`.
```cpp
toml::value v; // not initialized as an array
v[2] = 42; // error! UB
```
Please make sure that the `toml::value` has an array inside when you access to
its element via `operator[]`.
## Checking value type
You can check the type of a value by `is_xxx` function.
@@ -763,7 +746,6 @@ date information, but it can be converted to `std::chrono::duration` that
represents a duration from the beginning of the day, `00:00:00.000`.
```toml
# sample.toml
date = 2018-12-23
time = 12:30:00
l_dt = 2018-12-23T12:30:00
@@ -780,12 +762,8 @@ const auto o_dt = toml::get<std::chrono::system_clock::time_point>(data.at("o_dt
const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min
```
`local_date` and `local_datetime` are assumed to be in the local timezone when
they are converted into `time_point`. On the other hand, `offset_datetime` only
uses the offset part of the data and it does not take local timezone into account.
To contain datetime data, toml11 defines its own datetime types.
For more detail, you can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
toml11 defines its own datetime classes.
You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
## Getting with a fallback
@@ -898,25 +876,14 @@ toml::value v(toml::local_time(std::chrono::hours(10)));
```
You can construct an array object not only from `initializer_list`, but also
from STL containers. In that case, the element type must be convertible to
`toml::value`.
from STL containers.
```cpp
std::vector<int> vec{1,2,3,4,5};
toml::value v(vec);
toml::value v = vec;
```
When you construct an array value, all the elements of `initializer_list`
must be convertible into `toml::value`.
If a `toml::value` has an array, you can `push_back` an element in it.
```cpp
toml::value v{1,2,3,4,5};
v.push_back(6);
```
`emplace_back` also works.
All the elements of `initializer_list` should be convertible into `toml::value`.
## Preserving comments
@@ -1133,7 +1100,7 @@ const auto data = toml::parse("example.toml");
const foo f = toml::find<ext::foo>(data, "foo");
```
There are 3 ways to use `toml::get` with the types that you defined.
There are 2 ways to use `toml::get` with the types that you defined.
The first one is to implement `from_toml(const toml::value&)` member function.
@@ -1160,31 +1127,7 @@ struct foo
In this way, because `toml::get` first constructs `foo` without arguments,
the type should be default-constructible.
The second is to implement `constructor(const toml::value&)`.
```cpp
namespace ext
{
struct foo
{
explicit foo(const toml::value& v)
: a(toml::find<int>(v, "a")), b(toml::find<double>(v, "b")),
c(toml::find<std::string>(v, "c"))
{}
int a;
double b;
std::string c;
};
} // ext
```
Note that implicit default constructor declaration will be suppressed
when a constructor is defined. If you want to use the struct (here, `foo`)
in a container (e.g. `std::vector<foo>`), you may need to define default
constructor explicitly.
The third is to implement specialization of `toml::from` for your type.
The second is to implement specialization of `toml::from` for your type.
```cpp
namespace ext
@@ -1202,7 +1145,7 @@ namespace toml
template<>
struct from<ext::foo>
{
static ext::foo from_toml(const value& v)
ext::foo from_toml(const value& v)
{
ext::foo f;
f.a = find<int >(v, "a");
@@ -1242,7 +1185,7 @@ template<>
struct from<ext::foo>
{
template<typename C, template<typename ...> class M, template<typename ...> class A>
static ext::foo from_toml(const basic_value<C, M, A>& v)
ext::foo from_toml(const basic_value<C, M, A>& v)
{
ext::foo f;
f.a = find<int >(v, "a");
@@ -1298,7 +1241,7 @@ namespace toml
template<>
struct into<ext::foo>
{
static toml::table into_toml(const ext::foo& f)
toml::table into_toml(const ext::foo& f)
{
return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}};
}
@@ -1365,28 +1308,7 @@ you will get an error message like this.
| ~~ maximum number here
```
You can print hints at the end of the message.
```cpp
std::vector<std::string> hints;
hints.push_back("positive number means n >= 0.");
hints.push_back("negative number is not positive.");
std::cerr << toml::format_error("[error] value should be positive",
data.at("num"), "positive number required", hints)
<< std::endl;
```
```console
[error] value should be positive
--> example.toml
2 | num = 42
| ~~ positive number required
|
Hint: positive number means n >= 0.
Hint: negative number is not positive.
```
## Obtaining location information
### Obtaining location information
You can also format error messages in your own way by using `source_location`.
@@ -1411,92 +1333,6 @@ const toml::value v = /*...*/;
const toml::source_location loc = v.location();
```
## Exceptions
All the exceptions thrown by toml11 inherits `toml::exception` that inherits
`std::exception`.
```cpp
namespace toml {
struct exception : public std::exception {/**/};
struct syntax_error : public toml::exception {/**/};
struct type_error : public toml::exception {/**/};
struct internal_error : public toml::exception {/**/};
} // toml
```
`toml::exception` has `toml::exception::location()` member function that returns
`toml::source_location`, in addition to `what()`.
```cpp
namespace toml {
struct exception : public std::exception
{
// ...
source_location const& location() const noexcept;
};
} // toml
```
It represents where the error occurs.
## Colorize Error Messages
By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from
`toml::parse` and `toml::find|get` will be colorized. By default, this feature
is turned off.
With the following toml file taken from `toml-lang/toml/tests/hard_example.toml`,
```toml
[error]
array = [
"This might most likely happen in multiline arrays",
Like here,
"or here,
and here"
] End of array comment, forgot the #
```
the error message would be like this.
![error-message-1](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-1.png)
With the following,
```toml
[error]
# array = [
# "This might most likely happen in multiline arrays",
# Like here,
# "or here,
# and here"
# ] End of array comment, forgot the #
number = 3.14 pi <--again forgot the #
```
the error message would be like this.
![error-message-2](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-2.png)
The message would be messy when it is written to a file, not a terminal because
it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code).
Without `TOML11_COLORIZE_ERROR_MESSAGE`, you can still colorize user-defined
error message by passing `true` to the `toml::format_error` function.
If you define `TOML11_COLORIZE_ERROR_MESSAGE`, the value is `true` by default.
If not, the defalut value would be `false`.
```cpp
std::cerr << toml::format_error("[error] value should be positive",
data.at("num"), "positive number required",
hints, /*colorize = */ true) << std::endl;
```
Note: It colorize `[error]` in red. That means that it detects `[error]` prefix
at the front of the error message. If there is no `[error]` prefix,
`format_error` adds it to the error message.
## Serializing TOML data
toml11 enables you to serialize data into toml format.
@@ -1629,83 +1465,7 @@ This feature is introduced to make it easy to write a custom serializer.
Because `std::chrono::system_clock::time_point` is a __time point__,
not capable of representing a Local Time independent from a specific day.
## Unreleased TOML features
There are some unreleased features in toml-lang/toml:master.
Currently, the following features are available after defining
`TOML11_USE_UNRELEASED_TOML_FEATURES` macro flag.
To use those features, `#define` `TOML11_USE_UNRELEASED_TOML_FEATURES` before
including `toml.hpp` or pass `-DTOML11_USE_UNRELEASED_TOML_FEATURES` to your
compiler.
- Leading zeroes in exponent parts of floats are permitted.
- e.g. `1.0e+01`, `5e+05`
- [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656)
- Allow raw tab characters in basic strings and multi-line basic strings.
- [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627)
- Allow heterogeneous arrays
- [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676)
### Note about heterogeneous arrays
Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value`
does not.
```cpp
// this won't be compiled
toml::value v{
"foo", 3.14, 42, {1,2,3,4,5}, {{"key", "value"}}
}
```
There is a workaround for this issue. By explicitly converting values into
`toml::value`, you can initialize `toml::value` with a heterogeneous array.
```cpp
// OK!
toml::value v{
toml::value("foo"), toml::value(3.14), toml::value(42),
toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
}
```
The reason why the first example is not allowed is the following.
Let's assume that you are initializing a `toml::value` with a table.
```cpp
// # expecting TOML table.
toml::value v{ // [v]
{"answer", 42}, // answer = 42
{"pi", 3.14}, // pi = 3.14
{"foo", "bar"} // foo = "bar"
};
```
This is indistinguishable from a (heterogeneous) TOML array definition.
```toml
v = [
["answer", 42],
["pi", 3.14],
["foo", "bar"],
]
```
This means that the above C++ code makes constructor's overload resolution
ambiguous. So a constructor that allows both "table as an initializer-list" and
"heterogeneous array as an initializer-list" cannot be implemented.
Thus, although it is painful, you need to explicitly cast values into
`toml::value` when you initialize heterogeneous array in C++ code.
```cpp
// You need to do this when you want to initialize hetero array.
toml::value v{
toml::value("foo"), toml::value(3.14), toml::value(42),
toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
}
```
It is recommended to get `datetime`s as `std::chrono` classes through `toml::get`.
## Breaking Changes from v2
@@ -1776,22 +1536,11 @@ I appreciate the help of the contributors who introduced the great feature to th
- Fixed warnings while type conversion
- @KerstinKeller
- Added installation script to CMake
- J.C. Moyer (@jcmoyer)
- Fixed an example code in the documentation
- Jt Freeman (@blockparty-sh)
- Fixed feature test macro around `localtime_s`
- Suppress warnings in Debug mode
- OGAWA Kenichi (@kenichiice)
- Suppress warnings on intel compiler
- Jordan Williams (@jwillikers)
- Fixed clang range-loop-analysis warnings
- Fixed feature test macro to suppress -Wundef
- Use cache variables in CMakeLists.txt
## Licensing terms
This product is licensed under the terms of the [MIT License](LICENSE).
- Copyright (c) 2017-2020 Toru Niina
- Copyright (c) 2017-2019 Toru Niina
All rights reserved.

View File

@@ -27,6 +27,7 @@ set(TEST_NAMES
test_get_or
test_find
test_find_or
test_find_fuzzy
test_expect
test_parse_file
test_serialize_file
@@ -39,17 +40,6 @@ set(TEST_NAMES
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
CHECK_CXX_COMPILER_FLAG("-Wextra" COMPILER_SUPPORTS_WEXTRA)
CHECK_CXX_COMPILER_FLAG("-Wpedantic" COMPILER_SUPPORTS_WPEDANTIC)
CHECK_CXX_COMPILER_FLAG("-Werror" COMPILER_SUPPORTS_WERROR)
CHECK_CXX_COMPILER_FLAG("-Wsign-conversion" COMPILER_SUPPORTS_WSIGN_CONVERSION)
CHECK_CXX_COMPILER_FLAG("-Wconversion" COMPILER_SUPPORTS_WCONVERSION)
CHECK_CXX_COMPILER_FLAG("-Wduplicated-cond" COMPILER_SUPPORTS_WDUPLICATED_COND)
CHECK_CXX_COMPILER_FLAG("-Wduplicated-branches" COMPILER_SUPPORTS_WDUPLICATED_BRANCHES)
CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP)
CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST)
CHECK_CXX_COMPILER_FLAG("-Wdouble-promotion" COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
CHECK_CXX_COMPILER_FLAG("-Wrange-loop-analysis" COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
CHECK_CXX_COMPILER_FLAG("-Wundef" COMPILER_SUPPORTS_WUNDEF)
if(COMPILER_SUPPORTS_WALL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -60,44 +50,6 @@ endif()
if(COMPILER_SUPPORTS_WPEDANTIC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic")
endif()
if(COMPILER_SUPPORTS_WERROR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
endif()
if(COMPILER_SUPPORTS_WSIGN_CONVERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-conversion")
endif()
if(COMPILER_SUPPORTS_WCONVERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wconversion")
endif()
if(COMPILER_SUPPORTS_WDUPLICATED_COND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-cond")
endif()
if(COMPILER_SUPPORTS_WDUPLICATED_BRANCHES)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-branches")
endif()
if(COMPILER_SUPPORTS_WLOGICAL_OP)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op")
endif()
if(COMPILER_SUPPORTS_WUSELESS_CAST)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuseless-cast")
endif()
if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
endif()
if(COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wrange-loop-analysis")
endif()
if(COMPILER_SUPPORTS_WUNDEF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef")
endif()
option(TOML11_USE_UNRELEASED_TOML_FEATURES
"use features in toml-lang/toml master while testing" OFF)
if(TOML11_USE_UNRELEASED_TOML_FEATURES)
message(STATUS "adding TOML11_USE_UNRELEASED_TOML_FEATURES flag")
add_definitions("-DTOML11_USE_UNRELEASED_TOML_FEATURES")
endif()
# Disable some MSVC warnings
if(MSVC)
@@ -145,19 +97,6 @@ foreach(TEST_NAME ${TEST_NAMES})
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11)
target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(toml11_TEST_WITH_ASAN)
set_target_properties(${TEST_NAME} PROPERTIES
COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer"
LINK_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
elseif(toml11_TEST_WITH_UBSAN)
set_target_properties(${TEST_NAME} PROPERTIES
COMPILE_FLAGS "-fsanitize=undefined"
LINK_FLAGS "-fsanitize=undefined")
endif()
endif()
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
# Set the PATH to be able to find Boost DLL

View File

@@ -84,7 +84,9 @@ struct json_serializer
{
if(!is_first) {std::cout << ", ";}
is_first = false;
std::cout << this->format_key(elem.first) << ':';
std::cout << toml::format(toml::value(elem.first),
std::numeric_limits<std::size_t>::max());
std::cout << ':';
toml::visit(*this, elem.second);
}
std::cout << '}';
@@ -110,12 +112,6 @@ struct json_serializer
}
return retval;
}
std::string format_key(const std::string& s) const
{
const auto quote("\"");
return quote + escape_string(s) + quote;
}
};
int main()

View File

@@ -80,14 +80,10 @@ BOOST_AUTO_TEST_CASE(test_detect_conflicting_value)
BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array)
{
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("heterogeneous array will be allowed in the next release");
#else
std::istringstream stream(std::string(
"a = [1, 1.0]\n"
));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
#endif
}
BOOST_AUTO_TEST_CASE(test_detect_appending_array_of_table)

View File

@@ -33,29 +33,6 @@ struct bar
return toml::table{{"a", this->a}, {"b", this->b}};
}
};
struct baz
{
int a;
std::string b;
};
struct qux
{
int a;
std::string b;
};
struct foobar
{
foobar() = default; // later we use std::vector<foobar>, default ctor is required.
// via constructor
explicit foobar(const toml::value& v)
: a(toml::find<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib
namespace toml
@@ -77,24 +54,6 @@ struct into<extlib::foo>
return toml::table{{"a", f.a}, {"b", f.b}};
}
};
template<>
struct from<extlib::baz>
{
static extlib::baz from_toml(const toml::value& v)
{
return extlib::baz{toml::find<int>(v, "a"), toml::find<std::string>(v, "b")};
}
};
template<>
struct into<extlib::qux>
{
static toml::table into_toml(const extlib::qux& f)
{
return toml::table{{"a", f.a}, {"b", f.b}};
}
};
} // toml
// ---------------------------------------------------------------------------
@@ -124,29 +83,6 @@ struct bar
return toml::table{{"a", this->a}, {"b", this->b}};
}
};
struct baz
{
int a;
std::string b;
};
struct qux
{
int a;
std::string b;
};
struct foobar
{
foobar() = default; // later we use std::vector<foobar>, default ctor is required.
template<typename C, template<typename ...> class M, template<typename ...> class A>
explicit foobar(const toml::basic_value<C, M, A>& v)
: a(toml::find<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib2
namespace toml
@@ -169,28 +105,6 @@ struct into<extlib2::foo>
return toml::table{{"a", f.a}, {"b", f.b}};
}
};
template<>
struct from<extlib2::baz>
{
template<typename C, template<typename ...> class M, template<typename ...> class A>
static extlib2::baz from_toml(const toml::basic_value<C, M, A>& v)
{
return extlib2::baz{toml::find<int>(v, "a"), toml::find<std::string>(v, "b")};
}
};
template<>
struct into<extlib2::qux>
{
static toml::basic_value<toml::preserve_comments, std::map>
into_toml(const extlib2::qux& f)
{
return toml::basic_value<toml::preserve_comments, std::map>{
{"a", f.a}, {"b", f.b}
};
}
};
} // toml
// ---------------------------------------------------------------------------
@@ -274,62 +188,6 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_specialization)
}
}
BOOST_AUTO_TEST_CASE(test_conversion_one_way)
{
{
const toml::value v{{"a", 42}, {"b", "baz"}};
const auto baz = toml::get<extlib::baz>(v);
BOOST_TEST(baz.a == 42);
BOOST_TEST(baz.b == "baz");
}
{
const extlib::qux q{42, "qux"};
const toml::value v(q);
BOOST_TEST(toml::find<int>(v, "a") == 42);
BOOST_TEST(toml::find<std::string>(v, "b") == "qux");
}
{
const toml::basic_value<toml::discard_comments, std::map> v{
{"a", 42}, {"b", "baz"}
};
const auto baz = toml::get<extlib2::baz>(v);
BOOST_TEST(baz.a == 42);
BOOST_TEST(baz.b == "baz");
}
{
const extlib::qux q{42, "qux"};
const toml::basic_value<toml::preserve_comments, std::map> v(q);
BOOST_TEST(toml::find<int>(v, "a") == 42);
BOOST_TEST(toml::find<std::string>(v, "b") == "qux");
}
}
BOOST_AUTO_TEST_CASE(test_conversion_via_constructor)
{
{
const toml::value v{{"a", 42}, {"b", "foobar"}};
const auto foobar = toml::get<extlib::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
{
const toml::basic_value<toml::discard_comments, std::map> v{
{"a", 42}, {"b", "foobar"}
};
const auto foobar = toml::get<extlib2::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
}
BOOST_AUTO_TEST_CASE(test_recursive_conversion)
{
{
@@ -430,63 +288,5 @@ BOOST_AUTO_TEST_CASE(test_recursive_conversion)
BOOST_TEST(bars.at(2).b == "quux");
BOOST_TEST(bars.at(3).b == "foobar");
}
// via constructor
{
const toml::value v{
toml::table{{"a", 42}, {"b", "baz"}},
toml::table{{"a", 43}, {"b", "qux"}},
toml::table{{"a", 44}, {"b", "quux"}},
toml::table{{"a", 45}, {"b", "foobar"}}
};
{
const auto foobars = toml::get<std::vector<extlib::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
{
const auto foobars = toml::get<std::vector<extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
}
{
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v{
toml::table{{"a", 42}, {"b", "baz"}},
toml::table{{"a", 43}, {"b", "qux"}},
toml::table{{"a", 44}, {"b", "quux"}},
toml::table{{"a", 45}, {"b", "foobar"}}
};
const auto foobars = toml::get<std::vector<extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
}

View File

@@ -27,32 +27,6 @@ using test_value_types = std::tuple<
BOOST_AUTO_TEST_CASE(test_find_throws)
{
// -----------------------------------------------------------------------
// const-reference version
{
// value is not a table
const toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, "key"), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
const toml::value v{{"key", 42}};
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, "key"), toml::type_error);
}
{
// the value corresponding to the key is not found
const toml::value v{{"key", 42}};
BOOST_CHECK_THROW(toml::find<toml::integer>(v, "different_key"),
std::out_of_range);
}
{
// the positive control.
const toml::value v{{"key", 42}};
BOOST_TEST(42 == toml::find<int>(v, "key"));
}
// -----------------------------------------------------------------------
// reference version
{
// value is not a table
toml::value v(true);
@@ -74,103 +48,6 @@ BOOST_AUTO_TEST_CASE(test_find_throws)
toml::value v{{"key", 42}};
BOOST_TEST(42 == toml::find<int>(v, "key"));
}
// -----------------------------------------------------------------------
// move version
{
// value is not a table
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), "key"), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
toml::value v{{"key", 42}};
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), "key"), toml::type_error);
}
{
// the value corresponding to the key is not found
toml::value v{{"key", 42}};
BOOST_CHECK_THROW(toml::find<toml::integer>(std::move(v), "different_key"),
std::out_of_range);
}
{
// the positive control.
toml::value v{{"key", 42}};
BOOST_TEST(42 == toml::find<int>(std::move(v), "key"));
}
}
BOOST_AUTO_TEST_CASE(test_find_array_throws)
{
// -----------------------------------------------------------------------
// const-reference version
{
// value is not an array
const toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
const toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not found
const toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
const toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// non-const reference version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not found
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// move version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), 0), toml::type_error);
}
{
// the value corresponding to the key is not found
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(std::move(v), 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(std::move(v), 2));
}
}
BOOST_AUTO_TEST_CASE(test_find_recursive)
@@ -197,77 +74,6 @@ BOOST_AUTO_TEST_CASE(test_find_recursive)
auto& num2 = toml::find<toml::integer>(v, a, b, c, d);
num2 = 42;
BOOST_TEST(42 == toml::find<int>(v, a, b, c, d));
auto num3 = toml::find<toml::integer>(v, a, "b", c, "d");
BOOST_TEST(42 == num3);
auto num4 = toml::find<toml::integer>(std::move(v), a, b, c, d);
BOOST_TEST(42 == num4);
}
// recursively search arrays
{
toml::value v{
toml::array{"array", "of", "string"},
toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}}
};
BOOST_TEST("array" == toml::find<std::string>(v, 0, 0));
BOOST_TEST("of" == toml::find<std::string>(v, 0, 1));
BOOST_TEST("string" == toml::find<std::string>(v, 0, 2));
BOOST_TEST(1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST(2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(3 == toml::find<int>(v, 1, 0, 2));
BOOST_TEST(3.14 == toml::find<double>(v, 1, 1, 0));
BOOST_TEST(2.71 == toml::find<double>(v, 1, 1, 1));
// reference that can be used to modify the content
auto& num = toml::find<toml::integer>(v, 1, 0, 2);
num = 42;
BOOST_TEST( 1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST( 2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(42 == toml::find<int>(v, 1, 0, 2));
// move value
auto num2 = toml::find<toml::integer>(std::move(v), 1, 0, 2);
BOOST_TEST(42 == num2);
}
// 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}}
}}
}};
BOOST_TEST(1 == toml::find<int>(v, "array", 0, 0));
BOOST_TEST(2 == toml::find<int>(v, "array", 0, 1));
BOOST_TEST(3 == toml::find<int>(v, "array", 0, 2));
BOOST_TEST("bar" == toml::find<std::string>(v, "array", 1, 0, "foo"));
BOOST_TEST("qux" == toml::find<std::string>(v, "array", 1, 0, "baz"));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, "pi"));
BOOST_TEST(2.71 == toml::find<double>(v, "array", 1, 1, "e"));
const std::string ar("array");
const auto ar_c = "array";
const std::string pi("pi");
const auto pi_c = "pi";
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi_c));
}
}
@@ -279,9 +85,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::boolean>(v, "key") = false;
BOOST_TEST(false == toml::find<toml::boolean>(v, "key"));
const auto moved = toml::find<toml::boolean>(std::move(v), "key");
BOOST_TEST(false == moved);
}
{
value_type v{{"key", 42}};
@@ -289,9 +92,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::integer>(v, "key") = 54;
BOOST_TEST(toml::integer(54) == toml::find<toml::integer>(v, "key"));
const auto moved = toml::find<toml::integer>(std::move(v), "key");
BOOST_TEST(toml::integer(54) == moved);
}
{
value_type v{{"key", 3.14}};
@@ -299,9 +99,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::floating>(v, "key") = 2.71;
BOOST_TEST(toml::floating(2.71) == toml::find<toml::floating>(v, "key"));
const auto moved = toml::find<toml::floating>(std::move(v), "key");
BOOST_TEST(toml::floating(2.71) == moved);
}
{
value_type v{{"key", "foo"}};
@@ -311,9 +108,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::string>(v, "key").str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::basic) ==
toml::find<toml::string>(v, "key"));
const auto moved = toml::find<toml::string>(std::move(v), "key");
BOOST_TEST(toml::string("foobar", toml::string_t::basic) == moved);
}
{
value_type v{{"key", value_type("foo", toml::string_t::literal)}};
@@ -323,9 +117,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::string>(v, "key").str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::literal) ==
toml::find<toml::string>(v, "key"));
const auto moved = toml::find<toml::string>(std::move(v), "key");
BOOST_TEST(toml::string("foobar", toml::string_t::literal) == moved);
}
{
toml::local_date d(2018, toml::month_t::Apr, 22);
@@ -335,9 +126,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::local_date>(v, "key").year = 2017;
d.year = 2017;
BOOST_CHECK(d == toml::find<toml::local_date>(v, "key"));
const auto moved = toml::find<toml::local_date>(std::move(v), "key");
BOOST_CHECK(d == moved);
}
{
toml::local_time t(12, 30, 45);
@@ -347,9 +135,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::local_time>(v, "key").hour = 9;
t.hour = 9;
BOOST_CHECK(t == toml::find<toml::local_time>(v, "key"));
const auto moved = toml::find<toml::local_time>(std::move(v), "key");
BOOST_CHECK(t == moved);
}
{
toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22),
@@ -360,9 +145,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::local_datetime>(v, "key").date.year = 2017;
dt.date.year = 2017;
BOOST_CHECK(dt == toml::find<toml::local_datetime>(v, "key"));
const auto moved = toml::find<toml::local_datetime>(std::move(v), "key");
BOOST_CHECK(dt == moved);
}
{
toml::offset_datetime dt(toml::local_datetime(
@@ -374,9 +156,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::offset_datetime>(v, "key").date.year = 2017;
dt.date.year = 2017;
BOOST_CHECK(dt == toml::find<toml::offset_datetime>(v, "key"));
const auto moved = toml::find<toml::offset_datetime>(std::move(v), "key");
BOOST_CHECK(dt == moved);
}
{
typename value_type::array_type vec;
@@ -392,10 +171,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
const bool result2 = (vec == toml::find<typename value_type::array_type>(v, "key"));
BOOST_CHECK(result2);
const auto moved = toml::find<typename value_type::array_type>(std::move(v), "key");
const bool result3 = (vec == moved);
BOOST_CHECK(result3);
}
{
typename value_type::table_type tab;
@@ -409,10 +184,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
tab["key3"] = value_type(123);
const bool result2 = (tab == toml::find<typename value_type::table_type>(v, "key"));
BOOST_CHECK(result2);
const auto moved = toml::find<typename value_type::table_type>(std::move(v), "key");
const bool result3 = (tab == moved);
BOOST_CHECK(result3);
}
{
value_type v1(42);
@@ -422,9 +193,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
value_type v2(54);
toml::find(v, "key") = v2;
BOOST_CHECK(v2 == toml::find(v, "key"));
const auto moved = toml::find(std::move(v), "key");
BOOST_CHECK(v2 == moved);
}
}
@@ -432,16 +200,15 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_integer_type, value_type, test_value_typ
{
{
value_type v{{"key", 42}};
BOOST_TEST(int(42) == toml::find<int >(v, "key"));
BOOST_TEST(short(42) == toml::find<short >(v, "key"));
BOOST_TEST(char(42) == toml::find<char >(v, "key"));
BOOST_TEST(unsigned(42) == toml::find<unsigned >(v, "key"));
BOOST_TEST(long(42) == toml::find<long >(v, "key"));
BOOST_TEST(std::int64_t(42) == toml::find<std::int64_t >(v, "key"));
BOOST_TEST(int(42) == toml::find<int >(v, "key"));
BOOST_TEST(short(42) == toml::find<short >(v, "key"));
BOOST_TEST(char(42) == toml::find<char >(v, "key"));
BOOST_TEST(unsigned(42) == toml::find<unsigned >(v, "key"));
BOOST_TEST(long(42) == toml::find<long >(v, "key"));
BOOST_TEST(std::int64_t(42) == toml::find<std::int64_t >(v, "key"));
BOOST_TEST(std::uint64_t(42) == toml::find<std::uint64_t>(v, "key"));
BOOST_TEST(std::int16_t(42) == toml::find<std::int16_t >(v, "key"));
BOOST_TEST(std::int16_t(42) == toml::find<std::int16_t >(v, "key"));
BOOST_TEST(std::uint16_t(42) == toml::find<std::uint16_t>(v, "key"));
BOOST_TEST(std::uint16_t(42) == toml::find<std::uint16_t>(std::move(v), "key"));
}
}
@@ -449,11 +216,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_floating_type, value_type, test_value_ty
{
{
value_type v{{"key", 3.14}};
const double ref(3.14);
BOOST_TEST(static_cast<float >(ref) == toml::find<float >(v, "key"));
BOOST_TEST( ref == toml::find<double >(v, "key"));
BOOST_TEST(static_cast<long double>(ref) == toml::find<long double>(v, "key"));
BOOST_TEST(static_cast<float >(ref) == toml::find<float >(std::move(v), "key"));
BOOST_TEST(static_cast<float >(3.14) == toml::find<float >(v, "key"));
BOOST_TEST(static_cast<double >(3.14) == toml::find<double >(v, "key"));
BOOST_TEST(static_cast<long double>(3.14) == toml::find<long double>(v, "key"));
}
}
@@ -471,11 +236,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_string_type, value_type, test_value_type
toml::find<std::string>(v, "key") += "bar";
BOOST_TEST("foobar" == toml::find<std::string>(v, "key"));
}
{
value_type v{{"key", toml::string("foo", toml::string_t::literal)}};
const auto moved = toml::find<std::string>(std::move(v), "key");
BOOST_TEST("foo" == moved);
}
#if __cplusplus >= 201703L
{
@@ -514,14 +274,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array, value_type, test_value_types
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(v, "key");
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(v, "key");
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -532,53 +292,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array, value_type, test_value_types
BOOST_TEST(2.71 == pr.second);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_move_toml_array, value_type, test_value_types)
{
value_type v1{{"key", {42, 54, 69, 72}}};
value_type v2{{"key", {42, 54, 69, 72}}};
value_type v3{{"key", {42, 54, 69, 72}}};
value_type v4{{"key", {42, 54, 69, 72}}};
value_type v5{{"key", {42, 54, 69, 72}}};
const std::vector<int> vec = toml::find<std::vector<int>>(std::move(v1), "key");
const std::list<short> lst = toml::find<std::list<short>>(std::move(v2), "key");
const std::deque<std::int64_t> deq = toml::find<std::deque<std::int64_t>>(std::move(v3), "key");
BOOST_TEST(42 == vec.at(0));
BOOST_TEST(54 == vec.at(1));
BOOST_TEST(69 == vec.at(2));
BOOST_TEST(72 == vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
BOOST_TEST(static_cast<short>(42) == *(iter++));
BOOST_TEST(static_cast<short>(54) == *(iter++));
BOOST_TEST(static_cast<short>(69) == *(iter++));
BOOST_TEST(static_cast<short>(72) == *(iter++));
BOOST_TEST(static_cast<std::int64_t>(42) == deq.at(0));
BOOST_TEST(static_cast<std::int64_t>(54) == deq.at(1));
BOOST_TEST(static_cast<std::int64_t>(69) == deq.at(2));
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(std::move(v4), "key");
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(std::move(v5), "key");
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
value_type p{{"key", {3.14, 2.71}}};
std::pair<double, double> pr = toml::find<std::pair<double, double> >(std::move(p), "key");
BOOST_TEST(3.14 == pr.first);
BOOST_TEST(2.71 == pr.second);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array_of_array, value_type, test_value_types)
{
value_type v1{42, 54, 69, 72};
@@ -610,153 +323,62 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array_of_array, value_type, test_va
BOOST_TEST(std::get<1>(t).at(2) == "baz");
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_move_toml_array_of_array, value_type, test_value_types)
{
value_type a1{42, 54, 69, 72};
value_type a2{"foo", "bar", "baz"};
value_type v1{{"key", {a1, a2}}};
value_type v2{{"key", {a1, a2}}};
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::find<std::pair<std::vector<int>, std::vector<std::string>>>(std::move(v1), "key");
BOOST_TEST(p.first.at(0) == 42);
BOOST_TEST(p.first.at(1) == 54);
BOOST_TEST(p.first.at(2) == 69);
BOOST_TEST(p.first.at(3) == 72);
BOOST_TEST(p.second.at(0) == "foo");
BOOST_TEST(p.second.at(1) == "bar");
BOOST_TEST(p.second.at(2) == "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::find<std::tuple<std::vector<int>, std::vector<std::string>>>(std::move(v2), "key");
BOOST_TEST(std::get<0>(t).at(0) == 42);
BOOST_TEST(std::get<0>(t).at(1) == 54);
BOOST_TEST(std::get<0>(t).at(2) == 69);
BOOST_TEST(std::get<0>(t).at(3) == 72);
BOOST_TEST(std::get<1>(t).at(0) == "foo");
BOOST_TEST(std::get<1>(t).at(1) == "bar");
BOOST_TEST(std::get<1>(t).at(2) == "baz");
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_table, value_type, test_value_types)
{
{
value_type v1{{"key", {
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::find<std::map<std::string, int>>(v1, "key");
BOOST_TEST(v.at("key1") == 1);
BOOST_TEST(v.at("key2") == 2);
BOOST_TEST(v.at("key3") == 3);
BOOST_TEST(v.at("key4") == 4);
}
{
value_type v1{{"key", {
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::find<std::map<std::string, int>>(std::move(v1), "key");
BOOST_TEST(v.at("key1") == 1);
BOOST_TEST(v.at("key2") == 2);
BOOST_TEST(v.at("key3") == 3);
BOOST_TEST(v.at("key4") == 4);
}
value_type v1{{"key", {
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::find<std::map<std::string, int>>(v1, "key");
BOOST_TEST(v.at("key1") == 1);
BOOST_TEST(v.at("key2") == 2);
BOOST_TEST(v.at("key3") == 3);
BOOST_TEST(v.at("key4") == 4);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_local_date, value_type, test_value_types)
{
{
value_type v1{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}};
const auto date = std::chrono::system_clock::to_time_t(
toml::find<std::chrono::system_clock::time_point>(v1, "key"));
value_type v1{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}};
const auto date = std::chrono::system_clock::to_time_t(
toml::find<std::chrono::system_clock::time_point>(v1, "key"));
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);
BOOST_TEST(c == date);
}
{
value_type v1{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}};
const auto date = std::chrono::system_clock::to_time_t(
toml::find<std::chrono::system_clock::time_point>(std::move(v1), "key"));
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);
BOOST_TEST(c == date);
}
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);
BOOST_TEST(c == date);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_local_time, value_type, test_value_types)
{
{
value_type v1{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::find<std::chrono::seconds>(v1, "key");
BOOST_CHECK(time == std::chrono::hours(12) +
std::chrono::minutes(30) + std::chrono::seconds(45));
}
{
value_type v1{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::find<std::chrono::seconds>(std::move(v1), "key");
BOOST_CHECK(time == std::chrono::hours(12) +
std::chrono::minutes(30) + std::chrono::seconds(45));
}
value_type v1{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::find<std::chrono::seconds>(v1, "key");
BOOST_CHECK(time == std::chrono::hours(12) +
std::chrono::minutes(30) + std::chrono::seconds(45));
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_local_datetime, value_type, test_value_types)
{
{
value_type v1{{"key", toml::local_datetime(
toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_time{12, 30, 45})}};
value_type v1{{"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::find<std::chrono::system_clock::time_point>(v1, "key"));
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);
BOOST_TEST(c == date);
}
{
value_type v1{{"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::find<std::chrono::system_clock::time_point>(std::move(v1), "key"));
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);
BOOST_TEST(c == date);
}
const auto date = std::chrono::system_clock::to_time_t(
toml::find<std::chrono::system_clock::time_point>(v1, "key"));
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);
BOOST_TEST(c == date);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_value_types)
@@ -806,28 +428,5 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_va
BOOST_TEST(tm.tm_min == 30);
BOOST_TEST(tm.tm_sec == 0);
}
{
value_type v1{{"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::find<std::chrono::system_clock::time_point>(std::move(v1), "key");
const auto timet = std::chrono::system_clock::to_time_t(date);
// get time_t as gmtime (2018-04-01T03:30:00Z)
const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe!
BOOST_CHECK(tmp);
const auto tm = *tmp;
BOOST_TEST(tm.tm_year + 1900 == 2018);
BOOST_TEST(tm.tm_mon + 1 == 4);
BOOST_TEST(tm.tm_mday == 1);
BOOST_TEST(tm.tm_hour == 20);
BOOST_TEST(tm.tm_min == 30);
BOOST_TEST(tm.tm_sec == 0);
}
}

350
tests/test_find_fuzzy.cpp Normal file
View File

@@ -0,0 +1,350 @@
#define BOOST_TEST_MODULE "test_find_fuzzy"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
BOOST_AUTO_TEST_CASE(test_levenstein_distance)
{
const toml::levenstein_matcher lev(1);
// distance == 0
{
const std::string s1("foobar");
const std::string s2 = s1;
BOOST_TEST(lev.distance(s1, s2) == 0);
}
{
const std::string s1("foobar");
const std::string s2("foobaz");
BOOST_TEST(lev.distance(s1, s2) == 1);
}
{
const std::string s1("foobar"); // insertion (+x)
const std::string s2("fooxbar");
BOOST_TEST(lev.distance(s1, s2) == 1);
}
{
const std::string s1("foobar");
const std::string s2("fooar"); // insertion(+b)
BOOST_TEST(lev.distance(s1, s2) == 1);
}
// distance > 1
{
const std::string s1("foobar");
const std::string s2("fooquux");
BOOST_TEST(lev.distance(s1, s2) == 4);
}
{
const std::string s1("foobar");
const std::string s2("fooqu");
BOOST_TEST(s1 != s2);
BOOST_TEST(lev.distance(s1, s2) == 3);
}
}
BOOST_AUTO_TEST_CASE(test_find_fuzzy)
{
{
toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy(v, "key") == toml::value("value"));
BOOST_CHECK_THROW(toml::find_fuzzy(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<
toml::value&, decltype(toml::find_fuzzy(v, "key"))>::value, "");
toml::find_fuzzy(v, "key") = "foobar";
BOOST_TEST(toml::find(v, "keu") == toml::value("foobar"));
}
{
const toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy(v, "key") == toml::value("value"));
BOOST_CHECK_THROW(toml::find_fuzzy(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<
toml::value const&, decltype(toml::find_fuzzy(v, "key"))>::value, "");
}
{
toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy(std::move(v), "key") == toml::value("value"));
static_assert(std::is_same<
toml::value&&, decltype(toml::find_fuzzy(std::move(v), "key"))>::value, "");
}
{
toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_CHECK_THROW(toml::find_fuzzy(std::move(v), "kiwi"), std::out_of_range);
static_assert(std::is_same<
toml::value&&, decltype(toml::find_fuzzy(std::move(v), "key"))>::value, "");
}
// find with conversion
{
toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy<int>(v, "key") == 42);
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<int,
decltype(toml::find_fuzzy<int>(v, "key"))>::value, "");
}
{
const toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy<int>(v, "key") == 42);
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<int,
decltype(toml::find_fuzzy<int>(v, "key"))>::value, "");
}
{
toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy<int>(std::move(v), "key") == 42);
static_assert(std::is_same<int,
decltype(toml::find_fuzzy<int>(std::move(v), "key"))>::value, "");
}
{
toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(std::move(v), "kiwi"), std::out_of_range);
}
}
BOOST_AUTO_TEST_CASE(test_find_fuzzy_throw)
{
{
toml::value v{
{"keu", "value"}, // typo! key -> keu
{"ky", "value"} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy(v, "key"), std::out_of_range);
}
{
const toml::value v{
{"keu", "value"}, // typo! key -> keu
{"ky", "value"} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy(v, "key"), std::out_of_range);
}
{
toml::value v{
{"keu", "value"}, // typo! key -> keu
{"ky", "value"} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy(std::move(v), "key"), std::out_of_range);
}
{
toml::value v{
{"keu", 42}, // typo! key -> keu
{"ky", 42} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "key"), std::out_of_range);
}
{
const toml::value v{
{"keu", 42}, // typo! key -> keu
{"ky", 42} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "key"), std::out_of_range);
}
{
toml::value v{
{"keu", 42}, // typo! key -> keu
{"ky", 42} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(std::move(v), "key"), std::out_of_range);
}
}
BOOST_AUTO_TEST_CASE(test_find_throw_typo_aware_exception)
{
using namespace toml::literals::toml_literals;
const toml::levenstein_matcher lev(1);
{
toml::value v = u8R"(
keu = "value"
)"_toml;
BOOST_CHECK_THROW(toml::find(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find(v, "key", lev);
(void)ret; // suppress unused variable
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<
toml::value&, decltype(toml::find(v, "key"))>::value, "");
}
{
const toml::value v = u8R"(
keu = "value"
)"_toml;
BOOST_CHECK_THROW(toml::find(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find(v, "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<
toml::value const&, decltype(toml::find(v, "key"))>::value, "");
}
{
toml::value v = u8R"(
keu = "value"
)"_toml;
bool thrown = false; // since it moves, we need to check both once
try
{
const auto& ret = toml::find(std::move(v), "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
thrown = true;
// std::cout << what << std::endl;
}
BOOST_TEST(thrown);
static_assert(std::is_same<
toml::value&, decltype(toml::find(v, "key"))>::value, "");
}
}
BOOST_AUTO_TEST_CASE(test_find_throw_conversion_typo_aware_exception)
{
using namespace toml::literals::toml_literals;
const toml::levenstein_matcher lev(1);
{
toml::value v = u8R"(
keu = 42
)"_toml;
BOOST_CHECK_THROW(toml::find<int>(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find<int>(v, "key", lev);
(void)ret; // suppress unused variable
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<int,
decltype(toml::find<int>(v, "key"))>::value, "");
}
{
const toml::value v = u8R"(
keu = 42
)"_toml;
BOOST_CHECK_THROW(toml::find<int>(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find<int>(v, "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<int,
decltype(toml::find<int>(v, "key"))>::value, "");
}
{
toml::value v = u8R"(
keu = 42
)"_toml;
bool thrown = false; // since it moves, we need to check both once
try
{
const auto& ret = toml::find<int>(std::move(v), "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
thrown = true;
// std::cout << what << std::endl;
}
BOOST_TEST(thrown);
static_assert(std::is_same<int,
decltype(toml::find<int>(v, "key"))>::value, "");
}
}

View File

@@ -121,55 +121,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_exact, value_type, test_value_types)
}
#undef TOML11_TEST_FIND_OR_EXACT
#define TOML11_TEST_FIND_OR_MOVE(toml_type, init_expr, opt_expr) \
{ \
using namespace test; \
const toml::toml_type init init_expr ; \
toml::toml_type opt opt_expr ; \
value_type v{{"key", init}}; \
BOOST_TEST(init != opt); \
const auto moved = toml::find_or(std::move(v), "key", std::move(opt));\
BOOST_TEST(init == moved); \
} \
/**/
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_move, value_type, test_value_types)
{
TOML11_TEST_FIND_OR_MOVE(boolean, ( true), (false))
TOML11_TEST_FIND_OR_MOVE(integer, ( 42), ( 54))
TOML11_TEST_FIND_OR_MOVE(floating, ( 3.14), ( 2.71))
TOML11_TEST_FIND_OR_MOVE(string, ("foo"), ("bar"))
TOML11_TEST_FIND_OR_MOVE(local_time, (12, 30, 45), (6, 0, 30))
TOML11_TEST_FIND_OR_MOVE(local_date, (2019, toml::month_t::Apr, 1),
(1999, toml::month_t::Jan, 2))
TOML11_TEST_FIND_OR_MOVE(local_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30))
)
TOML11_TEST_FIND_OR_MOVE(offset_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0))
)
{
typename value_type::array_type init{1,2,3,4,5};
typename value_type::array_type opt {6,7,8,9,10};
value_type v{{"key", init}};
BOOST_TEST(init != opt);
const auto moved = toml::find_or(std::move(v), "key", std::move(opt));
BOOST_TEST(init == moved);
}
{
typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}};
typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}};
value_type v{{"key", init}};
BOOST_TEST(init != opt);
const auto moved = toml::find_or(std::move(v), "key", std::move(opt));
BOOST_TEST(init == moved);
}
}
#undef TOML11_TEST_FIND_OR_MOVE
#define TOML11_TEST_FIND_OR_MODIFY(toml_type, init_expr, opt_expr)\
{ \
using namespace test; \
@@ -353,20 +304,10 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_fallback, value_type, test_value_type
BOOST_AUTO_TEST_CASE(test_find_or_integer)
{
{
toml::value v{{"num", 42}};
toml::value v = toml::table{{"num", 42}};
BOOST_TEST(42u == toml::find_or(v, "num", 0u));
BOOST_TEST(0u == toml::find_or(v, "foo", 0u));
}
{
toml::value v{{"num", 42}};
const auto moved = toml::find_or(std::move(v), "num", 0u);
BOOST_TEST(42u == moved);
}
{
toml::value v{{"num", 42}};
const auto moved = toml::find_or(std::move(v), "foo", 0u);
BOOST_TEST(0u == moved);
}
}
BOOST_AUTO_TEST_CASE(test_find_or_floating)
@@ -375,17 +316,7 @@ BOOST_AUTO_TEST_CASE(test_find_or_floating)
toml::value v1{{"key", 42}};
toml::value v2{{"key", 3.14}};
BOOST_TEST(2.71f == toml::find_or(v1, "key", 2.71f));
const double ref(3.14);
BOOST_TEST(static_cast<float>(ref) == toml::find_or(v2, "key", 2.71f));
}
{
toml::value v1{{"key", 42}};
toml::value v2{{"key", 3.14}};
const auto moved1 = toml::find_or(std::move(v1), "key", 2.71f);
const auto moved2 = toml::find_or(std::move(v2), "key", 2.71f);
BOOST_TEST(2.71f == moved1);
const double ref(3.14);
BOOST_TEST(static_cast<float>(ref) == moved2);
BOOST_TEST(static_cast<float>(double(3.14)) == toml::find_or(v2, "key", 2.71f));
}
}
@@ -414,32 +345,6 @@ BOOST_AUTO_TEST_CASE(test_find_or_string)
s1 = "bazqux"; // restoring moved value
BOOST_TEST("bazqux" == toml::find_or(std::move(v2), "key", std::move(s1)));
}
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key", 42}};
std::string s1("bazqux");
const auto moved1 = toml::find_or(std::move(v1), "key", s1);
const auto moved2 = toml::find_or(std::move(v2), "key", s1);
BOOST_TEST("foobar" == moved1);
BOOST_TEST("bazqux" == moved2);
}
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key", 42}};
std::string s1("bazqux");
std::string s2("bazqux");
const auto moved1 = toml::find_or(std::move(v1), "key", std::move(s1));
const auto moved2 = toml::find_or(std::move(v2), "key", std::move(s2));
BOOST_TEST("foobar" == moved1);
BOOST_TEST("bazqux" == moved2);
}
// string literal
{
toml::value v1 = toml::table{{"key", "foobar"}};
@@ -452,28 +357,6 @@ BOOST_AUTO_TEST_CASE(test_find_or_string)
BOOST_TEST("foobar" == toml::find_or(v1, "key", lit));
BOOST_TEST("bazqux" == toml::find_or(v2, "key", lit));
}
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key",42}};
const auto moved1 = toml::find_or(std::move(v1), "key", "bazqux");
const auto moved2 = toml::find_or(std::move(v2), "key", "bazqux");
BOOST_TEST("foobar" == moved1);
BOOST_TEST("bazqux" == moved2);
}
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key",42}};
const char* lit = "bazqux";
const auto moved1 = toml::find_or(std::move(v1), "key", lit);
const auto moved2 = toml::find_or(std::move(v2), "key", lit);
BOOST_TEST("foobar" == moved1);
BOOST_TEST("bazqux" == moved2);
}
}
BOOST_AUTO_TEST_CASE(test_find_or_map)
@@ -494,7 +377,7 @@ BOOST_AUTO_TEST_CASE(test_find_or_map)
BOOST_TEST(key.at("key") == "value");
}
{
toml::value v1{
const toml::value v1{
{"key", {{"key", "value"}}}
};
@@ -507,35 +390,4 @@ BOOST_AUTO_TEST_CASE(test_find_or_map)
BOOST_TEST(key.size() == 1u);
BOOST_TEST(key.at("key") == "value");
}
{
toml::value v1{
{"key", {{"key", "value"}}}
};
toml::value v2(v1);
const auto key = toml::find_or(std::move(v1), "key", map_type{});
const auto key2 = toml::find_or(std::move(v2), "key2", map_type{});
BOOST_TEST(!key.empty());
BOOST_TEST(key2.empty());
BOOST_TEST(key.size() == 1u);
BOOST_TEST(key.at("key") == "value");
}
{
toml::value v1{
{"key", {{"key", "value"}}}
};
toml::value v2(v1);
const auto key = toml::find_or<map_type>(std::move(v1), "key", map_type{});
const auto key2 = toml::find_or<map_type>(std::move(v2), "key2", map_type{});
BOOST_TEST(!key.empty());
BOOST_TEST(key2.empty());
BOOST_TEST(key.size() == 1u);
BOOST_TEST(key.at("key") == "value");
}
}

View File

@@ -31,9 +31,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::boolean>(v) = false;
BOOST_TEST(false == toml::get<toml::boolean>(v));
toml::boolean x = toml::get<toml::boolean>(std::move(v));
BOOST_TEST(false == x);
}
{
value_type v(42);
@@ -41,9 +38,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::integer>(v) = 54;
BOOST_TEST(toml::integer(54) == toml::get<toml::integer>(v));
toml::integer x = toml::get<toml::integer>(std::move(v));
BOOST_TEST(toml::integer(54) == x);
}
{
value_type v(3.14);
@@ -51,9 +45,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::floating>(v) = 2.71;
BOOST_TEST(toml::floating(2.71) == toml::get<toml::floating>(v));
toml::floating x = toml::get<toml::floating>(std::move(v));
BOOST_TEST(toml::floating(2.71) == x);
}
{
value_type v("foo");
@@ -63,9 +54,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::string>(v).str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::basic) ==
toml::get<toml::string>(v));
toml::string x = toml::get<toml::string>(std::move(v));
BOOST_TEST(toml::string("foobar") == x);
}
{
value_type v("foo", toml::string_t::literal);
@@ -75,9 +63,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::string>(v).str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::literal) ==
toml::get<toml::string>(v));
toml::string x = toml::get<toml::string>(std::move(v));
BOOST_TEST(toml::string("foobar", toml::string_t::literal) == x);
}
{
toml::local_date d(2018, toml::month_t::Apr, 22);
@@ -87,9 +72,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::local_date>(v).year = 2017;
d.year = 2017;
BOOST_TEST(d == toml::get<toml::local_date>(v));
toml::local_date x = toml::get<toml::local_date>(std::move(v));
BOOST_TEST(d == x);
}
{
toml::local_time t(12, 30, 45);
@@ -99,9 +81,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::local_time>(v).hour = 9;
t.hour = 9;
BOOST_TEST(t == toml::get<toml::local_time>(v));
toml::local_time x = toml::get<toml::local_time>(std::move(v));
BOOST_TEST(t == x);
}
{
toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22),
@@ -112,9 +91,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::local_datetime>(v).date.year = 2017;
dt.date.year = 2017;
BOOST_TEST(dt == toml::get<toml::local_datetime>(v));
toml::local_datetime x = toml::get<toml::local_datetime>(std::move(v));
BOOST_TEST(dt == x);
}
{
toml::offset_datetime dt(toml::local_datetime(
@@ -126,9 +102,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::offset_datetime>(v).date.year = 2017;
dt.date.year = 2017;
BOOST_TEST(dt == toml::get<toml::offset_datetime>(v));
toml::offset_datetime x = toml::get<toml::offset_datetime>(std::move(v));
BOOST_TEST(dt == x);
}
{
using array_type = typename value_type::array_type;
@@ -141,9 +114,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<array_type>(v).push_back(value_type(123));
vec.push_back(value_type(123));
BOOST_TEST(vec == toml::get<array_type>(v));
array_type x = toml::get<array_type>(std::move(v));
BOOST_TEST(vec == x);
}
{
using table_type = typename value_type::table_type;
@@ -156,9 +126,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<table_type>(v)["key3"] = value_type(123);
tab["key3"] = value_type(123);
BOOST_TEST(tab == toml::get<table_type>(v));
table_type x = toml::get<table_type>(std::move(v));
BOOST_TEST(tab == x);
}
{
value_type v1(42);
@@ -167,9 +134,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
value_type v2(54);
toml::get<value_type>(v1) = v2;
BOOST_TEST(v2 == toml::get<value_type>(v1));
value_type x = toml::get<value_type>(std::move(v1));
BOOST_TEST(v2 == x);
}
}
@@ -186,8 +150,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_integer_type, value_type, test_value_type
BOOST_TEST(std::uint64_t(42) == toml::get<std::uint64_t>(v));
BOOST_TEST(std::int16_t(42) == toml::get<std::int16_t >(v));
BOOST_TEST(std::uint16_t(42) == toml::get<std::uint16_t>(v));
BOOST_TEST(std::uint16_t(42) == toml::get<std::uint16_t>(std::move(v)));
}
}
@@ -195,11 +157,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_floating_type, value_type, test_value_typ
{
{
value_type v(3.14);
const double ref(3.14);
BOOST_TEST(static_cast<float >(ref) == toml::get<float >(v));
BOOST_TEST( ref == toml::get<double >(v));
BOOST_TEST(static_cast<long double>(ref) == toml::get<long double>(v));
BOOST_TEST(static_cast<float >(ref) == toml::get<float>(std::move(v)));
BOOST_TEST(static_cast<float >(3.14) == toml::get<float >(v));
BOOST_TEST(static_cast<double >(3.14) == toml::get<double >(v));
BOOST_TEST(static_cast<long double>(3.14) == toml::get<long double>(v));
}
}
@@ -210,18 +170,12 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_string_type, value_type, test_value_types
BOOST_TEST("foo" == toml::get<std::string>(v));
toml::get<std::string>(v) += "bar";
BOOST_TEST("foobar" == toml::get<std::string>(v));
const auto x = toml::get<std::string>(std::move(v));
BOOST_TEST("foobar" == x);
}
{
value_type v("foo", toml::string_t::literal);
BOOST_TEST("foo" == toml::get<std::string>(v));
toml::get<std::string>(v) += "bar";
BOOST_TEST("foobar" == toml::get<std::string>(v));
const auto x = toml::get<std::string>(std::move(v));
BOOST_TEST("foobar" == x);
}
#if __cplusplus >= 201703L
@@ -238,176 +192,92 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_string_type, value_type, test_value_types
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types)
{
{
const value_type v{42, 54, 69, 72};
const value_type v{42, 54, 69, 72};
const std::vector<int> vec = toml::get<std::vector<int>>(v);
const std::list<short> lst = toml::get<std::list<short>>(v);
const std::deque<std::int64_t> deq = toml::get<std::deque<std::int64_t>>(v);
const std::vector<int> vec = toml::get<std::vector<int>>(v);
const std::list<short> lst = toml::get<std::list<short>>(v);
const std::deque<std::int64_t> deq = toml::get<std::deque<std::int64_t>>(v);
BOOST_TEST(42 == vec.at(0));
BOOST_TEST(54 == vec.at(1));
BOOST_TEST(69 == vec.at(2));
BOOST_TEST(72 == vec.at(3));
BOOST_TEST(42 == vec.at(0));
BOOST_TEST(54 == vec.at(1));
BOOST_TEST(69 == vec.at(2));
BOOST_TEST(72 == vec.at(3));
std::list<short>::const_iterator iter = lst.begin();
BOOST_TEST(static_cast<short>(42) == *(iter++));
BOOST_TEST(static_cast<short>(54) == *(iter++));
BOOST_TEST(static_cast<short>(69) == *(iter++));
BOOST_TEST(static_cast<short>(72) == *(iter++));
std::list<short>::const_iterator iter = lst.begin();
BOOST_TEST(static_cast<short>(42) == *(iter++));
BOOST_TEST(static_cast<short>(54) == *(iter++));
BOOST_TEST(static_cast<short>(69) == *(iter++));
BOOST_TEST(static_cast<short>(72) == *(iter++));
BOOST_TEST(static_cast<std::int64_t>(42) == deq.at(0));
BOOST_TEST(static_cast<std::int64_t>(54) == deq.at(1));
BOOST_TEST(static_cast<std::int64_t>(69) == deq.at(2));
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
BOOST_TEST(static_cast<std::int64_t>(42) == deq.at(0));
BOOST_TEST(static_cast<std::int64_t>(54) == deq.at(1));
BOOST_TEST(static_cast<std::int64_t>(69) == deq.at(2));
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::get<std::array<int, 4>>(v);
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
std::array<int, 4> ary = toml::get<std::array<int, 4>>(v);
BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(v);
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(v);
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
const value_type p{3.14, 2.71};
std::pair<double, double> pr = toml::get<std::pair<double, double> >(p);
BOOST_TEST(3.14 == pr.first);
BOOST_TEST(2.71 == pr.second);
}
{
value_type v{42, 54, 69, 72};
const std::vector<int> vec = toml::get<std::vector<int>>(std::move(v));
BOOST_TEST(42 == vec.at(0));
BOOST_TEST(54 == vec.at(1));
BOOST_TEST(69 == vec.at(2));
BOOST_TEST(72 == vec.at(3));
}
{
value_type v{42, 54, 69, 72};
const std::deque<int> deq = toml::get<std::deque<int>>(std::move(v));
BOOST_TEST(42 == deq.at(0));
BOOST_TEST(54 == deq.at(1));
BOOST_TEST(69 == deq.at(2));
BOOST_TEST(72 == deq.at(3));
}
{
value_type v{42, 54, 69, 72};
const std::list<int> lst = toml::get<std::list<int>>(std::move(v));
std::list<int>::const_iterator iter = lst.begin();
BOOST_TEST(42 == *(iter++));
BOOST_TEST(54 == *(iter++));
BOOST_TEST(69 == *(iter++));
BOOST_TEST(72 == *(iter++));
}
{
value_type v{42, 54, 69, 72};
std::array<int, 4> ary = toml::get<std::array<int, 4>>(std::move(v));
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
}
{
value_type v{42, 54, 69, 72};
std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(std::move(v));
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
}
const value_type p{3.14, 2.71};
std::pair<double, double> pr = toml::get<std::pair<double, double> >(p);
BOOST_TEST(3.14 == pr.first);
BOOST_TEST(2.71 == pr.second);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array_of_array, value_type, test_value_types)
{
{
const value_type v1{42, 54, 69, 72};
const value_type v2{"foo", "bar", "baz"};
const value_type v{v1, v2};
const value_type v1{42, 54, 69, 72};
const value_type v2{"foo", "bar", "baz"};
const value_type v{v1, v2};
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::get<std::pair<std::vector<int>, std::vector<std::string>>>(v);
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::get<std::pair<std::vector<int>, std::vector<std::string>>>(v);
BOOST_TEST(p.first.size() == 4u);
BOOST_TEST(p.first.at(0) == 42);
BOOST_TEST(p.first.at(1) == 54);
BOOST_TEST(p.first.at(2) == 69);
BOOST_TEST(p.first.at(3) == 72);
BOOST_TEST(p.first.at(0) == 42);
BOOST_TEST(p.first.at(1) == 54);
BOOST_TEST(p.first.at(2) == 69);
BOOST_TEST(p.first.at(3) == 72);
BOOST_TEST(p.second.size() == 3u);
BOOST_TEST(p.second.at(0) == "foo");
BOOST_TEST(p.second.at(1) == "bar");
BOOST_TEST(p.second.at(2) == "baz");
BOOST_TEST(p.second.at(0) == "foo");
BOOST_TEST(p.second.at(1) == "bar");
BOOST_TEST(p.second.at(2) == "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::get<std::tuple<std::vector<int>, std::vector<std::string>>>(v);
std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::get<std::tuple<std::vector<int>, std::vector<std::string>>>(v);
BOOST_TEST(std::get<0>(t).at(0) == 42);
BOOST_TEST(std::get<0>(t).at(1) == 54);
BOOST_TEST(std::get<0>(t).at(2) == 69);
BOOST_TEST(std::get<0>(t).at(3) == 72);
BOOST_TEST(std::get<0>(t).at(0) == 42);
BOOST_TEST(std::get<0>(t).at(1) == 54);
BOOST_TEST(std::get<0>(t).at(2) == 69);
BOOST_TEST(std::get<0>(t).at(3) == 72);
BOOST_TEST(std::get<1>(t).at(0) == "foo");
BOOST_TEST(std::get<1>(t).at(1) == "bar");
BOOST_TEST(std::get<1>(t).at(2) == "baz");
}
{
const value_type v1{42, 54, 69, 72};
const value_type v2{"foo", "bar", "baz"};
value_type v{v1, v2};
std::pair<std::vector<int>, std::vector<std::string>> p =
toml::get<std::pair<std::vector<int>, std::vector<std::string>>>(std::move(v));
BOOST_TEST(p.first.size() == 4u);
BOOST_TEST(p.first.at(0) == 42);
BOOST_TEST(p.first.at(1) == 54);
BOOST_TEST(p.first.at(2) == 69);
BOOST_TEST(p.first.at(3) == 72);
BOOST_TEST(p.second.size() == 3u);
BOOST_TEST(p.second.at(0) == "foo");
BOOST_TEST(p.second.at(1) == "bar");
BOOST_TEST(p.second.at(2) == "baz");
}
BOOST_TEST(std::get<1>(t).at(0) == "foo");
BOOST_TEST(std::get<1>(t).at(1) == "bar");
BOOST_TEST(std::get<1>(t).at(2) == "baz");
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_table, value_type, test_value_types)
{
{
const value_type v1{
{"key1", 1},
{"key2", 2},
{"key3", 3},
{"key4", 4}
};
const auto v = toml::get<std::map<std::string, int>>(v1);
BOOST_TEST(v.at("key1") == 1);
BOOST_TEST(v.at("key2") == 2);
BOOST_TEST(v.at("key3") == 3);
BOOST_TEST(v.at("key4") == 4);
}
{
value_type v1{
{"key1", 1},
{"key2", 2},
{"key3", 3},
{"key4", 4}
};
const auto v = toml::get<std::map<std::string, int>>(std::move(v1));
BOOST_TEST(v.at("key1") == 1);
BOOST_TEST(v.at("key2") == 2);
BOOST_TEST(v.at("key3") == 3);
BOOST_TEST(v.at("key4") == 4);
}
const value_type v1{
{"key1", 1},
{"key2", 2},
{"key3", 3},
{"key4", 4}
};
const auto v = toml::get<std::map<std::string, int>>(v1);
BOOST_TEST(v.at("key1") == 1);
BOOST_TEST(v.at("key2") == 2);
BOOST_TEST(v.at("key3") == 3);
BOOST_TEST(v.at("key4") == 4);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_local_date, value_type, test_value_types)

View File

@@ -25,11 +25,6 @@ using test_value_types = std::tuple<
namespace test
{
// to compare result values in BOOST_TEST().
//
// BOOST_TEST outputs the expected and actual values. Thus it includes the
// output stream operator inside. To compile it, we need operator<<s for
// containers to compare.
template<typename charT, typename traits, typename T, typename Alloc>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const std::vector<T, Alloc>& v)
@@ -126,55 +121,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_or_exact, value_type, test_value_types)
}
#undef TOML11_TEST_GET_OR_EXACT
#define TOML11_TEST_GET_OR_MOVE_EXACT(toml_type, init_expr, opt_expr)\
{ \
using namespace test; \
const toml::toml_type init init_expr ; \
toml::toml_type opt opt_expr ; \
value_type v(init); \
BOOST_TEST(init != opt); \
const auto opt_ = toml::get_or(std::move(v), std::move(opt));\
BOOST_TEST(init == opt_); \
} \
/**/
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_or_move, value_type, test_value_types)
{
TOML11_TEST_GET_OR_MOVE_EXACT(boolean, ( true), (false))
TOML11_TEST_GET_OR_MOVE_EXACT(integer, ( 42), ( 54))
TOML11_TEST_GET_OR_MOVE_EXACT(floating, ( 3.14), ( 2.71))
TOML11_TEST_GET_OR_MOVE_EXACT(string, ("foo"), ("bar"))
TOML11_TEST_GET_OR_MOVE_EXACT(local_time, (12, 30, 45), (6, 0, 30))
TOML11_TEST_GET_OR_MOVE_EXACT(local_date, (2019, toml::month_t::Apr, 1),
(1999, toml::month_t::Jan, 2))
TOML11_TEST_GET_OR_MOVE_EXACT(local_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30))
)
TOML11_TEST_GET_OR_MOVE_EXACT(offset_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0))
)
{
const typename value_type::array_type init{1,2,3,4,5};
typename value_type::array_type opt {6,7,8,9,10};
value_type v(init);
BOOST_TEST(init != opt);
const auto opt_ = toml::get_or(std::move(v), std::move(opt));
BOOST_TEST(init == opt_);
}
{
const typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}};
typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}};
value_type v(init);
BOOST_TEST(init != opt);
const auto opt_ = toml::get_or(std::move(v), std::move(opt));
BOOST_TEST(init == opt_);
}
}
#undef TOML11_TEST_GET_OR_MOVE_EXACT
#define TOML11_TEST_GET_OR_MODIFY(toml_type, init_expr, opt_expr)\
{ \
using namespace test; \
@@ -363,13 +309,6 @@ BOOST_AUTO_TEST_CASE(test_get_or_integer)
BOOST_TEST(42u == toml::get_or(v1, 0u));
BOOST_TEST(0u == toml::get_or(v2, 0u));
}
{
toml::value v1(42);
toml::value v2(3.14);
BOOST_TEST(42u == toml::get_or(std::move(v1), 0u));
BOOST_TEST(0u == toml::get_or(std::move(v2), 0u));
}
}
BOOST_AUTO_TEST_CASE(test_get_or_floating)
@@ -380,12 +319,6 @@ BOOST_AUTO_TEST_CASE(test_get_or_floating)
BOOST_TEST(2.71f == toml::get_or(v1, 2.71f));
BOOST_TEST(static_cast<float>(v2.as_floating()) == toml::get_or(v2, 2.71f));
}
{
toml::value v1(42);
toml::value v2(3.14);
BOOST_TEST(2.71f == toml::get_or(std::move(v1), 2.71f));
BOOST_TEST(static_cast<float>(3.14) == toml::get_or(std::move(v2), 2.71f));
}
}
BOOST_AUTO_TEST_CASE(test_get_or_string)
@@ -412,16 +345,7 @@ BOOST_AUTO_TEST_CASE(test_get_or_string)
BOOST_TEST("foobar" == toml::get_or(v1, std::move(s1)));
BOOST_TEST("bazqux" == toml::get_or(v2, std::move(s1)));
}
{
toml::value v1("foobar");
toml::value v2(42);
std::string s1("bazqux");
const std::string s2("bazqux");
BOOST_TEST("foobar" == toml::get_or(std::move(v1), s1));
BOOST_TEST("bazqux" == toml::get_or(std::move(v2), s1));
}
{
toml::value v1("foobar");
toml::value v2(42);
@@ -433,20 +357,4 @@ BOOST_AUTO_TEST_CASE(test_get_or_string)
BOOST_TEST("foobar" == toml::get_or(v1, lit));
BOOST_TEST("bazqux" == toml::get_or(v2, lit));
}
{
toml::value v1("foobar");
toml::value v2(42);
BOOST_TEST("foobar" == toml::get_or(std::move(v1), "bazqux"));
BOOST_TEST("bazqux" == toml::get_or(std::move(v2), "bazqux"));
}
{
toml::value v1("foobar");
toml::value v2(42);
const char* lit = "bazqux";
BOOST_TEST("foobar" == toml::get_or(v1, lit));
BOOST_TEST("bazqux" == toml::get_or(v2, lit));
}
}

View File

@@ -51,18 +51,10 @@ BOOST_AUTO_TEST_CASE(test_exponential_valid)
TOML11_TEST_LEX_ACCEPT(lex_float, "123E-10", "123E-10");
TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-10", "1_2_3E-10");
TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-1_0", "1_2_3E-1_0");
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part");
// toml-lang/toml master permits leading 0s in exp part (unreleased)
TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-01", "1_2_3E-01");
TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-0_1", "1_2_3E-0_1");
#endif
}
BOOST_AUTO_TEST_CASE(test_exponential_invalid)
{
// accept partially
TOML11_TEST_LEX_ACCEPT(lex_float, "1e1E0", "1e1");
TOML11_TEST_LEX_ACCEPT(lex_float, "1E1e0", "1E1");
}
@@ -72,26 +64,12 @@ BOOST_AUTO_TEST_CASE(test_both_valid)
TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e23", "6.02e23");
TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e+23", "6.02e+23");
TOML11_TEST_LEX_ACCEPT(lex_float, "1.112_650_06e-17", "1.112_650_06e-17");
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part");
// toml-lang/toml master permits leading 0s in exp part (unreleased)
TOML11_TEST_LEX_ACCEPT(lex_float, "1.0e-07", "1.0e-07");
#endif
}
BOOST_AUTO_TEST_CASE(test_both_invalid)
{
TOML11_TEST_LEX_REJECT(lex_float, "01e1.0");
// accept partially
TOML11_TEST_LEX_ACCEPT(lex_float, "1e1.0", "1e1");
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part");
// toml-lang/toml master permits leading 0s in exp part (unreleased)
TOML11_TEST_LEX_ACCEPT(lex_float, "1.0e_01", "1.0");
TOML11_TEST_LEX_ACCEPT(lex_float, "1.0e0__1", "1.0e0");
#endif
TOML11_TEST_LEX_REJECT(lex_float, "01e1.0");
}
BOOST_AUTO_TEST_CASE(test_special_floating_point)

View File

@@ -57,22 +57,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"");
}
BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -99,16 +83,4 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''That's still pointless', she said.'''",
"''''That's still pointless', she said.'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''This,' she said, 'is just a pointless statement.''''",
"''''This,' she said, 'is just a pointless statement.''''");
}

View File

@@ -128,47 +128,3 @@ BOOST_AUTO_TEST_CASE(test_multiline_array_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a));
}
}
BOOST_AUTO_TEST_CASE(test_heterogeneous_array)
{
#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("In strict TOML v0.5.0, heterogeneous arrays are not allowed.");
#else
{
array a(5);
a[0] = toml::value("foo");
a[1] = toml::value(3.14);
a[2] = toml::value(42);
a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
a[4] = toml::value{{"key", "value"}};
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\", 3.14, 42, [\"array\", \"of\", \"hetero-array\", 1], {key = \"value\"}]", toml::value(a));
}
{
array a(5);
a[0] = toml::value("foo");
a[1] = toml::value(3.14);
a[2] = toml::value(42);
a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
a[4] = toml::value{{"key", "value"}};
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n 3.14,\n 42,\n [\"array\", \"of\", \"hetero-array\", 1],\n {key = \"value\"},\n]", toml::value(a));
}
{
array a(5);
a[0] = toml::value("foo");
a[1] = toml::value(3.14);
a[2] = toml::value(42);
a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
a[4] = toml::value{{"key", "value"}};
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n 3.14,#comment\n 42,#comment\n [\"array\", \"of\", \"hetero-array\", 1],#comment\n {key = \"value\"},#comment\n]#comment", toml::value(a));
}
{
array a(5);
a[0] = toml::value("foo");
a[1] = toml::value(3.14);
a[2] = toml::value(42);
a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
a[4] = toml::value{{"key", "value"}};
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n 3.14,\n 42,\n [\"array\",\n \"of\",\n \"hetero-array\",\n 1],\n {key = \"value\"},\n]", toml::value(a));
}
#endif
}

View File

@@ -68,13 +68,6 @@ BOOST_AUTO_TEST_CASE(test_exponential)
TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-1_0", 123e-10);
TOML11_TEST_PARSE_EQUAL(parse_floating, "+0e0", 0.0);
TOML11_TEST_PARSE_EQUAL(parse_floating, "-0e0", -0.0);
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part");
// toml-lang/toml master permits leading 0s in exp part (unreleased)
TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-01", 123e-1);
TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-0_1", 123e-1);
#endif
}
BOOST_AUTO_TEST_CASE(test_exponential_value)
@@ -97,13 +90,6 @@ BOOST_AUTO_TEST_CASE(test_exponential_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1_2_3E-1_0", value(123e-10));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "+0e0", value( 0.0));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "-0e0", value(-0.0));
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part");
// toml-lang/toml master permits leading 0s in exp part (unreleased)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1_2_3E-01", value(123e-1));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1_2_3E-0_1", value(123e-1));
#endif
}
BOOST_AUTO_TEST_CASE(test_fe)
{
@@ -116,12 +102,6 @@ BOOST_AUTO_TEST_CASE(test_fe_vaule)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "6.02e23", value(6.02e23));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "6.02e+23", value(6.02e23));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1.112_650_06e-17", value(1.11265006e-17));
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part");
// toml-lang/toml master permits leading 0s in exp part (unreleased)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "3.141_5e-01", value(3.1415e-1));
#endif
}
BOOST_AUTO_TEST_CASE(test_inf)

View File

@@ -46,19 +46,3 @@ BOOST_AUTO_TEST_CASE(test_inline_table_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "{type.name = \"pug\"}", value(t));
}
}
BOOST_AUTO_TEST_CASE(test_inline_table_immutability)
{
{
std::istringstream stream(std::string(
"a = {b = 1}\n"
"a.c = 2\n"));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
}
{
std::istringstream stream(std::string(
"a = {b = {c = 1}}\n"
"a.b.d = 2\n"));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
}
}

View File

@@ -69,9 +69,6 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\" And when \\\"'s are in the along with # \\\"\"",
string(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
string("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_basic_string_value)
@@ -97,9 +94,6 @@ BOOST_AUTO_TEST_CASE(test_basic_string_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\" And when \\\"'s are in the along with # \\\"\"",
value(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
value("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_ml_basic_string)
@@ -110,18 +104,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
string("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
string("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
string("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
string("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_ml_basic_string_value)
@@ -132,19 +114,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
value("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
value("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
value("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -187,15 +156,6 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''That's still pointless', she said.'''",
string("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''This,' she said, 'is just a pointless statement.''''",
string("'This,' she said, 'is just a pointless statement.'", string_t::literal));
}
BOOST_AUTO_TEST_CASE(test_ml_literal_string_value)
@@ -206,15 +166,6 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''That's still pointless', she said.'''",
value("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''This,' she said, 'is just a pointless statement.''''",
value("'This,' she said, 'is just a pointless statement.'", string_t::literal));
}
BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence)

View File

@@ -111,44 +111,3 @@ BOOST_AUTO_TEST_CASE(test_literal_ml_string)
}
}
BOOST_AUTO_TEST_CASE(test_string_add_assign)
{
// string literal
{
toml::string str("foo");
str += "bar";
BOOST_TEST(str.str == "foobar");
}
// std::string
{
toml::string str("foo");
std::string str2("bar");
str += str2;
BOOST_TEST(str.str == "foobar");
}
// toml::string
{
toml::string str("foo");
toml::string str2("bar");
str += str2;
BOOST_TEST(str.str == "foobar");
}
#if __cplusplus >= 201703L
// std::string_view
{
toml::string str("foo");
str += std::string_view("bar");
BOOST_TEST(str == "foobar");
}
#endif
// std::string += toml::string
{
std::string str("foo");
toml::string str2("bar");
str += str2;
BOOST_TEST(str == "foobar");
}
}

View File

@@ -905,115 +905,3 @@ BOOST_AUTO_TEST_CASE(test_value_empty)
BOOST_CHECK_THROW(v1.as_array(), toml::type_error);
BOOST_CHECK_THROW(v1.as_table(), toml::type_error);
}
BOOST_AUTO_TEST_CASE(test_value_at)
{
{
toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}};
BOOST_TEST(v1.at("foo").as_integer() == 42);
BOOST_TEST(v1.at("bar").as_floating() == 3.14);
BOOST_TEST(v1.at("baz").as_string() == "qux");
BOOST_CHECK_THROW(v1.at(0), toml::type_error);
BOOST_CHECK_THROW(v1.at("quux"), std::out_of_range);
}
{
toml::value v1{1,2,3,4,5};
BOOST_TEST(v1.at(0).as_integer() == 1);
BOOST_TEST(v1.at(1).as_integer() == 2);
BOOST_TEST(v1.at(2).as_integer() == 3);
BOOST_TEST(v1.at(3).as_integer() == 4);
BOOST_TEST(v1.at(4).as_integer() == 5);
BOOST_CHECK_THROW(v1.at("foo"), toml::type_error);
BOOST_CHECK_THROW(v1.at(5), std::out_of_range);
}
}
BOOST_AUTO_TEST_CASE(test_value_bracket)
{
{
toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}};
BOOST_TEST(v1["foo"].as_integer() == 42);
BOOST_TEST(v1["bar"].as_floating() == 3.14);
BOOST_TEST(v1["baz"].as_string() == "qux");
v1["qux"] = 54;
BOOST_TEST(v1["qux"].as_integer() == 54);
}
{
toml::value v1;
v1["foo"] = 42;
BOOST_TEST(v1.is_table());
BOOST_TEST(v1["foo"].as_integer() == 42);
}
{
toml::value v1{1,2,3,4,5};
BOOST_TEST(v1[0].as_integer() == 1);
BOOST_TEST(v1[1].as_integer() == 2);
BOOST_TEST(v1[2].as_integer() == 3);
BOOST_TEST(v1[3].as_integer() == 4);
BOOST_TEST(v1[4].as_integer() == 5);
BOOST_CHECK_THROW(v1["foo"], toml::type_error);
}
}
BOOST_AUTO_TEST_CASE(test_value_map_methods)
{
{
toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}};
BOOST_TEST(v1.count("foo") == 1u);
BOOST_TEST(v1.count("bar") == 1u);
BOOST_TEST(v1.count("baz") == 1u);
BOOST_TEST(v1.count("qux") == 0u);
BOOST_TEST( v1.contains("foo"));
BOOST_TEST( v1.contains("bar"));
BOOST_TEST( v1.contains("baz"));
BOOST_TEST(!v1.contains("qux"));
BOOST_TEST(v1.size() == 3);
v1["qux"] = 54;
BOOST_TEST(v1.count("qux") == 1u);
BOOST_TEST(v1.contains("qux"));
BOOST_TEST(v1.size() == 4);
}
{
toml::value v1(42);
BOOST_CHECK_THROW(v1.size() , toml::type_error);
BOOST_CHECK_THROW(v1.count("k") , toml::type_error);
BOOST_CHECK_THROW(v1.contains("k"), toml::type_error);
}
}
BOOST_AUTO_TEST_CASE(test_value_vector_methods)
{
{
toml::value v1{1, 2, 3, 4, 5};
BOOST_TEST(v1.size() == 5);
v1.push_back(6);
BOOST_TEST(v1.size() == 6);
v1.emplace_back(6);
BOOST_TEST(v1.size() == 7);
}
{
toml::value v1(42);
BOOST_CHECK_THROW(v1.size(), toml::type_error);
BOOST_CHECK_THROW(v1.push_back(1), toml::type_error);
BOOST_CHECK_THROW(v1.emplace_back(1), toml::type_error);
}
}

View File

@@ -37,5 +37,6 @@
#include "toml/literal.hpp"
#include "toml/serializer.hpp"
#include "toml/get.hpp"
#include "toml/find.hpp"
#endif// TOML_FOR_MODERN_CPP

View File

@@ -1,64 +0,0 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include <ostream>
#include <cstdint>
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true
#else
#define TOML11_ERROR_MESSAGE_COLORIZED false
#endif
namespace toml
{
// put ANSI escape sequence to ostream
namespace color_ansi
{
namespace detail
{
inline int colorize_index()
{
static const int index = std::ios_base::xalloc();
return index;
}
} // detail
inline std::ostream& colorize(std::ostream& os)
{
// by default, it is zero.
os.iword(detail::colorize_index()) = 1;
return os;
}
inline std::ostream& nocolorize(std::ostream& os)
{
os.iword(detail::colorize_index()) = 0;
return os;
}
inline std::ostream& reset (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;}
inline std::ostream& bold (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;}
inline std::ostream& grey (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;}
inline std::ostream& red (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;}
inline std::ostream& green (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;}
inline std::ostream& yellow (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;}
inline std::ostream& blue (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;}
inline std::ostream& magenta(std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;}
inline std::ostream& cyan (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
inline std::ostream& white (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
} // color_ansi
// ANSI escape sequence is the only and default colorization method currently
namespace color = color_ansi;
} // toml
#endif// TOML11_COLOR_HPP

View File

@@ -45,7 +45,6 @@ inline std::string show_char(const char c)
buf.fill('\0');
const auto r = std::snprintf(
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
(void) r; // Unused variable warning
assert(r == static_cast<int>(buf.size()) - 1);
return std::string(buf.data());
}

View File

@@ -20,7 +20,7 @@ namespace toml
namespace detail
{
// TODO: find more sophisticated way to handle this
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
@@ -28,14 +28,7 @@ inline std::tm localtime_s(const std::time_t* src)
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#elif defined(_MSC_VER)
#elif _MSC_VER
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
@@ -43,26 +36,13 @@ inline std::tm localtime_s(const std::time_t* src)
if (result) { throw std::runtime_error("localtime_s failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_s(&dst, src);
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#else // fallback. not threadsafe
#else
inline std::tm localtime_s(const std::time_t* src)
{
const auto result = std::localtime(src);
if (!result) { throw std::runtime_error("localtime failed."); }
return *result;
}
inline std::tm gmtime_s(const std::time_t* src)
{
const auto result = std::gmtime(src);
if (!result) { throw std::runtime_error("gmtime failed."); }
return *result;
}
#endif
} // detail
@@ -172,9 +152,9 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
{
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month + 1) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day );
return os;
}
@@ -398,31 +378,10 @@ struct local_datetime
{
using internal_duration =
typename std::chrono::system_clock::time_point::duration;
// Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
// of local_date and local_time independently, the conversion fails if
// it is the day when DST begins or ends. Since local_date considers the
// time is 00:00 A.M. and local_time does not consider DST because it
// does not have any date information. We need to consider both date and
// time information at the same time to convert it correctly.
std::tm t;
t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
// std::mktime returns date as local time zone. no conversion needed
auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t));
auto dt = std::chrono::system_clock::time_point(this->date);
dt += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
std::chrono::nanoseconds(this->time));
return dt;
}
@@ -488,71 +447,40 @@ struct offset_datetime
: date(dt.date), time(dt.time), offset(o)
{}
explicit offset_datetime(const local_datetime& ld)
: date(ld.date), time(ld.time), offset(get_local_offset(nullptr))
// use the current local timezone offset
: date(ld.date), time(ld.time), offset(get_local_offset())
{}
explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset(0, 0) // use gmtime
{
const auto timet = std::chrono::system_clock::to_time_t(tp);
const auto tm = detail::gmtime_s(&timet);
this->date = local_date(tm);
this->time = local_time(tm);
}
: offset_datetime(local_datetime(tp))
{}
explicit offset_datetime(const std::time_t& t)
: offset(0, 0) // use gmtime
{
const auto tm = detail::gmtime_s(&t);
this->date = local_date(tm);
this->time = local_time(tm);
}
: offset_datetime(local_datetime(t))
{}
explicit offset_datetime(const std::tm& t)
: offset(0, 0) // assume gmtime
{
this->date = local_date(t);
this->time = local_time(t);
}
: offset_datetime(local_datetime(t))
{}
operator std::chrono::system_clock::time_point() const
{
// get date-time
using internal_duration =
typename std::chrono::system_clock::time_point::duration;
std::chrono::system_clock::time_point tp =
std::chrono::system_clock::time_point(this->date) +
std::chrono::duration_cast<internal_duration>(
std::chrono::nanoseconds(this->time));
// first, convert it to local date-time information in the same way as
// local_datetime does. later we will use time_t to adjust time offset.
std::tm t;
t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
const std::time_t tp_loc = std::mktime(std::addressof(t));
auto tp = std::chrono::system_clock::from_time_t(tp_loc);
tp += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
// Since mktime uses local time zone, it should be corrected.
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
// to add `+09:00` to `03:00:00Z`.
// Here, it uses the time_t converted from date-time info to handle
// daylight saving time.
const auto ofs = get_local_offset(std::addressof(tp_loc));
// get date-time in UTC. let's say we are in +09:00 (JPN).
// writing 12:00:00 in +09:00 means 03:00:00Z. to represent
// 12:00:00Z, first we need to add +09:00.
const auto ofs = get_local_offset();
tp += std::chrono::hours (ofs.hour);
tp += std::chrono::minutes(ofs.minute);
// We got `12:00:00Z` by correcting local timezone applied by mktime.
// Then we will apply the offset. Let's say `12:00:00-08:00` is given.
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
// So we need to subtract the offset.
// here, tp represents 12:00:00 in UTC but we have offset information.
// we need to subtract it. For example, let's say the input is
// 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above
// conversion. But the actual time we need to return is 20:00:00Z
// because of -08:00.
tp -= std::chrono::minutes(this->offset);
return tp;
}
@@ -572,10 +500,11 @@ struct offset_datetime
private:
static time_offset get_local_offset(const std::time_t* tp)
static time_offset get_local_offset()
{
// get local timezone with the same date-time information as mktime
const auto t = detail::localtime_s(tp);
// get current timezone
const auto tmp1 = std::time(nullptr);
const auto t = detail::localtime_s(&tmp1);
std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0

View File

@@ -2,7 +2,6 @@
// Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include "source_location.hpp"
#include <stdexcept>
#include <string>
@@ -12,21 +11,15 @@ namespace toml
struct exception : public std::exception
{
public:
explicit exception(const source_location& loc): loc_(loc) {}
virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";}
virtual source_location const& location() const noexcept {return loc_;}
protected:
source_location loc_;
};
struct syntax_error : public toml::exception
{
public:
explicit syntax_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
explicit syntax_error(const std::string& what_arg) : what_(what_arg){}
explicit syntax_error(const char* what_arg) : what_(what_arg){}
virtual ~syntax_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
@@ -37,9 +30,8 @@ struct syntax_error : public toml::exception
struct type_error : public toml::exception
{
public:
explicit type_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
explicit type_error(const std::string& what_arg) : what_(what_arg){}
explicit type_error(const char* what_arg) : what_(what_arg){}
virtual ~type_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
@@ -50,12 +42,10 @@ struct type_error : public toml::exception
struct internal_error : public toml::exception
{
public:
explicit internal_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
explicit internal_error(const std::string& what_arg) : what_(what_arg){}
explicit internal_error(const char* what_arg) : what_(what_arg){}
virtual ~internal_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
protected:
std::string what_;
};

786
toml/find.hpp Normal file
View File

@@ -0,0 +1,786 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_FIND_HPP
#define TOML11_FIND_HPP
#include "get.hpp"
#include <numeric>
namespace toml
{
// ----------------------------------------------------------------------------
// these overloads do not require to set T. and returns value itself.
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
{
const auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
{
auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>&& find(basic_value<C, M, V>&& v, const key& ky)
{
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return std::move(tab.at(ky));
}
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& v, const key& ky)
{
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const key& ky)
{
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const key& ky)
{
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(std::move(tab.at(ky)));
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys)
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, const basic_value<C, M, V>&>
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, basic_value<C, M, V>&>
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, basic_value<C, M, V>&&>
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, decltype(get<T>(std::declval<const basic_value<C, M, V>&>()))>
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, decltype(get<T>(std::declval<basic_value<C, M, V>&>()))>
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, decltype(get<T>(std::declval<basic_value<C, M, V>&&>()))>
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
}
// ===========================================================================
// find_or(value, key, fallback)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find_or(const basic_value<C, M, V>& v, const key& ky,
const basic_value<C, M, V>& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, basic_value<C, M, V>& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return tab[ky];
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, basic_value<C, M, V>&& opt)
{
if(!v.is_table()) {return std::move(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::move(opt);}
return std::move(tab[ky]);
}
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&&
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::string>
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::string(opt);}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return std::string(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
// T is not an exact toml type
detail::negation<detail::is_exact_toml_type<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
basic_value<C, M, V>>>,
// T is not std::string
detail::negation<std::is_same<std::string,
typename std::remove_cv<typename std::remove_reference<T>::type>::type>>,
// T is not a string literal
detail::negation<detail::is_string_literal<
typename std::remove_reference<T>::type>>
>::value, typename std::remove_cv<typename std::remove_reference<T>::type>::type>
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ============================================================================
// expect
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string> expect(const basic_value<C, M, V>& v) noexcept
{
try
{
return ok(get<T>(v));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string>
expect(const basic_value<C, M, V>& v, const toml::key& k) noexcept
{
try
{
return ok(find<T>(v, k));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T, typename Table>
detail::enable_if_t<detail::conjunction<
detail::is_map<Table>, detail::is_basic_value<typename Table::mapped_type>
>::value, result<T, std::string>>
expect(const Table& t, const toml::key& k,
std::string tablename = "unknown table") noexcept
{
try
{
return ok(find<T>(t, k, std::move(tablename)));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
// ===========================================================================
// find_fuzzy
// ---------------------------------------------------------------------------
// default fuzzy matcher; levenstein distance (all cost is 1)
struct levenstein_matcher
{
levenstein_matcher(): tolerance(1) {}
levenstein_matcher(const std::uint32_t tol): tolerance(tol) {}
~levenstein_matcher() = default;
levenstein_matcher(levenstein_matcher const&) = default;
levenstein_matcher(levenstein_matcher &&) = default;
levenstein_matcher& operator=(levenstein_matcher const&) = default;
levenstein_matcher& operator=(levenstein_matcher &&) = default;
template<typename charT, typename traitsT, typename Alloc1, typename Alloc2>
bool operator()(const std::basic_string<charT, traitsT, Alloc1>& lhs,
const std::basic_string<charT, traitsT, Alloc2>& rhs) const
{
return this->distance(lhs, rhs) <= this->tolerance;
}
template<typename charT, typename traitsT, typename Alloc1, typename Alloc2>
std::uint32_t distance(
const std::basic_string<charT, traitsT, Alloc1>& lhs,
const std::basic_string<charT, traitsT, Alloc2>& rhs) const
{
// force `lhs.size() <= rhs.size()`
if(lhs.size() > rhs.size()) {return this->distance(rhs, lhs);}
std::vector<std::uint32_t> matrix(lhs.size() + 1u);
std::iota(matrix.begin(), matrix.end(), 0);
for(const charT r : rhs)
{
std::uint32_t prev_diag = matrix.front();
matrix.front() += 1;
for(std::size_t i=0; i<lhs.size(); ++i)
{
const charT l = lhs[i];
if(traitsT::eq(l, r))
{
std::swap(matrix[i+1], prev_diag);
}
else
{
const auto tmp = matrix[i+1];
matrix[i+1] = std::min(prev_diag, std::min(matrix[i], matrix[i+1])) + 1;
prev_diag = tmp;
}
}
}
return matrix.back();
}
private:
std::uint32_t tolerance;
};
// ---------------------------------------------------------------------------
// toml::find_fuzzy<T>(v, "tablename", FuzzyMatcher);
namespace detail
{
template<typename Iterator, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
Iterator find_unique(
Iterator iter, const Iterator end, const basic_value<C, M, V>& v,
const toml::key& k, const FuzzyMatcher& match)
{
Iterator found = end;
for(; iter != end; ++iter)
{
if(match(iter->first, k))
{
if(found != end)
{
throw std::out_of_range(detail::format_underline(
concat_to_string("[error] key \"", k, "\" not found."),
{
{std::addressof(detail::get_region(v)),"in this table"},
{std::addressof(detail::get_region(found->second)),
"did you mean this here?"},
{std::addressof(detail::get_region(iter->second)),
"or this?"}
}));
}
found = iter;
}
}
return found;
}
} // detail
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
auto find_fuzzy(const basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
-> decltype(find<T>(std::declval<const basic_value<C, M, V>&>(), ky))
{
try
{
return find<T>(v, ky);
}
catch(const std::out_of_range& oor)
{
const auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return get<T>(found->second);
}
throw;
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
auto find_fuzzy(basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
-> decltype(find<T>(std::declval<basic_value<C, M, V>&>(), ky))
{
try
{
return find<T>(v, ky);
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return get<T>(found->second);
}
throw;
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
auto find_fuzzy(basic_value<C, M, V>&& v_, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
-> decltype(find<T>(std::declval<basic_value<C, M, V>&&>(), ky))
{
basic_value<C, M, V> v = v_; // to re-use later, store it once
try
{
return std::move(find<T>(v, ky)); // pass lref, move later
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table(); // because v is used here
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return get<T>(std::move(found->second));
}
throw;
}
}
// ---------------------------------------------------------------------------
// no-template-argument case (by default, return toml::value).
// toml::find_fuzzy(v, "tablename", FuzzyMatcher);
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
basic_value<C, M, V> const&
find_fuzzy(const basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
{
try
{
return find(v, ky);
}
catch(const std::out_of_range& oor)
{
const auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return found->second;
}
throw;
}
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
basic_value<C, M, V>&
find_fuzzy(basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
{
try
{
return find(v, ky);
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return found->second;
}
throw;
}
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
basic_value<C, M, V>&&
find_fuzzy(basic_value<C, M, V>&& v_, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
{
basic_value<C, M, V> v = v_; // to re-use later, store it once
try
{
return std::move(find(v, ky));
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return std::move(found->second);
}
throw;
}
}
// ===========================================================================
// find(v, k, matcher)
//
// when matcher is passed, check a key that matches exists or not. if it exists,
// suggest that in the error message
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
basic_value<C, M, V> const&
find(const basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
const auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
basic_value<C, M, V>&
find(basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
basic_value<C, M, V>&&
find(basic_value<C, M, V>&& v, const key& ky, FuzzyMatcher match)
{
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return std::move(tab.at(ky));
}
// ----------------------------------------------------------------------------
// find<T>(value, key, fuzzy_matcher);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
detail::enable_if_t<
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))>
find(const basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this here?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
detail::enable_if_t<
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))>
find(basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this here?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
detail::enable_if_t<
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))>
find(basic_value<C, M, V>&& v, const key& ky, FuzzyMatcher match)
{
auto tab = v.as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this here?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(std::move(tab.at(ky)));
}
} // toml
#endif// TOML11_FIND_HPP

View File

@@ -31,10 +31,10 @@ get(const basic_value<C, M, V>& v)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>
detail::enable_if_t<detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> &&
get(basic_value<C, M, V>&& v)
{
return T(std::move(v).template cast<detail::type_to_enum<T, basic_value<C, M, V>>::value>());
return std::move(v).template cast<detail::type_to_enum<T, basic_value<C, M, V>>::value>();
}
// ============================================================================
@@ -58,10 +58,10 @@ get(const basic_value<C, M, V>& v)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, basic_value<C, M, V>>::value, T>
inline detail::enable_if_t<std::is_same<T, basic_value<C, M, V>>::value, T> &&
get(basic_value<C, M, V>&& v)
{
return basic_value<C, M, V>(std::move(v));
return std::move(v);
}
// ============================================================================
@@ -90,7 +90,7 @@ inline detail::enable_if_t<detail::conjunction<
>::value, T>
get(const basic_value<C, M, V>& v)
{
return static_cast<T>(v.as_integer());
return static_cast<T>(v.template cast<value_t::integer>());
}
// ============================================================================
@@ -105,7 +105,7 @@ inline detail::enable_if_t<detail::conjunction<
>::value, T>
get(const basic_value<C, M, V>& v)
{
return static_cast<T>(v.as_floating());
return static_cast<T>(v.template cast<value_t::floating>());
}
// ============================================================================
@@ -117,7 +117,7 @@ template<typename T, typename C,
inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
get(basic_value<C, M, V>& v)
{
return v.as_string().str;
return v.template cast<value_t::string>().str;
}
template<typename T, typename C,
@@ -125,15 +125,15 @@ template<typename T, typename C,
inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
get(const basic_value<C, M, V>& v)
{
return v.as_string().str;
return v.template cast<value_t::string>().str;
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
get(basic_value<C, M, V>&& v)
{
return std::string(std::move(v.as_string().str));
return std::move(v.template cast<value_t::string>().str);
}
// ============================================================================
@@ -145,7 +145,7 @@ template<typename T, typename C,
inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
get(const basic_value<C, M, V>& v)
{
return std::string_view(v.as_string().str);
return std::string_view(v.template cast<value_t::string>().str);
}
#endif
@@ -158,7 +158,7 @@ inline detail::enable_if_t<detail::is_chrono_duration<T>::value, T>
get(const basic_value<C, M, V>& v)
{
return std::chrono::duration_cast<T>(
std::chrono::nanoseconds(v.as_local_time()));
std::chrono::nanoseconds(v.template cast<value_t::local_time>()));
}
// ============================================================================
@@ -174,23 +174,26 @@ get(const basic_value<C, M, V>& v)
{
case value_t::local_date:
{
return std::chrono::system_clock::time_point(v.as_local_date());
return std::chrono::system_clock::time_point(
v.template cast<value_t::local_date>());
}
case value_t::local_datetime:
{
return std::chrono::system_clock::time_point(v.as_local_datetime());
return std::chrono::system_clock::time_point(
v.template cast<value_t::local_datetime>());
}
case value_t::offset_datetime:
{
return std::chrono::system_clock::time_point(v.as_offset_datetime());
return std::chrono::system_clock::time_point(
v.template cast<value_t::offset_datetime>());
}
default:
{
throw type_error(detail::format_underline("toml::value: "
throw type_error(detail::format_underline("[error] toml::value "
"bad_cast to std::chrono::system_clock::time_point", {
{std::addressof(detail::get_region(v)),
concat_to_string("the actual type is ", v.type())}
}), v.location());
}));
}
}
}
@@ -256,18 +259,9 @@ get(const basic_value<C, M, V>&);
// toml::from<T>::from_toml(v)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
std::size_t S = sizeof(::toml::from<T>)>
std::size_t S = sizeof(::toml::into<T>)>
T get(const basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
>::value, T>
get(const basic_value<C, M, V>&);
// ============================================================================
// array-like types; most likely STL container, like std::vector, etc.
@@ -282,7 +276,7 @@ detail::enable_if_t<detail::conjunction<
get(const basic_value<C, M, V>& v)
{
using value_type = typename T::value_type;
const auto& ar = v.as_array();
const auto& ar = v.template cast<value_t::array>();
T container;
container.resize(ar.size());
std::transform(ar.cbegin(), ar.cend(), container.begin(),
@@ -304,13 +298,13 @@ detail::enable_if_t<detail::conjunction<
get(const basic_value<C, M, V>& v)
{
using value_type = typename T::value_type;
const auto& ar = v.as_array();
const auto& ar = v.template cast<value_t::array>();
T container;
if(ar.size() != container.size())
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified container size is ", container.size(),
"[erorr] toml::get specified container size is ", container.size(),
" but there are ", ar.size(), " elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
@@ -331,11 +325,11 @@ get(const basic_value<C, M, V>& v)
using first_type = typename T::first_type;
using second_type = typename T::second_type;
const auto& ar = v.as_array();
const auto& ar = v.template cast<value_t::array>();
if(ar.size() != 2)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::pair but there are ", ar.size(),
"[erorr] toml::get specified std::pair but there are ", ar.size(),
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
@@ -362,12 +356,12 @@ template<typename T, typename C,
detail::enable_if_t<detail::is_std_tuple<T>::value, T>
get(const basic_value<C, M, V>& v)
{
const auto& ar = v.as_array();
const auto& ar = v.template cast<value_t::array>();
if(ar.size() != std::tuple_size<T>::value)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::tuple with ",
std::tuple_size<T>::value, " elements, but there are ", ar.size(),
"[erorr] toml::get specified std::tuple with ",
std::tuple_size<T>::value, "elements, but there are ", ar.size(),
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
@@ -394,7 +388,7 @@ get(const basic_value<C, M, V>& v)
"toml::get only supports map type of which key_type is "
"convertible from std::string.");
T map;
for(const auto& kv : v.as_table())
for(const auto& kv : v.template cast<value_t::table>())
{
map[key_type(kv.first)] = ::toml::get<mapped_type>(kv.second);
}
@@ -426,310 +420,6 @@ T get(const basic_value<C, M, V>& v)
return ::toml::from<T>::from_toml(v);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
>::value, T>
get(const basic_value<C, M, V>& v)
{
return T(v);
}
// ============================================================================
// find
// ----------------------------------------------------------------------------
// these overloads do not require to set T. and returns value itself.
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
{
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
{
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> find(basic_value<C, M, V>&& v, const key& ky)
{
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return basic_value<C, M, V>(std::move(tab.at(ky)));
}
// ----------------------------------------------------------------------------
// find(value, idx)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find(const basic_value<C, M, V>& v, const std::size_t idx)
{
const auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
}
return ary.at(idx);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
}
return ary.at(idx);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> find(basic_value<C, M, V>&& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
}
return basic_value<C, M, V>(std::move(ary.at(idx)));
}
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& v, const key& ky)
{
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const key& ky)
{
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const key& ky)
{
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(std::move(tab.at(ky)));
}
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& v, const std::size_t idx)
{
const auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
}
return ::toml::get<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
}
return ::toml::get<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const std::size_t idx)
{
typename basic_value<C, M, V>::array_type ary = std::move(v).as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
}
return ::toml::get<T>(std::move(ary.at(idx)));
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys)
namespace detail
{
// It suppresses warnings by -Wsign-conversion. Let's say we have the following
// code.
// ```cpp
// const auto x = toml::find<std::string>(data, "array", 0);
// ```
// Here, the type of literal number `0` is `int`. `int` is a signed integer.
// `toml::find` takes `std::size_t` as an index. So it causes implicit sign
// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0`
// suppresses the warning, but it makes user code messy.
// To suppress this warning, we need to be aware of type conversion caused
// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of
// keys can be any combination of {string-like, size_t-like}. Of course we can't
// write down all the combinations. Thus we need to use some function that
// recognize the type of argument and cast it into `std::string` or
// `std::size_t` depending on the context.
// `key_cast` does the job. It has 2 overloads. One is invoked when the
// argument type is an integer and cast the argument into `std::size_t`. The
// other is invoked when the argument type is not an integer, possibly one of
// std::string, const char[N] or const char*, and construct std::string from
// the argument.
// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to
// `toml::find(v, k)` to suppress -Wsign-conversion.
template<typename T>
enable_if_t<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>::value, std::size_t>
key_cast(T&& v) noexcept
{
return std::size_t(v);
}
template<typename T>
enable_if_t<negation<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>>::value, std::string>
key_cast(T&& v) noexcept
{
return std::string(std::forward<T>(v));
}
} // detail
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
const basic_value<C, M, V>&
find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V>&
find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V>
find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(std::move(v), std::forward<Key1>(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<const basic_value<C, M, V>&>()))
find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(std::move(v), detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
// ============================================================================
// get_or(value, fallback)
@@ -766,7 +456,8 @@ get_or(const basic_value<C, M, V>& v, const T& opt)
{
try
{
return get<detail::remove_cvref_t<T>>(v);
return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
}
catch(...)
{
@@ -781,7 +472,8 @@ get_or(basic_value<C, M, V>& v, T& opt)
{
try
{
return get<detail::remove_cvref_t<T>>(v);
return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
}
catch(...)
{
@@ -790,17 +482,18 @@ get_or(basic_value<C, M, V>& v, T& opt)
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::is_exact_toml_type<detail::remove_cvref_t<T>,
basic_value<C, M, V>>::value, detail::remove_cvref_t<T>>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&&
get_or(basic_value<C, M, V>&& v, T&& opt)
{
try
{
return get<detail::remove_cvref_t<T>>(std::move(v));
return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
}
catch(...)
{
return detail::remove_cvref_t<T>(std::forward<T>(opt));
return opt;
}
}
@@ -809,13 +502,14 @@ get_or(basic_value<C, M, V>&& v, T&& opt)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<detail::remove_cvref_t<T>, std::string>::value,
std::string> const&
get_or(const basic_value<C, M, V>& v, const T& opt)
detail::enable_if_t<std::is_same<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
std::string>::value, std::string> const&
get_or(const basic_value<C, M, V>& v, T&& opt)
{
try
{
return v.as_string().str;
return v.template cast<value_t::string>().str;
}
catch(...)
{
@@ -829,7 +523,7 @@ get_or(basic_value<C, M, V>& v, T& opt)
{
try
{
return v.as_string().str;
return v.template cast<value_t::string>().str;
}
catch(...)
{
@@ -838,17 +532,18 @@ get_or(basic_value<C, M, V>& v, T& opt)
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
std::is_same<detail::remove_cvref_t<T>, std::string>::value, std::string>
detail::enable_if_t<std::is_same<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
std::string>::value, std::string>
get_or(basic_value<C, M, V>&& v, T&& opt)
{
try
{
return std::move(v.as_string().str);
return std::move(v.template cast<value_t::string>().str);
}
catch(...)
{
return std::string(std::forward<T>(opt));
return std::forward<T>(opt);
}
}
@@ -863,11 +558,11 @@ get_or(const basic_value<C, M, V>& v, T&& opt)
{
try
{
return std::move(v.as_string().str);
return std::move(v.template cast<value_t::string>().str);
}
catch(...)
{
return std::string(std::forward<T>(opt));
return std::string(opt);
}
}
@@ -877,199 +572,24 @@ get_or(const basic_value<C, M, V>& v, T&& opt)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_exact_toml_type<detail::remove_cvref_t<T>,
detail::negation<detail::is_exact_toml_type<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
basic_value<C, M, V>>>,
detail::negation<std::is_same<std::string, detail::remove_cvref_t<T>>>,
detail::negation<std::is_same<std::string,
typename std::remove_cv<typename std::remove_reference<T>::type>::type>>,
detail::negation<detail::is_string_literal<
typename std::remove_reference<T>::type>>
>::value, detail::remove_cvref_t<T>>
>::value, typename std::remove_reference<T>::type>
get_or(const basic_value<C, M, V>& v, T&& opt)
{
try
{
return get<detail::remove_cvref_t<T>>(v);
return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
}
catch(...)
{
return detail::remove_cvref_t<T>(std::forward<T>(opt));
}
}
// ===========================================================================
// find_or(value, key, fallback)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find_or(const basic_value<C, M, V>& v, const key& ky,
const basic_value<C, M, V>& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, basic_value<C, M, V>& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, basic_value<C, M, V>&& opt)
{
if(!v.is_table()) {return opt;}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return opt;}
return basic_value<C, M, V>(std::move(tab.at(ky)));
}
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value,
detail::remove_cvref_t<T>>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::string>
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::string(opt);}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return std::string(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
// T is not an exact toml type
detail::negation<detail::is_exact_toml_type<
detail::remove_cvref_t<T>, basic_value<C, M, V>>>,
// T is not std::string
detail::negation<std::is_same<std::string, detail::remove_cvref_t<T>>>,
// T is not a string literal
detail::negation<detail::is_string_literal<
typename std::remove_reference<T>::type>>
>::value, detail::remove_cvref_t<T>>
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ============================================================================
// expect
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string> expect(const basic_value<C, M, V>& v) noexcept
{
try
{
return ok(get<T>(v));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string>
expect(const basic_value<C, M, V>& v, const toml::key& k) noexcept
{
try
{
return ok(find<T>(v, k));
}
catch(const std::exception& e)
{
return err(e.what());
return std::forward<T>(opt);
}
}

View File

@@ -63,22 +63,12 @@ using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>;
using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>;
using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>;
using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>;
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>, lex_dec_int>;
using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
sequence<lex_underscore, lex_digit>>, unlimited>>;
sequence<lex_underscore, lex_digit>>, unlimited>>;
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// use toml-lang/toml HEAD
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
maybe<lex_sign>, lex_zero_prefixable_int>;
#else
// strictly TOML v0.5.0
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
lex_dec_int>;
#endif
using lex_float = either<lex_special_float,
sequence<lex_dec_int, either<lex_exponent_part,
sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>;
@@ -125,17 +115,10 @@ using lex_local_time = lex_partial_time;
// ===========================================================================
using lex_quotation_mark = character<'"'>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab)
in_range<0x0a, 0x1F>, // is allowed
character<0x22>, character<0x5C>,
character<0x7F>>>;
#else
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x1F>,
character<0x22>, character<0x5C>,
character<0x7F>>>;
#endif
using lex_escape = character<'\\'>;
using lex_escape_unicode_short = sequence<character<'u'>,
repeat<lex_hex_dig, exactly<4>>>;
@@ -154,60 +137,12 @@ using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>,
lex_quotation_mark>;
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
// are allowed to be used.
// After this, the following strings are *explicitly* allowed.
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
// - One or two `"`s can appear just before or after the delimiter.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// str7 = """"This," she said, "is just a pointless statement.""""
// ```
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
// the above example. It is difficult to recognize `"` at the end of string body
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
// invalid `"`. Like this:
// ```console
// what(): [error] toml::parse_table: invalid line format
// --> hoge.toml
// |
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
//
// Note: This feature is a "clarification". Therefore this change is considered
// as a spec that has been defined since the time when the multi-line
// basic string was introduced. Although it is a post-v0.5.0 changes,
// this change will be activated regardless of the flag,
// `TOML11_USE_UNRELEASED_TOML_FEATURES`.
//
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab
character<0x5C>, // backslash
character<0x7F>, // DEL
lex_ml_basic_string_delim>>;
#else // TOML v0.5.0
using lex_ml_basic_unescaped = exclude<either<in_range<0x00,0x1F>,
character<0x5C>,
character<0x7F>,
lex_ml_basic_string_delim>>;
#endif
using lex_ml_basic_escaped_newline = sequence<
lex_escape, maybe<lex_ws>, lex_newline,
@@ -217,9 +152,9 @@ using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>,
unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
using lex_ml_basic_string = sequence<lex_ml_basic_string_delim,
lex_ml_basic_body,
lex_ml_basic_string_close>;
lex_ml_basic_string_delim>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>;
@@ -228,13 +163,7 @@ using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>,
lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>,
@@ -242,9 +171,9 @@ using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
using lex_ml_literal_string = sequence<lex_ml_literal_string_delim,
lex_ml_literal_body,
lex_ml_literal_string_close>;
lex_ml_literal_string_delim>;
using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>;

View File

@@ -76,8 +76,7 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
}
else // none of them.
{
throw ::toml::syntax_error(data.unwrap_err(),
source_location(std::addressof(loc)));
throw ::toml::syntax_error(data.unwrap_err());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
// Distributed under the MIT License.
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include "exception.hpp"
#include <memory>
#include <vector>
#include <algorithm>
@@ -9,7 +10,6 @@
#include <iterator>
#include <iomanip>
#include <cassert>
#include "color.hpp"
namespace toml
{
@@ -41,7 +41,6 @@ struct region_base
region_base& operator=(region_base&& ) = default;
virtual bool is_ok() const noexcept {return false;}
virtual char front() const noexcept {return '\0';}
virtual std::string str() const {return std::string("unknown region");}
virtual std::string name() const {return std::string("unknown file");}
@@ -90,7 +89,6 @@ struct location final : public region_base
~location() = default;
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;}
@@ -233,16 +231,16 @@ struct region final : public region_base
region& operator+=(const region& other)
{
// different regions cannot be concatenated
assert(this->begin() == other.begin() && this->end() == other.end() &&
this->last_ == other.first_);
if(this->begin() != other.begin() || this->end() != other.end() ||
this->last_ != other.first_)
{
throw internal_error("invalid region concatenation");
}
this->last_ = other.last_;
return *this;
}
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *first_;}
std::string str() const override {return make_string(first_, last_);}
std::string line() const override
@@ -422,8 +420,7 @@ struct region final : public region_base
// to show a better error message.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<region_base const*, std::string>>& reg_com,
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
const std::vector<std::string>& helps = {})
{
assert(!reg_com.empty());
@@ -437,27 +434,7 @@ inline std::string format_underline(const std::string& message,
)->first->line_num().size());
std::ostringstream retval;
if(colorize)
{
retval << color::colorize; // turn on ANSI color
}
// XXX
// Here, before `colorize` support, it does not output `[error]` prefix
// automatically. So some user may output it manually and this change may
// duplicate the prefix. To avoid it, check the first 7 characters and
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval << color::bold << color::red << "[error]" << color::reset
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
<< color::bold << message << color::reset << '\n';
}
retval << message << '\n';
for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter)
{
@@ -465,42 +442,34 @@ inline std::string format_underline(const std::string& message,
if(iter != reg_com.begin() &&
std::prev(iter)->first->name() == iter->first->name())
{
retval << color::bold << color::blue << "\n ...\n" << color::reset;
retval << "\n ...\n";
}
else // if filename differs, print " --> filename.toml"
{
if(iter != reg_com.begin()) {retval << '\n';}
retval << color::bold << color::blue << " --> " << color::reset
<< iter->first->name() << '\n';
// add one almost-empty line for readability
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
<< color::bold << color::blue << " | " << color::reset << '\n';
retval << " --> " << iter->first->name() << '\n';
}
const region_base* const reg = iter->first;
const std::string& comment = iter->second;
retval << ' ' << color::bold << color::blue << std::setw(line_num_width)
<< std::right << reg->line_num() << " | " << color::reset
<< reg->line() << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
<< color::bold << color::blue << " | " << color::reset
<< make_string(reg->before(), ' ');
retval << ' ' << std::setw(line_num_width) << reg->line_num();
retval << " | " << reg->line() << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << " | " << make_string(reg->before(), ' ');
if(reg->size() == 1)
{
// invalid
// ^------
retval << color::bold << color::red
<< '^' << make_string(reg->after(), '-') << color::reset;
retval << '^';
retval << make_string(reg->after(), '-');
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = std::min(reg->size(), reg->line().size());
retval << color::bold << color::red
<< make_string(underline_len, '~') << color::reset;
retval << make_string(underline_len, '~');
}
retval << ' ';
retval << comment;
@@ -510,10 +479,10 @@ inline std::string format_underline(const std::string& message,
{
retval << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << color::bold << color::blue << " | " << color::reset;
for(const auto& help : helps)
retval << " | ";
for(const auto help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << "\nHint: ";
retval << help;
}
}

View File

@@ -118,19 +118,8 @@ struct serializer
{
return token; // there is no exponent part. just return it.
}
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// Although currently it is not released yet as a tagged version,
// TOML will allow zero-prefix in an exponent part, such as `1.234e+01`.
// ```toml
// num1 = 1.234e+1 # OK in TOML v0.5.0
// num2 = 1.234e+01 # error in TOML v0.5.0 but will be allowed soon
// ```
// To avoid `e+01`, the following `else` section removes the zero
// prefixes in the exponent part.
// If the feature is activated, it can be skipped.
return token;
#else
// zero-prefix in an exponent is NOT allowed in TOML v0.5.0.
// zero-prefix in an exponent is NOT allowed in TOML.
// remove it if it exists.
bool sign_exists = false;
std::size_t zero_prefix = 0;
@@ -148,29 +137,17 @@ struct serializer
zero_prefix);
}
return token;
#endif
}
std::string operator()(const string_type& s) const
{
if(s.kind == string_t::basic)
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend())
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
{
// if linefeed or double-quote is contained,
// make it multiline basic string.
const auto escaped = this->escape_ml_basic_string(s.str);
std::string open("\"\"\"");
std::string close("\"\"\"");
if(escaped.find('\n') != std::string::npos ||
this->width_ < escaped.size() + 6)
{
// if the string body contains newline or is enough long,
// add newlines after and before delimiters.
open += "\n";
close = std::string("\\\n") + close;
}
return open + escaped + close;
// if linefeed is contained, make it multiline-string.
const std::string open("\"\"\"\n");
const std::string close("\\\n\"\"\"");
return open + this->escape_ml_basic_string(s.str) + close;
}
// no linefeed. try to make it oneline-string.
@@ -211,11 +188,7 @@ struct serializer
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
std::string open("'''");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string open("'''\n");
const std::string close("'''");
return open + s.str + close;
}
@@ -510,9 +483,7 @@ struct serializer
switch(*i)
{
case '\\': {retval += "\\\\"; break;}
// One or two consecutive "s are allowed.
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
@@ -533,23 +504,6 @@ struct serializer
default: {retval += *i; break;}
}
}
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
// 3 consecutive `"`s are considered as a closing delimiter.
// We need to check if there are 3 or more consecutive `"`s and insert
// backslash to break them down into several short `"`s like the `str6`
// in the following example.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// # str5 = """Here are three quotation marks: """.""" # INVALID
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// ```
auto found_3_quotes = retval.find("\"\"\"");
while(found_3_quotes != std::string::npos)
{
retval.replace(found_3_quotes, 3, "\"\"\\\"");
found_3_quotes = retval.find("\"\"\"");
}
return retval;
}
@@ -619,7 +573,7 @@ struct serializer
// print non-table stuff first. because after printing [foo.bar], the
// remaining non-table values will be assigned into [foo.bar], not [foo]
for(const auto& kv : v)
for(const auto kv : v)
{
if(kv.second.is_table() || is_array_of_tables(kv.second))
{

View File

@@ -48,11 +48,7 @@ struct source_location
{
if(reg)
{
if(reg->line_num() != detail::region_base().line_num())
{
line_num_ = static_cast<std::uint_least32_t>(
std::stoul(reg->line_num()));
}
line_num_ = static_cast<std::uint_least32_t>(std::stoul(reg->line_num()));
column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
region_size_ = static_cast<std::uint_least32_t>(reg->size());
file_name_ = reg->name();

View File

@@ -16,9 +16,10 @@ struct storage
{
using value_type = T;
explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
~storage() = default;
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
storage& operator=(const storage& rhs)
{

View File

@@ -2,7 +2,6 @@
// Distributed under the MIT License.
#ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP
#include <algorithm>
#include <string>
#include <cstdint>
#if __cplusplus >= 201703L
@@ -46,11 +45,6 @@ struct string
operator std::string const& () const& noexcept {return str;}
operator std::string&& () && noexcept {return std::move(str);}
string& operator+=(const char* rhs) {str += rhs; return *this;}
string& operator+=(const char rhs) {str += rhs; return *this;}
string& operator+=(const std::string& rhs) {str += rhs; return *this;}
string& operator+=(const string& rhs) {str += rhs.str; return *this;}
#if __cplusplus >= 201703L
explicit string(std::string_view s): kind(string_t::basic), str(s){}
string(std::string_view s, string_t k): kind(k), str(s){}
@@ -60,8 +54,6 @@ struct string
explicit operator std::string_view() const noexcept
{return std::string_view(str);}
string& operator+=(const std::string_view& rhs) {str += rhs; return *this;}
#endif
string_t kind;

View File

@@ -105,20 +105,12 @@ struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype
#undef decltype(...)
#endif
// ---------------------------------------------------------------------------
// C++17 and/or/not
#if __cplusplus >= 201703L
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts>
@@ -136,8 +128,6 @@ struct disjunction<T, Ts...> :
template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
#endif
// ---------------------------------------------------------------------------
// type checkers
@@ -192,13 +182,6 @@ struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// ---------------------------------------------------------------------------
// C++14 index_sequence
#if __cplusplus >= 201402L
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{};
@@ -222,22 +205,11 @@ struct index_sequence_maker<0>
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // __cplusplus >= 2014
// ---------------------------------------------------------------------------
// C++14 enable_if_t
#if __cplusplus >= 201402L
using std::enable_if_t;
#else
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
#endif // __cplusplus >= 2014
// ---------------------------------------------------------------------------
// return_type_of_t
@@ -268,19 +240,6 @@ disjunction<
>
>{};
// ---------------------------------------------------------------------------
// C++20 remove_cvref_t
template<typename T>
struct remove_cvref
{
using type = typename std::remove_cv<
typename std::remove_reference<T>::type>::type;
};
template<typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
}// detail
}//toml
#endif // TOML_TRAITS

View File

@@ -20,20 +20,12 @@
namespace toml
{
#if __cplusplus >= 201402L
using std::make_unique;
#else
template<typename T, typename ... Ts>
inline std::unique_ptr<T> make_unique(Ts&& ... args)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
}
#endif // __cplusplus >= 2014
namespace detail
{
@@ -84,10 +76,10 @@ std::string concat_to_string(Ts&& ... args)
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
}
template<typename T>
T from_string(const std::string& str, T opt)
template<typename T, typename U>
T from_string(const std::string& str, U&& opt)
{
T v(opt);
T v(static_cast<T>(std::forward<U>(opt)));
std::istringstream iss(str);
iss >> v;
return v;

View File

@@ -32,10 +32,10 @@ template<value_t Expected,
throw_bad_cast(value_t actual, const ::toml::basic_value<C, T, A>& v)
{
throw type_error(detail::format_underline(concat_to_string(
"toml::value: bad_cast to ", Expected), {
"[error] toml::value bad_cast to ", Expected), {
{std::addressof(get_region(v)),
concat_to_string("the actual type is ", actual)}
}), v.location());
}));
}
// switch by `value_t` and call the corresponding `value::as_xxx()`. {{{
@@ -529,6 +529,14 @@ class basic_value
assigner(this->boolean_, b);
return *this;
}
template<typename Container>
basic_value(boolean b, detail::region<Container> reg)
: type_(value_t::boolean),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->boolean_, b);
}
basic_value(boolean b, std::vector<std::string> comments)
: type_(value_t::boolean),
region_info_(std::make_shared<region_base>(region_base{})),
@@ -549,6 +557,19 @@ class basic_value
assigner(this->integer_, static_cast<integer>(i));
}
template<typename T, typename Container, typename std::enable_if<
detail::conjunction<
std::is_integral<T>,
detail::negation<std::is_same<T, boolean>>
>::value, std::nullptr_t>::type = nullptr>
basic_value(T i, detail::region<Container> reg)
: type_(value_t::integer),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->integer_, static_cast<integer>(i));
}
template<typename T, typename std::enable_if<detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>>::value,
std::nullptr_t>::type = nullptr>
@@ -583,6 +604,15 @@ class basic_value
assigner(this->floating_, static_cast<floating>(f));
}
template<typename T, typename Container, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, detail::region<Container> reg)
: type_(value_t::floating),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->floating_, static_cast<floating>(f));
}
template<typename T, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
@@ -613,6 +643,14 @@ class basic_value
{
assigner(this->string_, std::move(s));
}
template<typename Container>
basic_value(toml::string s, detail::region<Container> reg)
: type_(value_t::string),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->string_, std::move(s));
}
basic_value& operator=(toml::string s)
{
this->cleanup();
@@ -744,6 +782,14 @@ class basic_value
{
assigner(this->local_date_, ld);
}
template<typename Container>
basic_value(const local_date& ld, detail::region<Container> reg)
: type_(value_t::local_date),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->local_date_, ld);
}
basic_value& operator=(const local_date& ld)
{
this->cleanup();
@@ -768,6 +814,14 @@ class basic_value
{
assigner(this->local_time_, lt);
}
template<typename Container>
basic_value(const local_time& lt, detail::region<Container> reg)
: type_(value_t::local_time),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->local_time_, lt);
}
basic_value(const local_time& lt, std::vector<std::string> comments)
: type_(value_t::local_time),
region_info_(std::make_shared<region_base>(region_base{})),
@@ -818,6 +872,14 @@ class basic_value
{
assigner(this->local_datetime_, ldt);
}
template<typename Container>
basic_value(const local_datetime& ldt, detail::region<Container> reg)
: type_(value_t::local_datetime),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->local_datetime_, ldt);
}
basic_value(const local_datetime& ldt, std::vector<std::string> comments)
: type_(value_t::local_datetime),
region_info_(std::make_shared<region_base>(region_base{})),
@@ -842,6 +904,14 @@ class basic_value
{
assigner(this->offset_datetime_, odt);
}
template<typename Container>
basic_value(const offset_datetime& odt, detail::region<Container> reg)
: type_(value_t::offset_datetime),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->offset_datetime_, odt);
}
basic_value(const offset_datetime& odt, std::vector<std::string> comments)
: type_(value_t::offset_datetime),
region_info_(std::make_shared<region_base>(region_base{})),
@@ -888,6 +958,14 @@ class basic_value
{
assigner(this->array_, ary);
}
template<typename Container>
basic_value(const array_type& ary, detail::region<Container> reg)
: type_(value_t::array),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->array_, ary);
}
basic_value(const array_type& ary, std::vector<std::string> comments)
: type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{})),
@@ -1001,6 +1079,14 @@ class basic_value
{
assigner(this->table_, tab);
}
template<typename Container>
basic_value(const table_type& tab, detail::region<Container> reg)
: type_(value_t::table),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->table_, tab);
}
basic_value(const table_type& tab, std::vector<std::string> comments)
: type_(value_t::table),
region_info_(std::make_shared<region_base>(region_base{})),
@@ -1130,93 +1216,6 @@ class basic_value
}
// for internal use ------------------------------------------------------
//
// Those constructors take detail::region that contains parse result.
template<typename Container>
basic_value(boolean b, detail::region<Container> reg)
: type_(value_t::boolean),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->boolean_, b);
}
template<typename T, typename Container, typename std::enable_if<
detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
>::value, std::nullptr_t>::type = nullptr>
basic_value(T i, detail::region<Container> reg)
: type_(value_t::integer),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->integer_, static_cast<integer>(i));
}
template<typename T, typename Container, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, detail::region<Container> reg)
: type_(value_t::floating),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->floating_, static_cast<floating>(f));
}
template<typename Container>
basic_value(toml::string s, detail::region<Container> reg)
: type_(value_t::string),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->string_, std::move(s));
}
template<typename Container>
basic_value(const local_date& ld, detail::region<Container> reg)
: type_(value_t::local_date),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->local_date_, ld);
}
template<typename Container>
basic_value(const local_time& lt, detail::region<Container> reg)
: type_(value_t::local_time),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->local_time_, lt);
}
template<typename Container>
basic_value(const local_datetime& ldt, detail::region<Container> reg)
: type_(value_t::local_datetime),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->local_datetime_, ldt);
}
template<typename Container>
basic_value(const offset_datetime& odt, detail::region<Container> reg)
: type_(value_t::offset_datetime),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->offset_datetime_, odt);
}
template<typename Container>
basic_value(const array_type& ary, detail::region<Container> reg)
: type_(value_t::array),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->array_, ary);
}
template<typename Container>
basic_value(const table_type& tab, detail::region<Container> reg)
: type_(value_t::table),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
{
assigner(this->table_, tab);
}
template<typename T, typename Container, typename std::enable_if<
detail::is_exact_toml_type<T, value_type>::value,
@@ -1248,7 +1247,7 @@ class basic_value
bool is_array() const noexcept {return this->is(value_t::array );}
bool is_table() const noexcept {return this->is(value_t::table );}
value_t type() const noexcept {return type_;}
value_t type() const {return type_;}
template<value_t T>
typename detail::enum_to_type<T, value_type>::type& cast() &
@@ -1568,140 +1567,6 @@ class basic_value
return std::move(this->table_.value());
}
// accessors =============================================================
//
// may throw type_error or out_of_range
//
value_type& at(const key& k)
{
return this->as_table().at(k);
}
value_type const& at(const key& k) const
{
return this->as_table().at(k);
}
value_type& operator[](const key& k)
{
if(this->is_uninitialized())
{
*this = table_type{};
}
return this->as_table()[k];
}
value_type& at(const std::size_t idx)
{
return this->as_array().at(idx);
}
value_type const& at(const std::size_t idx) const
{
return this->as_array().at(idx);
}
value_type& operator[](const std::size_t idx) noexcept
{
return this->as_array(std::nothrow)[idx];
}
value_type const& operator[](const std::size_t idx) const noexcept
{
return this->as_array(std::nothrow)[idx];
}
void push_back(const value_type& x)
{
if(this->type_ != value_t::array)
{
throw type_error(detail::format_underline(
"toml::value::push_back(value): bad_cast to array type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
}
this->as_array(std::nothrow).push_back(x);
return;
}
void push_back(value_type&& x)
{
if(this->type_ != value_t::array)
{
throw type_error(detail::format_underline(
"toml::value::push_back(value): bad_cast to array type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
}
this->as_array(std::nothrow).push_back(std::move(x));
return;
}
template<typename ... Ts>
value_type& emplace_back(Ts&& ... args)
{
if(this->type_ != value_t::array)
{
throw type_error(detail::format_underline(
"toml::value::emplace_back(value): bad_cast to array type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
}
this->as_array(std::nothrow).emplace_back(std::forward<Ts>(args) ...);
return this->as_array(std::nothrow).back();
}
std::size_t size() const
{
switch(this->type_)
{
case value_t::array:
{
return this->as_array().size();
}
case value_t::table:
{
return this->as_table().size();
}
case value_t::string:
{
return this->as_string().str.size();
}
default:
{
throw type_error(detail::format_underline(
"toml::value::size(): bad_cast to container types", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
}
}
}
std::size_t count(const key_type& k) const
{
if(this->type_ != value_t::table)
{
throw type_error(detail::format_underline(
"toml::value::count(key): bad_cast to table type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
}
return this->as_table().count(k);
}
bool contains(const key_type& k) const
{
if(this->type_ != value_t::table)
{
throw type_error(detail::format_underline(
"toml::value::contains(key): bad_cast to table type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
}
return (this->as_table().count(k) != 0);
}
source_location location() const
{
return source_location(this->region_info_.get());
@@ -1956,27 +1821,25 @@ operator>=(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
template<typename C, template<typename ...> class T, template<typename ...> class A>
inline std::string format_error(const std::string& err_msg,
const basic_value<C, T, A>& v, const std::string& comment,
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v)), comment}
}, std::move(hints), colorize);
}, std::move(hints));
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
inline std::string format_error(const std::string& err_msg,
const toml::basic_value<C, T, A>& v1, const std::string& comment1,
const toml::basic_value<C, T, A>& v2, const std::string& comment2,
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v1)), comment1},
{std::addressof(detail::get_region(v2)), comment2}
}, std::move(hints), colorize);
}, std::move(hints));
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
@@ -1984,15 +1847,14 @@ inline std::string format_error(const std::string& err_msg,
const toml::basic_value<C, T, A>& v1, const std::string& comment1,
const toml::basic_value<C, T, A>& v2, const std::string& comment2,
const toml::basic_value<C, T, A>& v3, const std::string& comment3,
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v1)), comment1},
{std::addressof(detail::get_region(v2)), comment2},
{std::addressof(detail::get_region(v3)), comment3}
}, std::move(hints), colorize);
}, std::move(hints));
}
template<typename Visitor, typename C,