diff --git a/README.md b/README.md index b7a6d58..5072dc9 100644 --- a/README.md +++ b/README.md @@ -451,58 +451,20 @@ const auto value = toml::expect(data.at("number")) ## Finding a value from a table -toml11 provides utility function to find a value from `toml::value` and `toml::table`. +toml11 provides a utility function to find a value from `toml::value` and `toml::table`. ```cpp -const auto data = toml::parse("example.toml"); +const toml::value data = /* ... */; + // find a value named "num" from `data`. -const auto num = toml::find(data, "num"); +const auto num = toml::find(data, "num"); ``` -If the value does not exist, it throws `std::out_of_range` with an error message. -But, since `toml::table` is just an alias of `std::unordered_map`, -you need to pass a name to the function to show the name in the exception. +When you pass a `toml::value`, `toml::find` first casts it to `toml::table`. +If casting failed, `toml::type_error` will be thrown. -```cpp -const auto num = toml::find(data, "num", "example.toml"); -``` - -```console -terminate called after throwing an instance of 'std::out_of_range' - what(): [error] key "num" not found in example.toml -# ^^^^^^^^^^^^ this part -``` - -Of course, you can do this in your own way with `toml::get` because -it just searches an `unordered_map` and returns a value if it exists. - -```cpp -const toml::table data = toml::parse("example.toml"); -if(data.count("num") == 1) -{ - const auto num = toml::get(data.at("num")); - // more stuff ... -} -``` - ----- - -You can also use this with a `toml::value` that is expected to contain a `toml::table`. -It automatically casts the `toml::value` to a `toml::table`. If it failed to cast, -it would throw a `toml::type_error`. - -```cpp -// # expecting the following example.toml -// [table] -// num = 42 -const toml::table data = toml::parse("example.toml"); -const toml::value table = data.at("table"); -const auto num = toml::find(table, "num"); -``` - -In this case, because the `toml::value table` knows the locatoin of itself, -you don't need to pass the name to show it in an error message. -`toml::find` will automatically format an error message with the location of the table. +When the value does not exist, it throws `std::out_of_range` with an error message. +By passing a `toml::value`, it shows an informative error message like the following. ```console terminate called after throwing an instance of 'std::out_of_range' @@ -512,54 +474,35 @@ terminate called after throwing an instance of 'std::out_of_range' | ~~~~~~~ in this table ``` -The default return value of the `toml::find` is a `toml::value`. +Contrary, since `toml::table` is just an alias of `std::unordered_map`, +you need to pass a name to the function to show the name in the exception with `toml::table`. + +```cpp +const toml::table data = /* ... */; + +// you need to pass the name of the table to show it in an error message +const auto num = toml::find(data, "num", "[data]"); +``` + +```console +terminate called after throwing an instance of 'std::out_of_range' + what(): [error] key "num" not found in [data] +# table name is needed to show this part ^^^^^^ +``` + +By default (w/o template parameter), `toml::find` returns a `toml::value`. ```cpp const toml::value& subtable = toml::find(table, "subtable"); ``` -There are several ways to find a value buried in a deep recursion of tables. - -First, you can call `toml::find` as many as you need. - -```cpp -// # expecting the following example.toml -// answer.to.the.ultimate.question = 42 -// # is equivalent to {"answer": {"to":{"the":{"ultimate:{"question":42}}}}} - -const toml::table data = toml::parse("example.toml"); -const int a = toml::find(toml::find(toml::find(toml::find(toml::find( - data, "answer"), "to"), "the"), "ultimate"), "question"); -``` - -But it is a bother. - -After toml11 v2.4.0, you can pass a `toml::value` and as many number of keys as you want. - -```cpp -const toml::table data = toml::parse("example.toml"); -const int a = toml::find(data.at("answer"), "to", "the", "ultimate", "question"); -``` - __NOTE__: -Currently, this function does not support `toml::table` because of some -technical reason. Please make sure that the type of the first argument is -`toml::value`. The main reason is that the `toml::table` may take an additional string -as the third argumnet to show its location in an error message. And the -most confusing part is that `toml::parse` returns `toml::table`, not a -`toml::value`. This confusing API will hopefully be resolved in the next -major update, v3 (it will contain some unavoidable breaking changes). - ----- - -There is another utility function, `toml::find_or`. -It is almost same as `toml::find`, but returns a default value if the value is -not found or has a different type, like `toml::get_or`. - -```cpp -const auto data = toml::parse("example.toml"); -const auto num = toml::find_or(data.at("table"), "num", 42); -``` +A new feature, recursive toml::find was planned to be introduced, but it was +found that the change breaks a code that was previously compiled fine. So the +change was reverted. +The reason is that the overload resolution was ambiguous. To support this, +in the next major update, overloads of `toml::find` for `toml::table` possibly +be removed. ## Checking value type diff --git a/tests/test_get_related_func.cpp b/tests/test_get_related_func.cpp index e6e448c..30eb362 100644 --- a/tests/test_get_related_func.cpp +++ b/tests/test_get_related_func.cpp @@ -12,9 +12,8 @@ #include #include -BOOST_AUTO_TEST_CASE(test_find_for_value) +BOOST_AUTO_TEST_CASE(test_find) { - // value itself is not a table { toml::value v(true); bool thrown = false; @@ -28,87 +27,22 @@ BOOST_AUTO_TEST_CASE(test_find_for_value) } BOOST_CHECK(thrown); } - // the value corresponding to the key is not the expected type + { - toml::value v{{"key", 42}}; - bool thrown = false; - try - { - toml::find(v, "key"); - } - catch(toml::type_error const& te) - { - thrown = true; - } - BOOST_CHECK(thrown); + toml::table v{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::find(v, "num")); + toml::find(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::find(v, "num")); } { toml::value v = toml::table{{"num", 42}}; BOOST_CHECK_EQUAL(42, toml::find(v, "num")); - - // reference that can be used to modify the content - auto& num = toml::find(v, "num"); - num = 54; + toml::find(v, "num") = 54; BOOST_CHECK_EQUAL(54, toml::find(v, "num")); } - - // recursively search tables - { - toml::value v = toml::table{ - {"a", toml::table{ - {"b", toml::table{ - {"c", toml::table{ - {"d", 42} - }} - }} - }} - }; - BOOST_CHECK_EQUAL(42, toml::find(v, "a", "b", "c", "d")); - - // reference that can be used to modify the content - auto& num = toml::find(v, "a", "b", "c", "d"); - num = 54; - BOOST_CHECK_EQUAL(54, toml::find(v, "a", "b", "c", "d")); - - const std::string a("a"), b("b"), c("c"), d("d"); - auto& num2 = toml::find(v, a, b, c, d); - num2 = 42; - BOOST_CHECK_EQUAL(42, toml::find(v, a, b, c, d)); - } } -BOOST_AUTO_TEST_CASE(test_find_for_table) -{ - // the value corresponding to the key is not the expected type - { - toml::table v{{"key", 42}}; - bool thrown = false; - try - { - toml::find(v, "key"); - } - catch(toml::type_error const& te) - { - thrown = true; - } - BOOST_CHECK(thrown); - } - - { - toml::table v{{"num", 42}}; - BOOST_CHECK_EQUAL(42, toml::find(v, "num")); - - // reference that can be used to modify the content - auto& num = toml::find(v, "num"); - num = 54; - BOOST_CHECK_EQUAL(54, toml::find(v, "num")); - } - - // recursive search is not provided for tables. -} - - BOOST_AUTO_TEST_CASE(test_get_or) { // requires conversion int -> uint diff --git a/toml/get.hpp b/toml/get.hpp index fb2f403..b3bc337 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -430,32 +430,6 @@ find(toml::value&& v, const toml::key& ky) return ::toml::get(std::move(tab[ky])); } -// -------------------------------------------------------------------------- -// toml::find(toml::value, toml::key, Ts&& ... keys) -// -// Note: C++ draft N3337 (14.1.11) says that... -// > If a template-parameter of a class template or alias template has a default -// > template-argument, each subsequent template-parameter shall either have a -// > default template-argument supplied or be a template parameter pack. -// So the template parameter pack can appear after a default template argument. -template -decltype(::toml::get(std::declval())) -find(const ::toml::value& v, const ::toml::key& ky, Ts&& ... keys) -{ - return ::toml::find(::toml::find(v, ky), std::forward(keys)...); -} -template -decltype(::toml::get(std::declval<::toml::value&>())) -find(::toml::value& v, const ::toml::key& ky, Ts&& ... keys) -{ - return ::toml::find(::toml::find(v, ky), std::forward(keys)...); -} -template -decltype(::toml::get(std::declval<::toml::value&&>())) -find(::toml::value&& v, const ::toml::key& ky, Ts&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), ky), std::forward(keys)...); -} // ============================================================================ // get_or(value, fallback)