From 4c12dad51fb1a99b25be111dd5b156c56ae1c7cc Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 3 Oct 2019 15:27:25 +0900 Subject: [PATCH 1/4] feat: add find(value, idx) for arrays (#79) --- toml/get.hpp | 136 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index 16516c1..4cdbe81 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -465,6 +465,52 @@ basic_value find(basic_value&& v, const key& ky) return basic_value(std::move(tab.at(ky))); } +// ---------------------------------------------------------------------------- +// find(value, idx) +template class M, template class V> +basic_value const& +find(const basic_value& v, const std::size_t idx) +{ + const auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] index ", idx, " is out of range"), { + {std::addressof(detail::get_region(v)), "in this array"} + })); + } + return ary.at(idx); +} +template class M, template class V> +basic_value& find(basic_value& v, const std::size_t idx) +{ + auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] index ", idx, " is out of range"), { + {std::addressof(detail::get_region(v)), "in this array"} + })); + } + return ary.at(idx); +} +template class M, template class V> +basic_value find(basic_value&& v, const std::size_t idx) +{ + typename basic_value::array_type ary = std::move(v).as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] index ", idx, " is out of range"), { + {std::addressof(detail::get_region(v)), "in this array"} + })); + } + return basic_value(std::move(ary.at(idx))); +} + // ---------------------------------------------------------------------------- // find(value, key); @@ -516,57 +562,111 @@ find(basic_value&& v, const key& ky) return ::toml::get(std::move(tab.at(ky))); } +// ---------------------------------------------------------------------------- +// find(value, idx) +template class M, template class V> +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const std::size_t idx) +{ + const auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] index ", idx, " is out of range"), { + {std::addressof(detail::get_region(v)), "in this array"} + })); + } + return ::toml::get(ary.at(idx)); +} +template class M, template class V> +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const std::size_t idx) +{ + auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] index ", idx, " is out of range"), { + {std::addressof(detail::get_region(v)), "in this array"} + })); + } + return ::toml::get(ary.at(idx)); +} +template class M, template class V> +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const std::size_t idx) +{ + typename basic_value::array_type ary = std::move(v).as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] index ", idx, " is out of range"), { + {std::addressof(detail::get_region(v)), "in this array"} + })); + } + return ::toml::get(std::move(ary.at(idx))); +} + // -------------------------------------------------------------------------- // toml::find(toml::value, toml::key, Ts&& ... keys) template class M, template class V, - typename ... Ts> + typename Key1, typename Key2, typename ... Keys> const basic_value& -find(const basic_value& v, const ::toml::key& ky, Ts&& ... keys) +find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { - return ::toml::find(::toml::find(v, ky), std::forward(keys)...); + return ::toml::find(::toml::find(v, std::forward(k1)), + std::forward(k2), std::forward(keys)...); } template class M, template class V, - typename ... Ts> + typename Key1, typename Key2, typename ... Keys> basic_value& -find(basic_value& v, const ::toml::key& ky, Ts&& ... keys) +find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { - return ::toml::find(::toml::find(v, ky), std::forward(keys)...); + return ::toml::find(::toml::find(v, std::forward(k1)), + std::forward(k2), std::forward(keys)...); } template class M, template class V, - typename ... Ts> + typename Key1, typename Key2, typename ... Keys> basic_value -find(basic_value&& v, const ::toml::key& ky, Ts&& ... keys) +find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { - return ::toml::find(::toml::find(std::move(v), ky), std::forward(keys)...); + return ::toml::find(::toml::find(std::move(v), std::forward(k1)), + std::forward(k2), std::forward(keys)...); } template class M, template class V, - typename ... Ts> + typename Key1, typename Key2, typename ... Keys> decltype(::toml::get(std::declval&>())) -find(const basic_value& v, const ::toml::key& ky, Ts&& ... keys) +find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { - return ::toml::find(::toml::find(v, ky), std::forward(keys)...); + return ::toml::find(::toml::find(v, std::forward(k1)), + std::forward(k2), std::forward(keys)...); } template class M, template class V, - typename ... Ts> + typename Key1, typename Key2, typename ... Keys> decltype(::toml::get(std::declval&>())) -find(basic_value& v, const ::toml::key& ky, Ts&& ... keys) +find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { - return ::toml::find(::toml::find(v, ky), std::forward(keys)...); + return ::toml::find(::toml::find(v, std::forward(k1)), + std::forward(k2), std::forward(keys)...); } template class M, template class V, - typename ... Ts> + typename Key1, typename Key2, typename ... Keys> decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const ::toml::key& ky, Ts&& ... keys) +find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { - return ::toml::find(::toml::find(std::move(v), ky), std::forward(keys)...); + return ::toml::find(::toml::find(std::move(v), std::forward(k1)), + std::forward(k2), std::forward(keys)...); } // ============================================================================ From 17d78553ff0f92ee2c340acdd1ce009e6108d993 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 3 Oct 2019 15:48:04 +0900 Subject: [PATCH 2/4] test: add test cases for find(v, idx) - check whether find(v, idx) throws - check find(v, ks...) works with both indices and strings --- tests/test_find.cpp | 119 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/tests/test_find.cpp b/tests/test_find.cpp index 63c1d78..f0c664b 100644 --- a/tests/test_find.cpp +++ b/tests/test_find.cpp @@ -101,6 +101,78 @@ BOOST_AUTO_TEST_CASE(test_find_throws) } } +BOOST_AUTO_TEST_CASE(test_find_array_throws) +{ + // ----------------------------------------------------------------------- + // const-reference version + { + // value is not an array + const toml::value v(true); + BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); + } + { + // the value corresponding to the key is not the expected type + const toml::value v{1, 2, 3, 4, 5}; + BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); + } + { + // the value corresponding to the key is not found + const toml::value v{1, 2, 3, 4, 5}; + BOOST_CHECK_THROW(toml::find(v, 6), std::out_of_range); + } + { + // the positive control. + const toml::value v{1, 2, 3, 4, 5}; + BOOST_TEST(3 == toml::find(v, 2)); + } + + // ----------------------------------------------------------------------- + // non-const reference version + { + // value is not an array + toml::value v(true); + BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); + } + { + // the value corresponding to the key is not the expected type + toml::value v{1, 2, 3, 4, 5}; + BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); + } + { + // the value corresponding to the key is not found + toml::value v{1, 2, 3, 4, 5}; + BOOST_CHECK_THROW(toml::find(v, 6), std::out_of_range); + } + { + // the positive control. + toml::value v{1, 2, 3, 4, 5}; + BOOST_TEST(3 == toml::find(v, 2)); + } + + // ----------------------------------------------------------------------- + // move version + { + // value is not an array + toml::value v(true); + BOOST_CHECK_THROW(toml::find(std::move(v), 0), toml::type_error); + } + { + // the value corresponding to the key is not the expected type + toml::value v{1, 2, 3, 4, 5}; + BOOST_CHECK_THROW(toml::find(std::move(v), 0), toml::type_error); + } + { + // the value corresponding to the key is not found + toml::value v{1, 2, 3, 4, 5}; + BOOST_CHECK_THROW(toml::find(std::move(v), 6), std::out_of_range); + } + { + // the positive control. + toml::value v{1, 2, 3, 4, 5}; + BOOST_TEST(3 == toml::find(std::move(v), 2)); + } +} + BOOST_AUTO_TEST_CASE(test_find_recursive) { // recursively search tables @@ -129,6 +201,53 @@ BOOST_AUTO_TEST_CASE(test_find_recursive) auto num3 = toml::find(std::move(v), a, b, c, d); BOOST_TEST(42 == num3); } + // recursively search arrays + { + toml::value v{ + toml::array{"array", "of", "string"}, + toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}} + }; + BOOST_TEST("array" == toml::find(v, 0, 0)); + BOOST_TEST("of" == toml::find(v, 0, 1)); + BOOST_TEST("string" == toml::find(v, 0, 2)); + + BOOST_TEST(1 == toml::find(v, 1, 0, 0)); + BOOST_TEST(2 == toml::find(v, 1, 0, 1)); + BOOST_TEST(3 == toml::find(v, 1, 0, 2)); + + BOOST_TEST(3.14 == toml::find(v, 1, 1, 0)); + BOOST_TEST(2.71 == toml::find(v, 1, 1, 1)); + + // reference that can be used to modify the content + auto& num = toml::find(v, 1, 0, 2); + num = 42; + BOOST_TEST( 1 == toml::find(v, 1, 0, 0)); + BOOST_TEST( 2 == toml::find(v, 1, 0, 1)); + BOOST_TEST(42 == toml::find(v, 1, 0, 2)); + + // move value + auto num2 = toml::find(std::move(v), 1, 0, 2); + BOOST_TEST(42 == num2); + } + // recursively search mixtures + { + toml::value v = toml::table{{"array", toml::array{ + toml::array{1, 2, 3}, + toml::array{ + toml::table{{"foo", "bar"}, {"baz", "qux"}}, + toml::table{{"pi", 3.14}, {"e", 2.71}} + }} + }}; + BOOST_TEST(1 == toml::find(v, "array", 0, 0)); + BOOST_TEST(2 == toml::find(v, "array", 0, 1)); + BOOST_TEST(3 == toml::find(v, "array", 0, 2)); + + BOOST_TEST("bar" == toml::find(v, "array", 1, 0, "foo")); + BOOST_TEST("qux" == toml::find(v, "array", 1, 0, "baz")); + + BOOST_TEST(3.14 == toml::find(v, "array", 1, 1, "pi")); + BOOST_TEST(2.71 == toml::find(v, "array", 1, 1, "e")); + } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types) From 7f020f3f44660fdd6eb9fc16ba5a609c97b88f0d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 7 Jan 2020 22:27:13 +0900 Subject: [PATCH 3/4] refactor: remove error prefix that will automatically be added in format_underline --- toml/get.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index ea2a2cf..e34502c 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -476,7 +476,7 @@ find(const basic_value& v, const std::size_t idx) if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] index ", idx, " is out of range"), { + "index ", idx, " is out of range"), { {std::addressof(detail::get_region(v)), "in this array"} })); } @@ -490,7 +490,7 @@ basic_value& find(basic_value& v, const std::size_t idx) if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] index ", idx, " is out of range"), { + "index ", idx, " is out of range"), { {std::addressof(detail::get_region(v)), "in this array"} })); } @@ -500,11 +500,11 @@ template class M, template class V> basic_value find(basic_value&& v, const std::size_t idx) { - typename basic_value::array_type ary = std::move(v).as_array(); + auto& ary = v.as_array(); if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] index ", idx, " is out of range"), { + "index ", idx, " is out of range"), { {std::addressof(detail::get_region(v)), "in this array"} })); } @@ -573,7 +573,7 @@ find(const basic_value& v, const std::size_t idx) if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] index ", idx, " is out of range"), { + "index ", idx, " is out of range"), { {std::addressof(detail::get_region(v)), "in this array"} })); } @@ -588,7 +588,7 @@ find(basic_value& v, const std::size_t idx) if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] index ", idx, " is out of range"), { + "index ", idx, " is out of range"), { {std::addressof(detail::get_region(v)), "in this array"} })); } @@ -603,7 +603,7 @@ find(basic_value&& v, const std::size_t idx) if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] index ", idx, " is out of range"), { + "index ", idx, " is out of range"), { {std::addressof(detail::get_region(v)), "in this array"} })); } From aa6271af75233d05b5b421b3a764240b170c5c2d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 9 Jan 2020 01:40:05 +0900 Subject: [PATCH 4/4] doc: update README --- README.md | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 37b75d0..4a0c52d 100644 --- a/README.md +++ b/README.md @@ -274,10 +274,31 @@ const auto color = toml::find(data, "fruit", "physical", "color"); const auto shape = toml::find(data, "fruit", "physical", "shape"); ``` +### Finding a value in an array + +You can find n-th value in an array by `toml::find`. + +```toml +values = ["foo", "bar", "baz"] +``` + +``` cpp +const auto data = toml::parse("sample.toml"); +const auto values = toml::find(data, "values"); +const auto bar = toml::find(values, 1); +``` + +`toml::find` can also search array recursively. + +```cpp +const auto data = toml::parse("fruit.toml"); +const auto bar = toml::find(data, "values", 1); +``` + ### In case of error -If the value does not exist, `toml::find` throws an error with the location of -the table. +If the value does not exist, `toml::find` throws `std::out_of_range` with the +location of the table. ```console terminate called after throwing an instance of 'std::out_of_range' @@ -287,11 +308,6 @@ terminate called after throwing an instance of 'std::out_of_range' | ~~~~~ in this table ``` -**Note**: It is recommended to find a table as `toml::value` because it has much information -compared to `toml::table`, which is an alias of -`std::unordered_map`. Since `toml::table` does not have -any information about toml file, such as where the table was defined in the file. - ---- If the specified type differs from the actual value contained, it throws