Merge branch 'revert-recursive-find'

This commit is contained in:
ToruNiina
2019-06-17 11:54:52 +09:00
3 changed files with 38 additions and 187 deletions

119
README.md
View File

@@ -451,58 +451,20 @@ const auto value = toml::expect<int>(data.at("number"))
## Finding a value from a table ## 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 ```cpp
const auto data = toml::parse("example.toml"); const toml::value data = /* ... */;
// find a value named "num" from `data`. // find a value named "num" from `data`.
const auto num = toml::find<int>(data, "num"); const auto num = toml::find<int>(data, "num");
``` ```
If the value does not exist, it throws `std::out_of_range` with an error message. When you pass a `toml::value`, `toml::find` first casts it to `toml::table`.
But, since `toml::table` is just an alias of `std::unordered_map<toml::key, toml::value>`, If casting failed, `toml::type_error` will be thrown.
you need to pass a name to the function to show the name in the exception.
```cpp When the value does not exist, it throws `std::out_of_range` with an error message.
const auto num = toml::find<int>(data, "num", "example.toml"); 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'
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<int>(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<int>(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.
```console ```console
terminate called after throwing an instance of 'std::out_of_range' 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 | ~~~~~~~ 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<toml::key, toml::value>`,
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<int>(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 ```cpp
const toml::value& subtable = toml::find(table, "subtable"); 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<int>(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<int>(data.at("answer"), "to", "the", "ultimate", "question");
```
__NOTE__: __NOTE__:
Currently, this function does not support `toml::table` because of some A new feature, recursive toml::find was planned to be introduced, but it was
technical reason. Please make sure that the type of the first argument is found that the change breaks a code that was previously compiled fine. So the
`toml::value`. The main reason is that the `toml::table` may take an additional string change was reverted.
as the third argumnet to show its location in an error message. And the The reason is that the overload resolution was ambiguous. To support this,
most confusing part is that `toml::parse` returns `toml::table`, not a in the next major update, overloads of `toml::find` for `toml::table` possibly
`toml::value`. This confusing API will hopefully be resolved in the next be removed.
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);
```
## Checking value type ## Checking value type

View File

@@ -12,9 +12,8 @@
#include <deque> #include <deque>
#include <array> #include <array>
BOOST_AUTO_TEST_CASE(test_find_for_value) BOOST_AUTO_TEST_CASE(test_find)
{ {
// value itself is not a table
{ {
toml::value v(true); toml::value v(true);
bool thrown = false; bool thrown = false;
@@ -28,87 +27,22 @@ BOOST_AUTO_TEST_CASE(test_find_for_value)
} }
BOOST_CHECK(thrown); BOOST_CHECK(thrown);
} }
// the value corresponding to the key is not the expected type
{ {
toml::value v{{"key", 42}}; toml::table v{{"num", 42}};
bool thrown = false; BOOST_CHECK_EQUAL(42, toml::find<int>(v, "num"));
try toml::find<toml::integer>(v, "num") = 54;
{ BOOST_CHECK_EQUAL(54, toml::find<int>(v, "num"));
toml::find<toml::boolean>(v, "key");
}
catch(toml::type_error const& te)
{
thrown = true;
}
BOOST_CHECK(thrown);
} }
{ {
toml::value v = toml::table{{"num", 42}}; toml::value v = toml::table{{"num", 42}};
BOOST_CHECK_EQUAL(42, toml::find<int>(v, "num")); BOOST_CHECK_EQUAL(42, toml::find<int>(v, "num"));
toml::find<toml::integer>(v, "num") = 54;
// reference that can be used to modify the content
auto& num = toml::find<toml::integer>(v, "num");
num = 54;
BOOST_CHECK_EQUAL(54, toml::find<int>(v, "num")); BOOST_CHECK_EQUAL(54, toml::find<int>(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<int>(v, "a", "b", "c", "d"));
// reference that can be used to modify the content
auto& num = toml::find<toml::integer>(v, "a", "b", "c", "d");
num = 54;
BOOST_CHECK_EQUAL(54, toml::find<int>(v, "a", "b", "c", "d"));
const std::string a("a"), b("b"), c("c"), d("d");
auto& num2 = toml::find<toml::integer>(v, a, b, c, d);
num2 = 42;
BOOST_CHECK_EQUAL(42, toml::find<int>(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<toml::boolean>(v, "key");
}
catch(toml::type_error const& te)
{
thrown = true;
}
BOOST_CHECK(thrown);
}
{
toml::table v{{"num", 42}};
BOOST_CHECK_EQUAL(42, toml::find<int>(v, "num"));
// reference that can be used to modify the content
auto& num = toml::find<toml::integer>(v, "num");
num = 54;
BOOST_CHECK_EQUAL(54, toml::find<int>(v, "num"));
}
// recursive search is not provided for tables.
}
BOOST_AUTO_TEST_CASE(test_get_or) BOOST_AUTO_TEST_CASE(test_get_or)
{ {
// requires conversion int -> uint // requires conversion int -> uint

View File

@@ -430,32 +430,6 @@ find(toml::value&& v, const toml::key& ky)
return ::toml::get<T>(std::move(tab[ky])); return ::toml::get<T>(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<typename T = ::toml::value, typename ... Ts>
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
find(const ::toml::value& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename T = ::toml::value, typename ... Ts>
decltype(::toml::get<T>(std::declval<::toml::value&>()))
find(::toml::value& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename T = ::toml::value, typename ... Ts>
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
find(::toml::value&& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
}
// ============================================================================ // ============================================================================
// get_or(value, fallback) // get_or(value, fallback)