diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..421b8d1 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,45 @@ +version: 2.1 + +jobs: + test: + docker: + - image: circleci/buildpack-deps:bionic + steps: + - checkout + - run: + command: | + g++ --version + cd tests/ + ls + g++ -std=c++11 -O2 -Wall -Wextra -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 + cd toml-test/tests/invalid + for f in $(ls ./*.toml); + do echo "==> ${f}"; + cat ${f}; + echo "---------------------------------------"; + ./check ${f} invalid; + if [ $? -ne 0 ] ; then + exit 1 + fi + echo "======================================="; + done + cd ../valid + for f in $(ls ./*.toml); + do echo "==> ${f}"; + cat ${f}; + echo "---------------------------------------"; + ./check ${f} valid; + if [ $? -ne 0 ] ; then + exit 1 + fi + echo "======================================="; + done + +workflows: + version: 2.1 + test: + jobs: + - test diff --git a/.travis.yml b/.travis.yml index b549f32..840eabe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,39 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-5 - - build-essential - - cmake + - libboost-all-dev + - os: linux + language: cpp + compiler: gcc + env: COMPILER="g++-6" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + - libboost-all-dev + - os: linux + language: cpp + compiler: gcc + env: COMPILER="g++-7" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - libboost-all-dev + - os: linux + language: cpp + compiler: gcc + env: COMPILER="g++-8" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 - libboost-all-dev - os: linux language: cpp @@ -26,8 +57,66 @@ matrix: - llvm-toolchain-precise-3.7 packages: - clang-3.7 - - build-essential - - cmake + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-4.0" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-4.0 + packages: + - clang-4.0 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-5.0" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-6.0" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + packages: + - clang-6.0 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-7" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-7 + packages: + - clang-7 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-8" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-8 + packages: + - clang-8 - libboost-all-dev - os: osx language: cpp diff --git a/README.md b/README.md index 43b6f8c..03d602d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ toml11 [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) -C++11 header-only toml parser depending only on C++ standard library. +C++11 header-only toml parser/encoder depending only on C++ standard library. compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) @@ -387,7 +387,31 @@ If it's not a `toml::table`, the same error as "invalid type" would be thrown. ### Checking value type -When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`. +You can check what type of value does `toml::value` contains by `is_*` function. + +```cpp +toml::value v = /* ... */; +if(v.is_integer() && toml::get(v) == 42) +{ + std::cout << "value is 42" << std::endl; +} +``` + +The complete list of the functions is below. + +- `bool toml::value::is_boolean() const noexcept;` +- `bool toml::value::is_integer() const noexcept;` +- `bool toml::value::is_float() const noexcept;` +- `bool toml::value::is_string() const noexcept;` +- `bool toml::value::is_offset_datetime() const noexcept;` +- `bool toml::value::is_local_datetime() const noexcept;` +- `bool toml::value::is_local_date() const noexcept;` +- `bool toml::value::is_local_time() const noexcept;` +- `bool toml::value::is_array() const noexcept;` +- `bool toml::value::is_table() const noexcept;` +- `bool toml::value::is_uninitialized() const noexcept;` + +Also, you can get `enum class` value from `toml::value`. ```cpp switch(data.at("something").type()) @@ -400,6 +424,16 @@ switch(data.at("something").type()) } ``` +The complete list of the `enum`s can be found in the section +[underlying types](#underlying-types). + +The `enum`s can be used as a parameter of `toml::value::is` function like the following. + +```cpp +toml::value v = /* ... */; +if(v.is(toml::value_t::Boolean)) // ... +``` + ### Fill only the matched value The more sophisticated way is using `toml::from_toml` and `std::tie`. @@ -423,6 +457,148 @@ int i = 0; toml::from_toml(i, data.at("something")); ``` +### Conversion between toml value and your class + +You can also use `toml::get` and other related functions with the types you defined +after you implement some stuff. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; +}; +} // ext + +const auto data = toml::parse("example.toml"); + +const foo f = toml::get(data.at("foo")); +``` + +There are 2 ways to use `toml::get` with the types that you defined. + +The first one is to implement `from_toml(const toml::value&)` member function. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; + + void from_toml(const toml::value& v) + { + this->a = toml::find(v, "a"); + this->b = toml::find(v, "b"); + this->c = toml::find(v, "c"); + return; + } +}; +} // ext +``` + +In this way, because `toml::get` first constructs `foo` without arguments, +the type should be default-constructible. + +The second is to implement specialization of `toml::from` for your type. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; +}; +} // ext + +namespace toml +{ +template<> +struct from +{ + ext::foo from_toml(const toml::value& v) + { + ext::foo f; + f.a = toml::find(v, "a"); + f.b = toml::find(v, "b"); + f.c = toml::find(v, "c"); + return f; + } +}; +} // toml +``` + +In this way, since the conversion function is introduced from out of the class, +you can add conversion between `toml::value` and classes defined in another library. + +Note that you cannot implement both of the functions because the overload +resolution of `toml::get` become ambiguous. + +---- + +The opposite direction is also supported in a similar way. You can directly +pass your type to `toml::value`'s constructor by introducing `into_iter` or +`toml::into`. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; + + toml::table into_toml() const // you need to mark it const. + { + return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}}; + } +}; +} // ext + +ext::foo f{42, 3.14, "foobar"}; +toml::value v(f); +``` + +The definition of `toml::into` is similar to `from_toml()`. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; +}; +} // ext + +namespace toml +{ +template<> +struct into +{ + toml::table into_toml(const ext::foo& v) + { + return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}}; + } +}; +} // toml + +ext::foo f{42, 3.14, "foobar"}; +toml::value v(f); +``` + +Any type that can be converted to `toml::value`, e.g. `toml::table`, `toml::array`, +is okay to return from `into_toml`. + ### visiting toml::value TOML v2.1.0+ provides `toml::visit` to apply a function to `toml::value` in the @@ -642,6 +818,7 @@ I thank the contributor for providing great feature to this repository. - Intel Compiler support - Quentin Khan (@xaxousis) - Found & Fixed a bug around ODR + - Improved error message to show the location where the parser fails ## Licensing terms diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0664150..25c07d2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,12 +22,13 @@ set(TEST_NAMES test_parse_table_key test_get test_get_related_func - test_to_toml test_from_toml test_parse_file test_serialize_file test_parse_unicode test_error_detection + test_format_error + test_extended_conversions ) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) diff --git a/tests/check.cpp b/tests/check.cpp new file mode 100644 index 0000000..a271f3f --- /dev/null +++ b/tests/check.cpp @@ -0,0 +1,41 @@ +#include "toml.hpp" +#include +#include + +int main(int argc, char **argv) +{ + if(argc != 3) + { + std::cerr << "usage: ./check [filename] [valid|invalid]" << std::endl; + return 1; + } + + const std::string file_kind(argv[2]); + + try + { + const auto data = toml::parse(argv[1]); + std::cout << std::setprecision(16) << std::setw(80) << data; + if(file_kind == "valid") + { + return 0; + } + else + { + return 1; + } + } + catch(const toml::syntax_error& err) + { + std::cout << "what(): " << err.what() << std::endl; + if(file_kind == "invalid") + { + return 0; + } + else + { + return 1; + } + } + return 127; +} diff --git a/tests/test_extended_conversions.cpp b/tests/test_extended_conversions.cpp new file mode 100644 index 0000000..fd5d88d --- /dev/null +++ b/tests/test_extended_conversions.cpp @@ -0,0 +1,116 @@ +#define BOOST_TEST_MODULE "test_extended_conversions" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include + +namespace extlib +{ +struct foo +{ + int a; + std::string b; +}; +struct bar +{ + int a; + std::string b; + + void from_toml(const toml::value& v) + { + this->a = toml::find(v, "a"); + this->b = toml::find(v, "b"); + return ; + } + + toml::table into_toml() const + { + return toml::table{{"a", this->a}, {"b", this->b}}; + } +}; +} // extlib + +namespace toml +{ +template<> +struct from +{ + static extlib::foo from_toml(const toml::value& v) + { + return extlib::foo{toml::find(v, "a"), toml::find(v, "b")}; + } +}; + +template<> +struct into +{ + static toml::table into_toml(const extlib::foo& f) + { + return toml::table{{"a", f.a}, {"b", f.b}}; + } +}; +} // toml + + +BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods) +{ + const toml::value v{{"a", 42}, {"b", "baz"}}; + + const auto foo = toml::get(v); + BOOST_CHECK_EQUAL(foo.a, 42); + BOOST_CHECK_EQUAL(foo.b, "baz"); + + const toml::value v2(foo); + + BOOST_CHECK_EQUAL(v, v2); +} + +BOOST_AUTO_TEST_CASE(test_conversion_by_specialization) +{ + const toml::value v{{"a", 42}, {"b", "baz"}}; + + const auto bar = toml::get(v); + BOOST_CHECK_EQUAL(bar.a, 42); + BOOST_CHECK_EQUAL(bar.b, "baz"); + + const toml::value v2(bar); + + BOOST_CHECK_EQUAL(v, v2); +} + +BOOST_AUTO_TEST_CASE(test_recursive_conversion) +{ + 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 foos = toml::get>(v); + BOOST_CHECK_EQUAL(foos.size() , 4ul); + BOOST_CHECK_EQUAL(foos.at(0).a , 42); + BOOST_CHECK_EQUAL(foos.at(1).a , 43); + BOOST_CHECK_EQUAL(foos.at(2).a , 44); + BOOST_CHECK_EQUAL(foos.at(3).a , 45); + + BOOST_CHECK_EQUAL(foos.at(0).b , "baz"); + BOOST_CHECK_EQUAL(foos.at(1).b , "qux"); + BOOST_CHECK_EQUAL(foos.at(2).b , "quux"); + BOOST_CHECK_EQUAL(foos.at(3).b , "foobar"); + + const auto bars = toml::get>(v); + BOOST_CHECK_EQUAL(bars.size() , 4ul); + BOOST_CHECK_EQUAL(bars.at(0).a , 42); + BOOST_CHECK_EQUAL(bars.at(1).a , 43); + BOOST_CHECK_EQUAL(bars.at(2).a , 44); + BOOST_CHECK_EQUAL(bars.at(3).a , 45); + + BOOST_CHECK_EQUAL(bars.at(0).b , "baz"); + BOOST_CHECK_EQUAL(bars.at(1).b , "qux"); + BOOST_CHECK_EQUAL(bars.at(2).b , "quux"); + BOOST_CHECK_EQUAL(bars.at(3).b , "foobar"); +} diff --git a/tests/test_format_error.cpp b/tests/test_format_error.cpp new file mode 100644 index 0000000..36cb171 --- /dev/null +++ b/tests/test_format_error.cpp @@ -0,0 +1,75 @@ +#define BOOST_TEST_MODULE "test_value" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include + +// to check it successfully compiles. it does not check the formatted string. + +BOOST_AUTO_TEST_CASE(test_1_value) +{ + toml::value val(42); + + { + const std::string pretty_error = + toml::format_error("[error] test error", val, "this is a value"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error", val, "this is a value", + std::vector{"this is a hint"}); + std::cout << pretty_error << std::endl; + } +} + +BOOST_AUTO_TEST_CASE(test_2_values) +{ + toml::value v1(42); + toml::value v2(3.14); + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + std::vector{"hint"}); + std::cout << pretty_error << std::endl; + } +} + +BOOST_AUTO_TEST_CASE(test_3_values) +{ + toml::value v1(42); + toml::value v2(3.14); + toml::value v3("foo"); + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + v3, "this is a meta-syntactic variable"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + v3, "this is a meta-syntactic variable", + std::vector{"hint 1", "hint 2"}); + std::cout << pretty_error << std::endl; + } +} diff --git a/tests/test_to_toml.cpp b/tests/test_to_toml.cpp deleted file mode 100644 index 464a760..0000000 --- a/tests/test_to_toml.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#define BOOST_TEST_MODULE "test_to_toml" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include - -BOOST_AUTO_TEST_CASE(test_value_boolean) -{ - toml::value v1 = toml::to_toml(true); - toml::value v2 = toml::to_toml(false); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); - BOOST_CHECK(v1.is(toml::value_t::Boolean)); - BOOST_CHECK(v2.is(toml::value_t::Boolean)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), true); - BOOST_CHECK_EQUAL(v2.cast(), false); -} - -BOOST_AUTO_TEST_CASE(test_value_integer) -{ - toml::value v1 = toml::to_toml(-42); - toml::value v2 = toml::to_toml(42u); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK(v1.is(toml::value_t::Integer)); - BOOST_CHECK(v2.is(toml::value_t::Integer)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), -42); - BOOST_CHECK_EQUAL(v2.cast(), 42u); -} - -BOOST_AUTO_TEST_CASE(test_value_float) -{ - toml::value v1 = toml::to_toml(3.14); - toml::value v2 = toml::to_toml(3.14f); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); - BOOST_CHECK(v1.is(toml::value_t::Float)); - BOOST_CHECK(v2.is(toml::value_t::Float)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), 3.14); - BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); -} - -BOOST_AUTO_TEST_CASE(test_value_string) -{ - toml::value v1 = toml::to_toml(std::string("foo")); - toml::value v2 = toml::to_toml(std::string("foo"), toml::string_t::literal); - toml::value v3 = toml::to_toml("foo"); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); - BOOST_CHECK(v1.is(toml::value_t::String)); - BOOST_CHECK(v2.is(toml::value_t::String)); - BOOST_CHECK(v3.is(toml::value_t::String)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - BOOST_CHECK(v3.is()); - - BOOST_CHECK_EQUAL(v1.cast(), "foo"); - BOOST_CHECK_EQUAL(v2.cast(), "foo"); - BOOST_CHECK_EQUAL(v3.cast(), "foo"); -} - -BOOST_AUTO_TEST_CASE(test_value_local_date) -{ - toml::value v1 = toml::to_toml(toml::local_date(2018, toml::month_t::Jan, 31)); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); - BOOST_CHECK(v1.is(toml::value_t::LocalDate)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::local_date(2018, toml::month_t::Jan, 31)); -} - -BOOST_AUTO_TEST_CASE(test_value_local_time) -{ - toml::value v1 = toml::to_toml(toml::local_time(12, 30, 45)); - toml::value v2 = toml::to_toml(std::chrono::hours(12) + std::chrono::minutes(30) + - std::chrono::seconds(45)); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime); - BOOST_CHECK(v1.is(toml::value_t::LocalTime)); - BOOST_CHECK(v2.is(toml::value_t::LocalTime)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::local_time(12, 30, 45)); - BOOST_CHECK_EQUAL(v2.cast(), - toml::local_time(12, 30, 45)); - BOOST_CHECK_EQUAL(v1.cast(), - v2.cast()); -} - -BOOST_AUTO_TEST_CASE(test_value_local_datetime) -{ - toml::value v1 = toml::to_toml(toml::local_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45) - )); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); - BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::local_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45))); -} - -BOOST_AUTO_TEST_CASE(test_value_offset_datetime) -{ - toml::value v1 = toml::to_toml(toml::offset_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45), - toml::time_offset(9, 0) - )); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); - BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::offset_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45), - toml::time_offset(9, 0) - )); -} - -BOOST_AUTO_TEST_CASE(test_value_array) -{ - std::vector v{1,2,3,4,5}; - toml::value v1 = toml::to_toml(v); - toml::value v2 = toml::to_toml(6,7,8,9,0); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); - BOOST_CHECK(v1.is(toml::value_t::Array)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); - BOOST_CHECK(v2.is(toml::value_t::Array)); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); - BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); - BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 3); - BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 4); - BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 5); - - BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 6); - BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 7); - BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 8); - BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 9); - BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 0); -} - -BOOST_AUTO_TEST_CASE(test_value_table) -{ - toml::value v1 = toml::to_toml({{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); - BOOST_CHECK(v1.is(toml::value_t::Table)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); - BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); - BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "qux"); -} diff --git a/tests/test_value.cpp b/tests/test_value.cpp index 702fa8d..a0b38e4 100644 --- a/tests/test_value.cpp +++ b/tests/test_value.cpp @@ -20,6 +20,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), false); @@ -33,6 +35,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), false); BOOST_CHECK_EQUAL(v2.cast(), true); @@ -48,6 +52,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v4.is(toml::value_t::Boolean)); BOOST_CHECK(v3.is()); BOOST_CHECK(v4.is()); + BOOST_CHECK(v3.is_boolean()); + BOOST_CHECK(v4.is_boolean()); BOOST_CHECK_EQUAL(v3.cast(), false); BOOST_CHECK_EQUAL(v4.cast(), true); @@ -61,6 +67,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v6.is(toml::value_t::Boolean)); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v3.is_boolean()); + BOOST_CHECK(v4.is_boolean()); BOOST_CHECK_EQUAL(v5.cast(), false); BOOST_CHECK_EQUAL(v6.cast(), true); @@ -74,6 +82,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_integer()); + BOOST_CHECK(v2.is_float()); BOOST_CHECK_EQUAL(v1.cast(), 42); BOOST_CHECK_EQUAL(v2.cast(), 3.14); @@ -90,6 +100,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v2.is(toml::value_t::Integer)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_integer()); + BOOST_CHECK(v2.is_integer()); BOOST_CHECK_EQUAL(v1.cast(), -42); BOOST_CHECK_EQUAL(v2.cast(), 42u); @@ -103,6 +115,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v2.is(toml::value_t::Integer)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_integer()); + BOOST_CHECK(v2.is_integer()); BOOST_CHECK_EQUAL(v1.cast(), 54); BOOST_CHECK_EQUAL(v2.cast(), -54); @@ -118,6 +132,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v4.is(toml::value_t::Integer)); BOOST_CHECK(v3.is()); BOOST_CHECK(v4.is()); + BOOST_CHECK(v3.is_integer()); + BOOST_CHECK(v4.is_integer()); BOOST_CHECK_EQUAL(v3.cast(), 54); BOOST_CHECK_EQUAL(v4.cast(), -54); @@ -131,6 +147,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v6.is(toml::value_t::Integer)); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v5.is_integer()); + BOOST_CHECK(v6.is_integer()); BOOST_CHECK_EQUAL(v5.cast(), 54); BOOST_CHECK_EQUAL(v6.cast(), -54); @@ -144,6 +162,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), false); @@ -160,6 +180,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_float()); + BOOST_CHECK(v2.is_float()); BOOST_CHECK_EQUAL(v1.cast(), 3.14); BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); @@ -173,6 +195,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_float()); + BOOST_CHECK(v2.is_float()); BOOST_CHECK_CLOSE_FRACTION(v1.cast(), 2.718, 1e-3); BOOST_CHECK_EQUAL(v2.cast(), 2.718); @@ -188,6 +212,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v4.is(toml::value_t::Float)); BOOST_CHECK(v3.is()); BOOST_CHECK(v4.is()); + BOOST_CHECK(v3.is_float()); + BOOST_CHECK(v4.is_float()); BOOST_CHECK_CLOSE_FRACTION(v3.cast(), 2.718, 1e-3); BOOST_CHECK_EQUAL(v4.cast(), 2.718); @@ -201,6 +227,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v6.is(toml::value_t::Float)); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v5.is_float()); + BOOST_CHECK(v6.is_float()); BOOST_CHECK_CLOSE_FRACTION(v5.cast(), 2.718, 1e-3); BOOST_CHECK_EQUAL(v6.cast(), 2.718); @@ -214,6 +242,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), false); @@ -234,6 +264,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); BOOST_CHECK(v3.is()); + BOOST_CHECK(v1.is_string()); + BOOST_CHECK(v2.is_string()); + BOOST_CHECK(v3.is_string()); BOOST_CHECK_EQUAL(v1.cast(), "foo"); BOOST_CHECK_EQUAL(v2.cast(), "foo"); @@ -249,9 +282,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v1.is(toml::value_t::String)); BOOST_CHECK(v2.is(toml::value_t::String)); BOOST_CHECK(v3.is(toml::value_t::String)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - BOOST_CHECK(v3.is()); + BOOST_CHECK(v1.is_string()); + BOOST_CHECK(v2.is_string()); + BOOST_CHECK(v3.is_string()); BOOST_CHECK_EQUAL(v1.cast(), "bar"); BOOST_CHECK_EQUAL(v2.cast(), "bar"); @@ -273,6 +306,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v4.is()); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v4.is_string()); + BOOST_CHECK(v5.is_string()); + BOOST_CHECK(v6.is_string()); BOOST_CHECK_EQUAL(v4.cast(), "bar"); BOOST_CHECK_EQUAL(v5.cast(), "bar"); @@ -291,6 +327,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v4.is()); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v4.is_string()); + BOOST_CHECK(v5.is_string()); + BOOST_CHECK(v6.is_string()); BOOST_CHECK_EQUAL(v4.cast(), "baz"); BOOST_CHECK_EQUAL(v5.cast(), "baz"); @@ -309,6 +348,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); BOOST_CHECK(v3.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); + BOOST_CHECK(v3.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), true); @@ -322,6 +364,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); BOOST_CHECK(v1.is(toml::value_t::LocalDate)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_date()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_date(2018, toml::month_t::Jan, 31)); @@ -331,6 +374,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); BOOST_CHECK(v1.is(toml::value_t::LocalDate)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_date()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_date(2018, toml::month_t::Apr, 1)); @@ -341,6 +385,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate); BOOST_CHECK(v2.is(toml::value_t::LocalDate)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_local_date()); BOOST_CHECK_EQUAL(v2.cast(), toml::local_date(2018, toml::month_t::Apr, 1)); @@ -349,6 +394,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -364,6 +410,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK(v2.is(toml::value_t::LocalTime)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_local_time()); + BOOST_CHECK(v2.is_local_time()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_time(12, 30, 45)); @@ -377,6 +425,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); BOOST_CHECK(v1.is(toml::value_t::LocalTime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_time()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_time(1, 30, 0, 100, 0)); @@ -386,6 +435,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime); BOOST_CHECK(v3.is(toml::value_t::LocalTime)); BOOST_CHECK(v3.is()); + BOOST_CHECK(v3.is_local_time()); BOOST_CHECK_EQUAL(v3.cast(), toml::local_time(1, 30, 0, 100, 0)); @@ -394,6 +444,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -407,6 +458,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_datetime( @@ -420,6 +472,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_datetime( @@ -432,6 +485,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime); BOOST_CHECK(v2.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_local_datetime()); BOOST_CHECK_EQUAL(v2.cast(), toml::local_datetime( @@ -442,6 +496,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -456,6 +511,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_offset_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::offset_datetime( @@ -472,6 +528,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_offset_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::offset_datetime( @@ -485,6 +542,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime); BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_offset_datetime()); BOOST_CHECK_EQUAL(v2.cast(), toml::offset_datetime( @@ -495,6 +553,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -507,10 +566,12 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); BOOST_CHECK(v1.is(toml::value_t::Array)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_array()); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); BOOST_CHECK(v2.is(toml::value_t::Array)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_array()); BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); @@ -530,10 +591,12 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); BOOST_CHECK(v1.is(toml::value_t::Array)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_array()); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); BOOST_CHECK(v2.is(toml::value_t::Array)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_array()); BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 6); BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 7); @@ -553,6 +616,7 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array); BOOST_CHECK(v3.is(toml::value_t::Array)); BOOST_CHECK(v3.is()); + BOOST_CHECK(v3.is_array()); BOOST_CHECK_EQUAL(v3.cast().at(0).cast(), 6); BOOST_CHECK_EQUAL(v3.cast().at(1).cast(), 7); @@ -564,6 +628,7 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -574,6 +639,7 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); BOOST_CHECK(v1.is(toml::value_t::Table)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_table()); BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); @@ -584,6 +650,7 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); BOOST_CHECK(v1.is(toml::value_t::Table)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_table()); BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 2.71); BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 54); @@ -595,6 +662,7 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table); BOOST_CHECK(v3.is(toml::value_t::Table)); BOOST_CHECK(v3.is()); + BOOST_CHECK(v3.is_table()); BOOST_CHECK_EQUAL(v3.cast().at("foo").cast(), 2.71); BOOST_CHECK_EQUAL(v3.cast().at("bar").cast(), 54); @@ -604,5 +672,13 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } + +BOOST_AUTO_TEST_CASE(test_value_empty) +{ + toml::value v1; + BOOST_CHECK(v1.is_uninitialized()); + BOOST_CHECK(v1.is(toml::value_t::Empty)); +} diff --git a/toml.hpp b/toml.hpp index 80e62e2..8cd95af 100644 --- a/toml.hpp +++ b/toml.hpp @@ -35,7 +35,6 @@ #include "toml/parser.hpp" #include "toml/serializer.hpp" -#include "toml/to_toml.hpp" #include "toml/from_toml.hpp" #include "toml/get.hpp" diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 193640d..d20ea5b 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_DATETIME -#define TOML11_DATETIME +#ifndef TOML11_DATETIME_HPP +#define TOML11_DATETIME_HPP #include #include #include diff --git a/toml/exception.hpp b/toml/exception.hpp index f92a7c1..1a3cd0c 100644 --- a/toml/exception.hpp +++ b/toml/exception.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_EXCEPTION -#define TOML11_EXCEPTION +#ifndef TOML11_EXCEPTION_HPP +#define TOML11_EXCEPTION_HPP #include #include diff --git a/toml/from.hpp b/toml/from.hpp new file mode 100644 index 0000000..8251973 --- /dev/null +++ b/toml/from.hpp @@ -0,0 +1,20 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_FROM_HPP +#define TOML11_FROM_HPP +#include "traits.hpp" + +namespace toml +{ + +template +struct from; +// { +// static T from_toml(const toml::value& v) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_FROM_HPP diff --git a/toml/from_toml.hpp b/toml/from_toml.hpp index a9348eb..f75b24b 100644 --- a/toml/from_toml.hpp +++ b/toml/from_toml.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_FROM_TOML -#define TOML11_FROM_TOML +#ifndef TOML11_FROM_TOML_HPP +#define TOML11_FROM_TOML_HPP #include "get.hpp" namespace toml diff --git a/toml/get.hpp b/toml/get.hpp index fe51622..6aad019 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -1,7 +1,8 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_GET -#define TOML11_GET +#ifndef TOML11_GET_HPP +#define TOML11_GET_HPP +#include "from.hpp" #include "result.hpp" #include "value.hpp" #include @@ -173,6 +174,20 @@ template::value, std::nullptr_t>::type = nullptr> T get(const toml::value& v); +template>, // not a toml::value + detail::has_from_toml_method, // but has from_toml(toml::value) memfn + std::is_default_constructible // and default constructible + >::value, std::nullptr_t>::type = nullptr> +T get(const toml::value& v); + +template> // not a toml::value + >::value, std::nullptr_t>::type = nullptr, + std::size_t = sizeof(::toml::from) // and has from specialization + > +T get(const toml::value& v); + // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. @@ -210,8 +225,9 @@ T get(const value& v) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), - detail::get_region(v), "here")); + " but there are ", ar.size(), " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } std::transform(ar.cbegin(), ar.cend(), container.begin(), [](const value& x){return ::toml::get(x);}); @@ -233,7 +249,9 @@ T get(const value& v) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::pair but there are ", ar.size(), - " elements in toml array."), detail::get_region(v), "here")); + " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); @@ -264,7 +282,9 @@ T get(const value& v) throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::tuple with ", std::tuple_size::value, "elements, but there are ", ar.size(), - " elements in toml array."), detail::get_region(v), "here")); + " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); @@ -292,6 +312,29 @@ T get(const toml::value& v) return map; } + +// ============================================================================ +// user-defined, but compatible types. + +template>, // not a toml::value + detail::has_from_toml_method, // but has from_toml(toml::value) memfn + std::is_default_constructible // and default constructible + >::value, std::nullptr_t>::type> +T get(const toml::value& v) +{ + T ud; + ud.from_toml(v); + return ud; +} +template> // not a toml::value + >::value, std::nullptr_t>::type, std::size_t> // and has from +T get(const toml::value& v) +{ + return ::toml::from::from_toml(v); +} + // ============================================================================ // find and get @@ -340,8 +383,9 @@ find(const toml::value& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(tab.at(ky)); } @@ -353,8 +397,9 @@ find(toml::value& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(tab.at(ky)); } @@ -366,8 +411,9 @@ find(toml::value&& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(std::move(tab[ky])); } diff --git a/toml/into.hpp b/toml/into.hpp new file mode 100644 index 0000000..17f2248 --- /dev/null +++ b/toml/into.hpp @@ -0,0 +1,20 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_INTO_HPP +#define TOML11_INTO_HPP +#include "traits.hpp" + +namespace toml +{ + +template +struct into; +// { +// static toml::value into_toml(const T& user_defined_type) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_INTO_HPP diff --git a/toml/parser.hpp b/toml/parser.hpp index 983d54e..765d8b5 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -29,13 +29,13 @@ parse_boolean(location& loc) else // internal error. { throw toml::internal_error(format_underline( - "[error] toml::parse_boolean: internal error", reg, - "invalid token")); + "[error] toml::parse_boolean: internal error", + {{std::addressof(reg), "invalid token"}})); } } loc.iter() = first; //rollback - return err(format_underline("[error] toml::parse_boolean: ", loc, - "the next token is not a boolean")); + return err(format_underline("[error] toml::parse_boolean: ", + {{std::addressof(loc), "the next token is not a boolean"}})); } template @@ -57,14 +57,14 @@ parse_binary_integer(location& loc) { throw toml::internal_error(format_underline( "[error] toml::parse_integer: internal error", - token.unwrap(), "invalid token")); + {{std::addressof(token.unwrap()), "invalid token"}})); } } return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_binary_integer:", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_binary_integer:", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -84,8 +84,8 @@ parse_octal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_octal_integer:", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_octal_integer:", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -105,8 +105,8 @@ parse_hexadecimal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_hexadecimal_integer", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_hexadecimal_integer", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -133,8 +133,8 @@ parse_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_integer: ", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_integer: ", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -222,8 +222,8 @@ parse_floating(location& loc) return ok(std::make_pair(v, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_floating: ", loc, - "the next token is not a float")); + return err(format_underline("[error] toml::parse_floating: ", + {{std::addressof(loc), "the next token is not a float"}})); } template @@ -252,8 +252,9 @@ std::string read_utf8_codepoint(const region& reg, { throw syntax_error(format_underline("[error] " "toml::read_utf8_codepoint: codepoints in the range " - "[0xD800, 0xDFFF] are not valid UTF-8.", - loc, "not a valid UTF-8 codepoint")); + "[0xD800, 0xDFFF] are not valid UTF-8.", {{ + std::addressof(loc), "not a valid UTF-8 codepoint" + }})); } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx @@ -261,15 +262,8 @@ std::string read_utf8_codepoint(const region& reg, character += static_cast(0x80|(codepoint >> 6 & 0x3F)); character += static_cast(0x80|(codepoint & 0x3F)); } - else if(codepoint < 0x200000) // U+010000 ... U+1FFFFF + else if(codepoint < 0x110000) // U+010000 ... U+10FFFF { - if(0x10FFFF < codepoint) // out of Unicode region - { - throw syntax_error(format_underline("[error] " - "toml::read_utf8_codepoint: input codepoint is too large to " - "decode as a unicode character.", loc, - "should be in [0x00..0x10FFFF]")); - } // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += static_cast(0xF0| codepoint >> 18); character += static_cast(0x80|(codepoint >> 12 & 0x3F)); @@ -278,9 +272,9 @@ std::string read_utf8_codepoint(const region& reg, } else // out of UTF-8 region { - throw std::range_error(format_underline(concat_to_string("[error] " - "input codepoint (", str, ") is too large to encode as utf-8."), - reg, "should be in [0x00..0x10FFFF]")); + throw syntax_error(format_underline("[error] toml::read_utf8_codepoint:" + " input codepoint is too large.", + {{std::addressof(loc), "should be in [0x00..0x10FFFF]"}})); } return character; } @@ -291,8 +285,8 @@ result parse_escape_sequence(location& loc) const auto first = loc.iter(); if(first == loc.end() || *first != '\\') { - return err(format_underline("[error]: toml::parse_escape_sequence: ", loc, - "the next token is not an escape sequence \"\\\"")); + return err(format_underline("[error]: toml::parse_escape_sequence: ", {{ + std::addressof(loc), "the next token is not a backslash \"\\\""}})); } ++loc.iter(); switch(*loc.iter()) @@ -314,7 +308,7 @@ result parse_escape_sequence(location& loc) { return err(format_underline("[error] parse_escape_sequence: " "invalid token found in UTF-8 codepoint uXXXX.", - loc, token.unwrap_err())); + {{std::addressof(loc), token.unwrap_err()}})); } } case 'U': @@ -327,16 +321,16 @@ result parse_escape_sequence(location& loc) { return err(format_underline("[error] parse_escape_sequence: " "invalid token found in UTF-8 codepoint Uxxxxxxxx", - loc, token.unwrap_err())); + {{std::addressof(loc), token.unwrap_err()}})); } } } const auto msg = format_underline("[error] parse_escape_sequence: " - "unknown escape sequence appeared.", loc, "escape sequence is one of" - " \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx", {"if you want to write " - "backslash as just one backslash, use literal string like:", - "regex = '<\\i\\c*\\s*>'"}); + "unknown escape sequence appeared.", {{std::addressof(loc), + "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, + /* Hints = */{"if you want to write backslash as just one backslash, " + "use literal string like: regex = '<\\i\\c*\\s*>'"}); loc.iter() = first; return err(msg); } @@ -359,7 +353,7 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: invalid token", - inner_loc, "should be \"\"\"")); + {{std::addressof(inner_loc), "should be \"\"\""}})); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -385,7 +379,7 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: unexpected end of region", - inner_loc, "not sufficient token")); + {{std::addressof(inner_loc), "not sufficient token"}})); } delim = lex_ml_basic_string_delim::invoke(inner_loc); } @@ -412,7 +406,7 @@ parse_basic_string(location& loc) if(!quot) { throw internal_error(format_underline("[error] parse_basic_string: " - "invalid token", inner_loc, "should be \"")); + "invalid token", {{std::addressof(inner_loc), "should be \""}})); } std::string retval; @@ -434,7 +428,7 @@ parse_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: unexpected end of region", - inner_loc, "not sufficient token")); + {{std::addressof(inner_loc), "not sufficient token"}})); } quot = lex_quotation_mark::invoke(inner_loc); } @@ -461,7 +455,7 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - inner_loc, "should be '''")); + {{std::addressof(inner_loc), "should be '''"}})); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -473,7 +467,7 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - inner_loc, "should be '''")); + {{std::addressof(inner_loc), "should be '''"}})); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -500,7 +494,7 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - inner_loc, "should be '")); + {{std::addressof(inner_loc), "should be '"}})); } const auto body = repeat::invoke(inner_loc); @@ -510,7 +504,7 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - inner_loc, "should be '")); + {{std::addressof(inner_loc), "should be '"}})); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -531,8 +525,8 @@ parse_string(location& loc) if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;} if(const auto rslt = parse_basic_string(loc)) {return rslt;} if(const auto rslt = parse_literal_string(loc)) {return rslt;} - return err(format_underline("[error] toml::parse_string: ", loc, - "the next token is not a string")); + return err(format_underline("[error] toml::parse_string: ", + {{std::addressof(loc), "the next token is not a string"}})); } template @@ -547,21 +541,23 @@ parse_local_date(location& loc) const auto y = lex_date_fullyear::invoke(inner_loc); if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { + const std::string msg = y.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `-`"); + throw internal_error(format_underline("[error]: " "toml::parse_inner_local_date: invalid year format", - inner_loc, y.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `-`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto m = lex_date_month::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { + const std::string msg = m.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `-`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid month format", - inner_loc, m.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `-`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto d = lex_date_mday::invoke(inner_loc); @@ -569,7 +565,7 @@ parse_local_date(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid day format", - inner_loc, d.unwrap_err())); + {{std::addressof(inner_loc), d.unwrap_err()}})); } return ok(std::make_pair(local_date( static_cast(from_string(y.unwrap().str(), 0)), @@ -581,8 +577,8 @@ parse_local_date(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_date: ", loc, - "the next token is not a local_date")); + return err(format_underline("[error]: toml::parse_local_date: ", + {{std::addressof(loc), "the next token is not a local_date"}})); } } @@ -598,21 +594,23 @@ parse_local_time(location& loc) const auto h = lex_time_hour::invoke(inner_loc); if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { + const std::string msg = h.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `:`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid year format", - inner_loc, h.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `:`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto m = lex_time_minute::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { + const std::string msg = m.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `:`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid month format", - inner_loc, m.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `:`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto s = lex_time_second::invoke(inner_loc); @@ -620,7 +618,7 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid second format", - inner_loc, s.unwrap_err())); + {{std::addressof(inner_loc), s.unwrap_err()}})); } local_time time( static_cast(from_string(h.unwrap().str(), 0)), @@ -656,7 +654,7 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid subsecond format", - inner_loc, secfrac.unwrap_err())); + {{std::addressof(inner_loc), secfrac.unwrap_err()}})); } } return ok(std::make_pair(time, token.unwrap())); @@ -664,8 +662,8 @@ parse_local_time(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_time: ", loc, - "the next token is not a local_time")); + return err(format_underline("[error]: toml::parse_local_time: ", + {{std::addressof(loc), "the next token is not a local_time"}})); } } @@ -680,25 +678,26 @@ parse_local_datetime(location& loc) const auto date = parse_local_date(inner_loc); if(!date || inner_loc.iter() == inner_loc.end()) { + const std::string msg = date.map_err_or_else( + [](const std::string& msg) {return msg;}, "date, not datetime"); + throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, date.map_err_or_else([](const std::string& msg){ - return msg; - }, "date, not datetime"))); + {{std::addressof(inner_loc), msg}})); } const char delim = *(inner_loc.iter()++); if(delim != 'T' && delim != 't' && delim != ' ') { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, "should be `T` or ` ` (space)")); + {{std::addressof(inner_loc), "should be `T` or ` ` (space)"}})); } const auto time = parse_local_time(inner_loc); if(!time) { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, "invalid time fomrat")); + {{std::addressof(inner_loc), "invalid time fomrat"}})); } return ok(std::make_pair( local_datetime(date.unwrap().first, time.unwrap().first), @@ -707,8 +706,8 @@ parse_local_datetime(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_datetime: ", loc, - "the next token is not a local_datetime")); + return err(format_underline("[error]: toml::parse_local_datetime: ", + {{std::addressof(loc), "the next token is not a local_datetime"}})); } } @@ -723,11 +722,12 @@ parse_offset_datetime(location& loc) const auto datetime = parse_local_datetime(inner_loc); if(!datetime || inner_loc.iter() == inner_loc.end()) { + const std::string msg = datetime.map_err_or_else( + [](const std::string& msg){return msg;}, "date, not datetime"); + throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - inner_loc, datetime.map_err_or_else([](const std::string& msg){ - return msg; - }, "date, not datetime"))); + {{std::addressof(inner_loc), msg}})); } time_offset offset(0, 0); if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) @@ -748,7 +748,7 @@ parse_offset_datetime(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - inner_loc, "should be `Z` or `+HH:MM`")); + {{std::addressof(inner_loc), "should be `Z` or `+HH:MM`"}})); } return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), token.unwrap())); @@ -756,8 +756,8 @@ parse_offset_datetime(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_offset_datetime: ", loc, - "the next token is not a local_datetime")); + return err(format_underline("[error]: toml::parse_offset_datetime: ", + {{std::addressof(loc), "the next token is not a local_datetime"}})); } } @@ -778,8 +778,8 @@ parse_simple_key(location& loc) const auto reg = bare.unwrap(); return ok(std::make_pair(reg.str(), reg)); } - return err(format_underline("[error] toml::parse_simple_key: ", loc, - "the next token is not a simple key")); + return err(format_underline("[error] toml::parse_simple_key: ", + {{std::addressof(loc), "the next token is not a simple key"}})); } // dotted key become vector of keys @@ -806,7 +806,7 @@ parse_key(location& loc) { throw internal_error(format_underline("[error] " "toml::detail::parse_key: dotted key contains invalid key", - inner_loc, k.unwrap_err())); + {{std::addressof(inner_loc), k.unwrap_err()}})); } lex_ws::invoke(inner_loc); @@ -821,8 +821,8 @@ parse_key(location& loc) else { throw internal_error(format_underline("[error] toml::parse_key: " - "dotted key contains invalid key ", inner_loc, - "should be `.`")); + "dotted key contains invalid key ", + {{std::addressof(inner_loc), "should be `.`"}})); } } return ok(std::make_pair(keys, reg)); @@ -835,7 +835,8 @@ parse_key(location& loc) return ok(std::make_pair(std::vector(1, smpl.unwrap().first), smpl.unwrap().second)); } - return err(format_underline("[error] toml::parse_key: ", loc, "is not a valid key")); + return err(format_underline("[error] toml::parse_key: ", + {{std::addressof(loc), "is not a valid key"}})); } // forward-decl to implement parse_array and parse_table @@ -876,16 +877,34 @@ parse_array(location& loc) { if(!retval.empty() && retval.front().type() != val.as_ok().type()) { - throw syntax_error(format_underline( - "[error] toml::parse_array: type of elements should be the " - "same each other.", region(loc, first, loc.iter()), - "inhomogeneous types")); + auto array_start_loc = loc; + array_start_loc.iter() = first; + + throw syntax_error(format_underline("[error] toml::parse_array: " + "type of elements should be the same each other.", { + {std::addressof(array_start_loc), "array starts here"}, + { + std::addressof(get_region(retval.front())), + "value has type " + stringize(retval.front().type()) + }, + { + std::addressof(get_region(val.unwrap())), + "value has different type, " + stringize(val.unwrap().type()) + } + })); } retval.push_back(std::move(val.unwrap())); } else { - return err(val.unwrap_err()); + auto array_start_loc = loc; + array_start_loc.iter() = first; + + throw syntax_error(format_underline("[error] toml::parse_array: " + "value having invalid format appeared in an array", { + {std::addressof(array_start_loc), "array starts here"}, + {std::addressof(loc), "it is not a valid value."} + })); } using lex_array_separator = sequence, character<','>>; @@ -901,14 +920,21 @@ parse_array(location& loc) } else { + auto array_start_loc = loc; + array_start_loc.iter() = first; + throw syntax_error(format_underline("[error] toml::parse_array:" - " missing array separator `,`", loc, "should be `,`")); + " missing array separator `,` after a value", { + {std::addressof(array_start_loc), "array starts here"}, + {std::addressof(loc), "should be `,`"} + })); } } } loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array: " - "array did not closed by `]`", loc, "should be closed")); + "array did not closed by `]`", + {{std::addressof(loc), "should be closed"}})); } template @@ -926,7 +952,8 @@ parse_key_value_pair(location& loc) { loc.iter() = first; msg = format_underline("[error] toml::parse_key_value_pair: " - "empty key is not allowed.", loc, "key expected before '='"); + "empty key is not allowed.", + {{std::addressof(loc), "key expected before '='"}}); } return err(std::move(msg)); } @@ -941,14 +968,16 @@ parse_key_value_pair(location& loc) if(std::find(loc.iter(), line_end, '=') != line_end) { msg = format_underline("[error] toml::parse_key_value_pair: " - "invalid format for key", loc, "invalid character in key", { - "Did you forget '.' to separate dotted-key?", + "invalid format for key", + {{std::addressof(loc), "invalid character in key"}}, + {"Did you forget '.' to separate dotted-key?", "Allowed characters for bare key are [0-9a-zA-Z_-]."}); } else // if not, the error is lack of key-value separator. { msg = format_underline("[error] toml::parse_key_value_pair: " - "missing key-value separator `=`", loc, "should be `=`"); + "missing key-value separator `=`", + {{std::addressof(loc), "should be `=`"}}); } loc.iter() = first; return err(std::move(msg)); @@ -960,17 +989,17 @@ parse_key_value_pair(location& loc) { std::string msg; loc.iter() = after_kvsp; + // check there is something not a comment/whitespace after `=` if(sequence, maybe, lex_newline>::invoke(loc)) { loc.iter() = after_kvsp; msg = format_underline("[error] toml::parse_key_value_pair: " - "missing value after key-value separator '='", loc, - "expected value, but got nothing"); + "missing value after key-value separator '='", + {{std::addressof(loc), "expected value, but got nothing"}}); } - else + else // there is something not a comment/whitespace, so invalid format. { - msg = format_underline("[error] toml::parse_key_value_pair: " - "invalid value format", loc, val.unwrap_err()); + msg = std::move(val.unwrap_err()); } loc.iter() = first; return err(msg); @@ -995,6 +1024,75 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last) return retval; } +// forward decl for is_valid_forward_table_definition +template +result, region>, std::string> +parse_table_key(location& loc); +// The following toml file is allowed. +// ```toml +// [a.b.c] # here, table `a` has element `b`. +// foo = "bar" +// [a] # merge a = {baz = "qux"} to a = {b = {...}} +// baz = "qux" +// ``` +// But the following is not allowed. +// ```toml +// [a] +// b.c.foo = "bar" +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// The following is neither allowed. +// ```toml +// a = { b.c.foo = "bar"} +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// Here, it parses region of `tab->at(k)` as a table key and check the depth +// of the key. If the key region points deeper node, it would be allowed. +// Otherwise, the key points the same node. It would be rejected. +template +bool is_valid_forward_table_definition(const value& fwd, + Iterator key_first, Iterator key_curr, Iterator key_last) +{ + location def("internal", detail::get_region(fwd).str()); + if(const auto tabkeys = parse_table_key(def)) + { + // table keys always contains all the nodes from the root. + const auto& tks = tabkeys.unwrap().first; + if(std::size_t(std::distance(key_first, key_last)) == tks.size() && + std::equal(tks.begin(), tks.end(), key_first)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + if(const auto dotkeys = parse_key(def)) + { + // consider the following case. + // [a] + // b.c = {d = 42} + // [a.b.c] + // e = 2.71 + // this defines the table [a.b.c] twice. no? + + // a dotted key starts from the node representing a table in which the + // dotted key belongs to. + const auto& dks = dotkeys.unwrap().first; + if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && + std::equal(dks.begin(), dks.end(), key_curr)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + return false; +} + template result insert_nested_key(table& root, const toml::value& v, @@ -1026,19 +1124,26 @@ insert_nested_key(table& root, const toml::value& v, // show special err msg for conflicting table throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") cannot insert" - "ed"), get_region(tab->at(k)), "table already defined", - get_region(v), "this conflicts with the previous table")); + format_dotted_keys(first, last), + "\") cannot be defined"), { + {std::addressof(get_region(tab->at(k))), + "table already defined"}, + {std::addressof(get_region(v)), + "this conflicts with the previous table"} + })); } else if(!(tab->at(k).is(value_t::Array))) { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing value"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value already exists"), get_region(v), - "while inserting this array-of-tables")); + " existing value"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {std::addressof(get_region(v)), + "while inserting this array-of-tables"} + })); } array& a = tab->at(k).template cast(); if(!(a.front().is(value_t::Table))) @@ -1046,10 +1151,13 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing value"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value already exists"), get_region(v), - "while inserting this array-of-tables")); + " existing value"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {std::addressof(get_region(v)), + "while inserting this array-of-tables"} + })); } // avoid conflicting array of table like the following. // ```toml @@ -1071,10 +1179,13 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing array-of-tables"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value has static size"), get_region(v), - "appending this to the statically sized array")); + " existing array-of-tables"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value has static size")}, + {std::addressof(get_region(v)), + "appending it to the statically sized array"} + })); } a.push_back(v); return ok(true); @@ -1085,16 +1196,37 @@ insert_nested_key(table& root, const toml::value& v, tab->insert(std::make_pair(k, aot)); return ok(true); } - } + } // end if(array of table) + if(tab->count(k) == 1) { if(tab->at(k).is(value_t::Table) && v.is(value_t::Table)) { - throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: table (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "table already exists here", - get_region(v), "table defined twice")); + if(!is_valid_forward_table_definition( + tab->at(k), first, iter, last)) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: table (\"", + format_dotted_keys(first, last), + "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "table already exists here"}, + {std::addressof(get_region(v)), + "table defined twice"} + })); + } + // to allow the following toml file. + // [a.b.c] + // d = 42 + // [a] + // e = 2.71 + auto& t = tab->at(k).cast(); + for(const auto& kv : v.cast()) + { + t[kv.first] = kv.second; + } + detail::change_region(tab->at(k), key_reg); + return ok(true); } else if(v.is(value_t::Table) && tab->at(k).is(value_t::Array) && @@ -1103,18 +1235,23 @@ insert_nested_key(table& root, const toml::value& v, { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of tables (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "array of tables defined here", - get_region(v), "table conflicts with the previous array" - " of table")); + format_dotted_keys(first, last), "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "array of tables defined here"}, + {std::addressof(get_region(v)), + "table conflicts with the previous array of table"} + })); } else { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: value (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "value already exists here", - get_region(v), "value inserted twice")); + format_dotted_keys(first, last), "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "value already exists here"}, + {std::addressof(get_region(v)), + "value defined twice"} + })); } } tab->insert(std::make_pair(k, v)); @@ -1146,9 +1283,11 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), - get_region(a.back()), concat_to_string("actual type is ", - a.back().type()), get_region(v), "inserting this")); + ") is neither table nor an array of tables"), { + {std::addressof(get_region(a.back())), + concat_to_string("actual type is ", a.back().type())}, + {std::addressof(get_region(v)), "inserting this"} + })); } tab = std::addressof(a.back().template cast()); } @@ -1157,9 +1296,11 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), - get_region(tab->at(k)), concat_to_string("actual type is ", - tab->at(k).type()), get_region(v), "inserting this")); + ") is neither table nor an array of tables"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("actual type is ", tab->at(k).type())}, + {std::addressof(get_region(v)), "inserting this"} + })); } } } @@ -1174,8 +1315,8 @@ parse_inline_table(location& loc) table retval; if(!(loc.iter() != loc.end() && *loc.iter() == '{')) { - return err(format_underline("[error] toml::parse_inline_table: ", loc, - "the next token is not an inline table")); + return err(format_underline("[error] toml::parse_inline_table: ", + {{std::addressof(loc), "the next token is not an inline table"}})); } ++loc.iter(); // it starts from "{". it should be formatted as inline-table @@ -1221,13 +1362,14 @@ parse_inline_table(location& loc) { throw syntax_error(format_underline("[error] " "toml:::parse_inline_table: missing table separator `,` ", - loc, "should be `,`")); + {{std::addressof(loc), "should be `,`"}})); } } } loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_inline_table: " - "inline table did not closed by `}`", loc, "should be closed")); + "inline table did not closed by `}`", + {{std::addressof(loc), "should be closed"}})); } template @@ -1236,7 +1378,8 @@ result parse_value(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return err(format_underline("[error] toml::parse_value: input is empty", loc, "")); + return err(format_underline("[error] toml::parse_value: input is empty", + {{std::addressof(loc), ""}})); } if(auto r = parse_string (loc)) {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} @@ -1260,7 +1403,7 @@ result parse_value(location& loc) {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} const auto msg = format_underline("[error] toml::parse_value: " - "unknown token appeared", loc, "unknown"); + "unknown token appeared", {{std::addressof(loc), "unknown"}}); loc.iter() = first; return err(msg); } @@ -1277,7 +1420,8 @@ parse_table_key(location& loc) if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `[`", inner_loc, "should be `[`")); + "toml::parse_table_key: no `[`", + {{std::addressof(inner_loc), "should be `[`"}})); } // to skip [ a . b . c ] // ^----------- this whitespace @@ -1286,7 +1430,8 @@ parse_table_key(location& loc) if(!keys) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: invalid key", inner_loc, "not key")); + "toml::parse_table_key: invalid key", + {{std::addressof(inner_loc), "not key"}})); } // to skip [ a . b . c ] // ^-- this whitespace @@ -1295,7 +1440,8 @@ parse_table_key(location& loc) if(!close) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `]`", inner_loc, "should be `]`")); + "toml::parse_table_key: no `]`", + {{std::addressof(inner_loc), "should be `]`"}})); } // after [table.key], newline or EOF(empty table) requried. @@ -1308,7 +1454,7 @@ parse_table_key(location& loc) { throw syntax_error(format_underline("[error] " "toml::parse_table_key: newline required after [table.key]", - loc, "expected newline")); + {{std::addressof(loc), "expected newline"}})); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1331,23 +1477,24 @@ parse_array_table_key(location& loc) if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline("[error] " - "toml::parse_array_table_key: no `[[`", inner_loc, - "should be `[[`")); + "toml::parse_array_table_key: no `[[`", + {{std::addressof(inner_loc), "should be `[[`"}})); } lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); if(!keys) { throw internal_error(format_underline("[error] " - "toml::parse_array_table_key: invalid key", inner_loc, - "not key")); + "toml::parse_array_table_key: invalid key", + {{std::addressof(inner_loc), "not a key"}})); } lex_ws::invoke(inner_loc); const auto close = lex_array_table_close::invoke(inner_loc); if(!close) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `]]`", inner_loc, "should be `]]`")); + "toml::parse_table_key: no `]]`", + {{std::addressof(inner_loc), "should be `]]`"}})); } // after [[table.key]], newline or EOF(empty table) requried. @@ -1358,9 +1505,9 @@ parse_array_table_key(location& loc) const auto nl = lex_newline_after_table_key::invoke(loc); if(!nl) { - throw syntax_error(format_underline("[error] " - "toml::parse_array_table_key: newline required after " - "[[table.key]]", loc, "expected newline")); + throw syntax_error(format_underline("[error] toml::" + "parse_array_table_key: newline required after [[table.key]]", + {{std::addressof(loc), "expected newline"}})); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1436,8 +1583,8 @@ result parse_ml_table(location& loc) const auto before = loc.iter(); lex_ws::invoke(loc); // skip whitespace const auto msg = format_underline("[error] toml::parse_table: " - "invalid line format", loc, concat_to_string( - "expected newline, but got '", show_char(*loc.iter()), "'.")); + "invalid line format", {{std::addressof(loc), concat_to_string( + "expected newline, but got '", show_char(*loc.iter()), "'.")}}); loc.iter() = before; return err(msg); } @@ -1507,7 +1654,7 @@ result parse_toml_file(location& loc) continue; } return err(format_underline("[error]: toml::parse_toml_file: " - "unknown line appeared", loc, "unknown format")); + "unknown line appeared", {{std::addressof(loc), "unknown format"}})); } return ok(data); } diff --git a/toml/region.hpp b/toml/region.hpp index 7a0acba..db830c7 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_REGION_H -#define TOML11_REGION_H +#ifndef TOML11_REGION_HPP +#define TOML11_REGION_HPP #include "exception.hpp" #include #include @@ -28,44 +28,6 @@ inline std::string make_string(std::size_t len, char c) return std::string(len, c); } -// location in a container, normally in a file content. -// shared_ptr points the resource that the iter points. -// it can be used not only for resource handling, but also error message. -template -struct location -{ - static_assert(std::is_same::value,""); - using const_iterator = typename Container::const_iterator; - using source_ptr = std::shared_ptr; - - location(std::string name, Container cont) - : source_(std::make_shared(std::move(cont))), - source_name_(std::move(name)), iter_(source_->cbegin()) - {} - location(const location&) = default; - location(location&&) = default; - location& operator=(const location&) = default; - location& operator=(location&&) = default; - ~location() = default; - - const_iterator& iter() noexcept {return iter_;} - const_iterator iter() const noexcept {return iter_;} - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - std::string const& name() const noexcept {return source_name_;} - - private: - - source_ptr source_; - std::string source_name_; - const_iterator iter_; -}; - // region in a container, normally in a file content. // shared_ptr points the resource that the iter points. // combinators returns this. @@ -86,12 +48,89 @@ struct region_base virtual std::string line() const {return std::string("unknown line");} virtual std::string line_num() const {return std::string("?");} - virtual std::size_t before() const noexcept {return 0;} virtual std::size_t size() const noexcept {return 0;} virtual std::size_t after() const noexcept {return 0;} }; +// location in a container, normally in a file content. +// shared_ptr points the resource that the iter points. +// it can be used not only for resource handling, but also error message. +// +// it can be considered as a region that contains only one character. +template +struct location final : public region_base +{ + static_assert(std::is_same::value,""); + using const_iterator = typename Container::const_iterator; + using source_ptr = std::shared_ptr; + + location(std::string name, Container cont) + : source_(std::make_shared(std::move(cont))), + source_name_(std::move(name)), iter_(source_->cbegin()) + {} + location(const location&) = default; + location(location&&) = default; + location& operator=(const location&) = default; + location& operator=(location&&) = default; + ~location() = default; + + bool is_ok() const noexcept override {return static_cast(source_);} + + const_iterator& iter() noexcept {return iter_;} + const_iterator iter() const noexcept {return iter_;} + + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} + + std::string str() const override {return make_string(1, *this->iter());} + std::string name() const override {return source_name_;} + + std::string line_num() const override + { + return std::to_string(1+std::count(this->begin(), this->iter(), '\n')); + } + + std::string line() const override + { + return make_string(this->line_begin(), this->line_end()); + } + + const_iterator line_begin() const noexcept + { + using reverse_iterator = std::reverse_iterator; + return std::find(reverse_iterator(this->iter()), + reverse_iterator(this->begin()), '\n').base(); + } + const_iterator line_end() const noexcept + { + return std::find(this->iter(), this->end(), '\n'); + } + + // location is always points a character. so the size is 1. + std::size_t size() const noexcept override + { + return 1u; + } + std::size_t before() const noexcept override + { + return std::distance(this->line_begin(), this->iter()); + } + std::size_t after() const noexcept override + { + return std::distance(this->iter(), this->line_end()); + } + + source_ptr const& source() const& noexcept {return source_;} + source_ptr&& source() && noexcept {return std::move(source_);} + + private: + + source_ptr source_; + std::string source_name_; + const_iterator iter_; +}; + template struct region final : public region_base { @@ -200,96 +239,67 @@ struct region final : public region_base // to show a better error message. inline std::string format_underline(const std::string& message, - const region_base& reg, const std::string& comment_for_underline, + std::vector> reg_com, std::vector helps = {}) { + assert(!reg_com.empty()); + #ifdef _WIN32 const auto newline = "\r\n"; #else const char newline = '\n'; #endif - const auto line = reg.line(); - const auto line_number = reg.line_num(); - std::string retval; - retval += message; - retval += newline; - retval += " --> "; - retval += reg.name(); - retval += newline; - retval += ' '; - retval += line_number; - retval += " | "; - retval += line; - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - retval += make_string(reg.before(), ' '); - retval += make_string(reg.size(), '~'); - retval += ' '; - retval += comment_for_underline; - if(helps.size() != 0) - { - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - for(const auto help : helps) + const auto line_num_width = std::max_element(reg_com.begin(), reg_com.end(), + [](std::pair const& lhs, + std::pair const& rhs) { - retval += newline; - retval += "Hint: "; - retval += help; + return lhs.first->line_num().size() < rhs.first->line_num().size(); } - } - return retval; -} - -// to show a better error message. -inline std::string format_underline(const std::string& message, - const region_base& reg1, const std::string& comment_for_underline1, - const region_base& reg2, const std::string& comment_for_underline2, - std::vector helps = {}) -{ -#ifdef _WIN32 - const auto newline = "\r\n"; -#else - const char newline = '\n'; -#endif - const auto line1 = reg1.line(); - const auto line_number1 = reg1.line_num(); - const auto line2 = reg2.line(); - const auto line_number2 = reg2.line_num(); - const auto line_num_width = - std::max(line_number1.size(), line_number2.size()); + )->first->line_num().size(); std::ostringstream retval; retval << message << newline; - retval << " --> " << reg1.name() << newline; -// --------------------------------------- - retval << ' ' << std::setw(line_num_width) << line_number1; - retval << " | " << line1 << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - retval << make_string(reg1.before(), ' '); - retval << make_string(reg1.size(), '~'); - retval << ' '; - retval << comment_for_underline1 << newline; -// --------------------------------------- - if(reg2.name() != reg1.name()) + + for(std::size_t i=0; i " << reg2.name() << newline; + if(i!=0 && reg_com.at(i-1).first->name() == reg_com.at(i).first->name()) + { + retval << newline << " ..." << newline; + } + else + { + if(i != 0) {retval << newline;} + retval << " --> " << reg_com.at(i).first->name() << newline; + } + + const region_base* const reg = reg_com.at(i).first; + const std::string& comment = reg_com.at(i).second; + + + retval << ' ' << std::setw(line_num_width) << reg->line_num(); + retval << " | " << reg->line() << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | " << make_string(reg->before(), ' '); + + if(reg->size() == 1) + { + // invalid + // ^------ + retval << '^'; + retval << make_string(reg->after(), '-'); + } + else + { + // invalid + // ~~~~~~~ + retval << make_string(reg->size(), '~'); + } + + retval << ' '; + retval << comment; } - else - { - retval << " ..." << newline; - } - retval << ' ' << std::setw(line_num_width) << line_number2; - retval << " | " << line2 << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - retval << make_string(reg2.before(), ' '); - retval << make_string(reg2.size(), '~'); - retval << ' '; - retval << comment_for_underline2; + if(helps.size() != 0) { retval << newline; @@ -305,62 +315,6 @@ inline std::string format_underline(const std::string& message, return retval.str(); } - -// to show a better error message. -template -std::string -format_underline(const std::string& message, const location& loc, - const std::string& comment_for_underline, - std::vector helps = {}) -{ -#ifdef _WIN32 - const auto newline = "\r\n"; -#else - const char newline = '\n'; -#endif - using const_iterator = typename location::const_iterator; - using reverse_iterator = std::reverse_iterator; - const auto line_begin = std::find(reverse_iterator(loc.iter()), - reverse_iterator(loc.begin()), - '\n').base(); - const auto line_end = std::find(loc.iter(), loc.end(), '\n'); - - const auto line_number = std::to_string( - 1 + std::count(loc.begin(), loc.iter(), '\n')); - - std::string retval; - retval += message; - retval += newline; - retval += " --> "; - retval += loc.name(); - retval += newline; - retval += ' '; - retval += line_number; - retval += " | "; - retval += make_string(line_begin, line_end); - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - retval += make_string(std::distance(line_begin, loc.iter()),' '); - retval += '^'; - retval += make_string(std::distance(loc.iter(), line_end), '-'); - retval += ' '; - retval += comment_for_underline; - if(helps.size() != 0) - { - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - for(const auto help : helps) - { - retval += newline; - retval += "Hint: "; - retval += help; - } - } - return retval; -} - } // detail } // toml #endif// TOML11_REGION_H diff --git a/toml/result.hpp b/toml/result.hpp index 949b6f8..4930140 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_RESULT_H -#define TOML11_RESULT_H +#ifndef TOML11_RESULT_HPP +#define TOML11_RESULT_HPP #include "traits.hpp" #include #include diff --git a/toml/serializer.hpp b/toml/serializer.hpp index cb150bb..68bdbcc 100644 --- a/toml/serializer.hpp +++ b/toml/serializer.hpp @@ -5,6 +5,7 @@ #include "value.hpp" #include "lexer.hpp" #include +#include namespace toml { @@ -30,13 +31,12 @@ struct serializer } std::string operator()(const toml::floating f) const { - std::string token = [=] { - // every float value needs decimal point (or exponent). - std::ostringstream oss; - oss << std::setprecision(float_prec_) << std::showpoint << f; - return oss.str(); - }(); + const auto fmt = "%.*g"; + const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); + std::vector buf(bsz + 1, '\0'); // +1 for null character(\0) + std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); + std::string token(buf.begin(), std::prev(buf.end())); if(token.back() == '.') // 1. => 1.0 { token += '0'; diff --git a/toml/string.hpp b/toml/string.hpp index eef932d..89e6d39 100644 --- a/toml/string.hpp +++ b/toml/string.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_STRING_H -#define TOML11_STRING_H +#ifndef TOML11_STRING_HPP +#define TOML11_STRING_HPP #include #include diff --git a/toml/to_toml.hpp b/toml/to_toml.hpp deleted file mode 100644 index 348ce47..0000000 --- a/toml/to_toml.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TO_TOML -#define TOML11_TO_TOML -#include "value.hpp" - -namespace toml -{ - -template -inline value to_toml(T&& x) -{ - return value(std::forward(x)); -} - -template -inline value to_toml(T&& x, string_t kind) -{ - return value(std::forward(x), kind); -} - -inline value to_toml(local_date d, local_time t) -{ - return value(local_datetime(d, t)); -} -inline value to_toml(local_date d, local_time t, time_offset ofs) -{ - return value(offset_datetime(d, t, ofs)); -} - -template -inline value to_toml(Ts&& ... xs) -{ - return value(toml::array{toml::value(std::forward(xs)) ... }); -} - -inline value to_toml(std::initializer_list> xs) -{ - return value(toml::table(xs.begin(), xs.end())); -} - -} // toml -#endif // TOML11_TO_TOML diff --git a/toml/traits.hpp b/toml/traits.hpp index 79bb8b2..7f8f413 100644 --- a/toml/traits.hpp +++ b/toml/traits.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_TRAITS -#define TOML11_TRAITS +#ifndef TOML11_TRAITS_HPP +#define TOML11_TRAITS_HPP #include #include #include @@ -9,6 +9,9 @@ namespace toml { + +class value; // forward decl + namespace detail { @@ -45,6 +48,22 @@ struct has_resize_method_impl template static std::false_type check(...); }; +struct has_from_toml_method_impl +{ + template + static std::true_type check( + decltype(std::declval().from_toml(std::declval<::toml::value>()))*); + template + static std::false_type check(...); +}; +struct has_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().into_toml())*); + template + static std::false_type check(...); +}; + /// Intel C++ compiler can not use decltype in parent class declaration, here /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 #ifdef __INTEL_COMPILER @@ -62,6 +81,14 @@ struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; template struct has_resize_method : decltype(has_resize_method_impl::check(nullptr)){}; + +template +struct has_from_toml_method +: decltype(has_from_toml_method_impl::check(nullptr)){}; +template +struct has_into_toml_method +: decltype(has_into_toml_method_impl::check(nullptr)){}; + #ifdef __INTEL_COMPILER #undef decltype(...) #endif diff --git a/toml/types.hpp b/toml/types.hpp index 361ed10..db93320 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_TYPES_H -#define TOML11_TYPES_H +#ifndef TOML11_TYPES_HPP +#define TOML11_TYPES_HPP #include "datetime.hpp" #include "string.hpp" #include "traits.hpp" diff --git a/toml/utility.hpp b/toml/utility.hpp index 6202163..01fe6d7 100644 --- a/toml/utility.hpp +++ b/toml/utility.hpp @@ -1,12 +1,22 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_UTILITY -#define TOML11_UTILITY +#ifndef TOML11_UTILITY_HPP +#define TOML11_UTILITY_HPP #include "traits.hpp" #include #include #include +#if __cplusplus >= 201402L +# define TOML11_MARK_AS_DEPRECATED [[deprecated]] +#elif defined(__GNUC__) +# define TOML11_MARK_AS_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define TOML11_MARK_AS_DEPRECATED __declspec(deprecated) +#else +# define TOML11_MARK_AS_DEPRECATED +#endif + namespace toml { diff --git a/toml/value.hpp b/toml/value.hpp index d6f5f0e..739f676 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1,8 +1,9 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_VALUE -#define TOML11_VALUE +#ifndef TOML11_VALUE_HPP +#define TOML11_VALUE_HPP #include "traits.hpp" +#include "into.hpp" #include "utility.hpp" #include "exception.hpp" #include "storage.hpp" @@ -21,6 +22,8 @@ namespace detail { // to show error messages. not recommended for users. region_base const& get_region(const value&); +template +void change_region(value&, Region&&); }// detail template @@ -531,10 +534,62 @@ class value return *this; } + // user-defined ========================================================= + + // convert using into_toml() method ------------------------------------- + + template>, // not a toml::value + detail::has_into_toml_method // but has `into_toml` method + >::value, std::nullptr_t>::type = nullptr> + value(const T& ud): value(ud.into_toml()) {} + + template>, // not a toml::value + detail::has_into_toml_method // but has `into_toml` method + >::value, std::nullptr_t>::type = nullptr> + value& operator=(const T& ud) + { + *this = ud.into_toml(); + return *this; + } + + // convert using into struct ----------------------------------------- + + template>::value, + std::nullptr_t>::type = nullptr, + std::size_t S = sizeof(::toml::into)> + value(const T& ud): value(::toml::into::into_toml(ud)) {} + + template>::value, + std::nullptr_t>::type = nullptr, + std::size_t S = sizeof(::toml::into)> + value& operator=(const T& ud) + { + *this = ::toml::into::into_toml(ud); + return *this; + } + + // type checking and casting ============================================ + template bool is() const noexcept {return value_traits::type_index == this->type_;} bool is(value_t t) const noexcept {return t == this->type_;} + bool is_uninitialized() const noexcept {return this->is(value_t::Empty );} + bool is_boolean() const noexcept {return this->is(value_t::Boolean );} + bool is_integer() const noexcept {return this->is(value_t::Integer );} + bool is_float() const noexcept {return this->is(value_t::Float );} + bool is_string() const noexcept {return this->is(value_t::String );} + bool is_offset_datetime() const noexcept {return this->is(value_t::OffsetDatetime);} + bool is_local_datetime() const noexcept {return this->is(value_t::LocalDatetime );} + bool is_local_date() const noexcept {return this->is(value_t::LocalDate );} + bool is_local_time() const noexcept {return this->is(value_t::LocalTime );} + 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 {return type_;} template @@ -560,6 +615,9 @@ class value // for error messages friend region_base const& detail::get_region(const value&); + template + friend void detail::change_region(value&, Region&&); + template struct switch_cast; @@ -594,6 +652,20 @@ inline region_base const& get_region(const value& v) { return *(v.region_info_); } + +template +void change_region(value& v, Region&& reg) +{ + using region_type = typename std::remove_reference< + typename std::remove_cv::type + >::type; + + std::shared_ptr new_reg = + std::make_shared(std::forward(reg)); + v.region_info_ = new_reg; + return; +} + }// detail template<> struct value::switch_cast @@ -662,9 +734,11 @@ typename detail::toml_default_type::type& value::cast() & { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(*this); } @@ -673,9 +747,11 @@ typename detail::toml_default_type::type const& value::cast() const& { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(*this); } @@ -684,9 +760,11 @@ typename detail::toml_default_type::type&& value::cast() && { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(std::move(*this)); } @@ -769,22 +847,38 @@ inline bool operator>=(const toml::value& lhs, const toml::value& rhs) return !(lhs < rhs); } -inline std::string format_error(const std::string& err_msg, - const toml::value& v, const std::string& comment, - std::vector hints = {}) +namespace detail { - return detail::format_underline(err_msg, detail::get_region(v), comment, - std::move(hints)); +inline std::string format_error_impl(const std::string& err_msg, + std::vector> val, + std::vector hints) +{ + return format_underline(err_msg, std::move(val), std::move(hints)); +} +inline std::string format_error_impl(const std::string& err_msg, + std::vector> val) +{ + return format_underline(err_msg, std::move(val)); } -inline std::string format_error(const std::string& err_msg, - const toml::value& v1, const std::string& comment1, - const toml::value& v2, const std::string& comment2, - std::vector hints = {}) +template +std::string format_error_impl(const std::string& err_msg, + std::vector> val, + const toml::value& v, const std::string& comment, + Ts&& ... args) { - return detail::format_underline(err_msg, detail::get_region(v1), comment1, - detail::get_region(v2), comment2, - std::move(hints)); + val.push_back(std::make_pair(std::addressof(get_region(v)), comment)); + return format_error_impl(err_msg, std::move(val), std::forward(args)...); +} +} // detail + +template +std::string format_error(const std::string& err_msg, Ts&& ... args) +{ + std::vector> val; + val.reserve(sizeof...(args) / 2); + return detail::format_error_impl(err_msg, std::move(val), + std::forward(args)...); } template