mirror of
https://github.com/ToruNiina/toml11.git
synced 2024-11-09 22:30:07 +00:00
⏪ revert recursive find function
I found that in a user-code (I'm also one of the users of this library), this new feature sometimes causes an error. Some of my code won't compile because of this change. Since toml::table is convertible to toml::value *implicitly*, if toml::find(table, key, tablename) was called, the overload resolution becomes ambiguous with toml::find( value, key1, key2). But dropping support for toml::find(toml::table, key, tablename) is a breaking change. So I concluded that now is not the right time yet.
This commit is contained in:
parent
b2daf916b3
commit
cbaaaaca7c
86
README.md
86
README.md
@ -451,58 +451,36 @@ 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 utility function to find a value from `toml::table`.
|
||||||
|
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
|
```cpp
|
||||||
const auto data = toml::parse("example.toml");
|
const auto data = toml::parse("example.toml");
|
||||||
// find a value named "num" from `data`.
|
const auto num = toml::find<int>(data, "num", /*for err msg*/"example.toml");
|
||||||
const auto num = toml::find<int>(data, "num");
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If the value does not exist, it throws `std::out_of_range` with an error message.
|
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<toml::key, toml::value>`,
|
|
||||||
you need to pass a name to the function to show the name in the exception.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
const auto num = toml::find<int>(data, "num", "example.toml");
|
|
||||||
```
|
|
||||||
|
|
||||||
```console
|
```console
|
||||||
terminate called after throwing an instance of 'std::out_of_range'
|
terminate called after throwing an instance of 'std::out_of_range'
|
||||||
what(): [error] key "num" not found in example.toml
|
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
|
You can use this with a `toml::value` that is expected to be a `toml::table`.
|
||||||
it just searches an `unordered_map` and returns a value if it exists.
|
It automatically casts the value to table.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const toml::table data = toml::parse("example.toml");
|
const auto data = toml::parse("example.toml");
|
||||||
if(data.count("num") == 1)
|
const auto num = toml::find<int>(data.at("table"), "num");
|
||||||
{
|
// expecting the following example.toml
|
||||||
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]
|
// [table]
|
||||||
// num = 42
|
// 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,
|
In this case, because the value `data.at("table")` knows the locatoin of itself,
|
||||||
you don't need to pass the name to show it in an error message.
|
you don't need to pass where you find the value.
|
||||||
`toml::find` will automatically format an error message with the location of the table.
|
`toml::find` will show you an error message including table location.
|
||||||
|
|
||||||
```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,45 +490,7 @@ 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`.
|
If it's not a `toml::table`, the same error as "invalid type" would be thrown.
|
||||||
|
|
||||||
```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<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__:
|
|
||||||
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`.
|
There is another utility function, `toml::find_or`.
|
||||||
It is almost same as `toml::find`, but returns a default value if the value is
|
It is almost same as `toml::find`, but returns a default value if the value is
|
||||||
|
@ -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
|
||||||
|
26
toml/get.hpp
26
toml/get.hpp
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user