Skip to content

Commit 16a1432

Browse files
authored
Add get_value_or() (#485)
* added get_value_or() in basic_node * added api reference page for get_value_or() * updated README
1 parent c093fc4 commit 16a1432

10 files changed

Lines changed: 492 additions & 43 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ fkYAML is a C++ header-only library to deserialize, serialize and build YAML doc
1616
It is also carefully desinged and tested to work with various compilers, C++ standards and platforms.
1717
So, if you want portability & development speed-up, fkYAML is the way to go.
1818
You can add YAML support into your projects by just including the header file(s).
19-
This simple example deserializes a YAML string into a document, make simple modifications, and finally serializes the modified documentation to a YAML string.
19+
This simple example (1) deserializes a YAML string into a document, (2) modifies the documentation, and finally (3) serializes the modified documentation to a YAML string.
2020
```cpp
2121
#include <iostream>
2222
#include <string>
2323
#include <fkYAML/node.hpp>
2424

2525
int main() {
26-
// 1. deserialize a YAML string into a document.
26+
// 1. Deserialize a YAML string into a document.
2727
// The input can be other container types or their iterators.
2828
// `deserialize` accepts `FILE*`, `std::istream` as well.
2929
std::string yaml = R"(
@@ -40,7 +40,7 @@ works on:
4040
node["maintainer"] = "fktn-k";
4141
node.at("works on").as_seq().emplace_back("Windows");
4242

43-
// 3. serialize the modified document to a YAML string and save it.
43+
// 3. Serialize the modified document to a YAML string and save it.
4444
std::ofstream ofs("out.yaml");
4545
ofs << node;
4646

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<small>Defined in header [`<fkYAML/node.hpp>`](https://github.com/fktn-k/fkYAML/blob/develop/include/fkYAML/node.hpp)</small>
2+
3+
# <small>fkyaml::basic_node::</small>get_value_or
4+
5+
```cpp
6+
template <typename T, typename U>
7+
T get_value_or(U&& default_value) const noexcept;
8+
```
9+
10+
This function tries to convert a [`fkyaml::basic_node`](./index.md) to `T`.
11+
Visit the documentation for the [get_value](./get_value.md) function for supported types since this function internally calls it.
12+
If the conversion fails, this function returns `default_value` instead of throwing an exception as the [`get_value`](./get_value.md) function does.
13+
14+
Just as the [`get_value`](./get_value.md) function, this function also makes a copy of the value.
15+
If the copying costs too much, or if you need an address of the original value, then you should call one of the following functions instead.
16+
17+
* [`as_seq`](as_seq.md)
18+
* [`as_map`](as_map.md)
19+
* [`as_bool`](as_bool.md)
20+
* [`as_int`](as_int.md)
21+
* [`as_float`](as_float.md)
22+
* [`as_str`](as_str.md)
23+
24+
## **Template Parameters**
25+
26+
***T***
27+
: A compatible value type which might be cv-qualified or a reference type.
28+
29+
***U***
30+
: The default value type from which `T` (the 1st overload) or `BasicNodeType` (the 2nd overload) must be constructible.
31+
This is likely to be `const T&` or `T&&`.
32+
33+
***BasicNodeType***
34+
: A basic_node template instance type.
35+
36+
## **Return Value**
37+
38+
A value converted from the [basic_node](./index.md) object if the conversion succeeded, `default_value` otherwise.
39+
40+
## **Examples**
41+
42+
??? Example
43+
44+
```cpp
45+
--8<-- "apis/basic_node/get_value_or.cpp:9"
46+
```
47+
48+
output:
49+
```bash
50+
--8<-- "apis/basic_node/get_value_or.output"
51+
```
52+
53+
## **See Also**
54+
55+
* [basic_node](index.md)
56+
* [get_value](get_value.md)
57+
* [as_seq](as_seq.md)
58+
* [as_map](as_map.md)
59+
* [as_bool](as_bool.md)
60+
* [as_int](as_int.md)
61+
* [as_float](as_float.md)
62+
* [as_str](as_str.md)
63+
* [node_value_converter::from_node](../node_value_converter/from_node.md)

docs/docs/api/basic_node/index.md

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -96,23 +96,24 @@ This class provides features to handle YAML nodes.
9696
| [is_string](is_string.md) | checks if a basic_node has a string node value. |
9797
9898
### Conversions
99-
| Name | | Description |
100-
| ----------------------------------------- | -------- | ----------------------------------------------------------------------- |
101-
| [deserialize](deserialize.md) | (static) | deserializes the first YAML document into a basic_node. |
102-
| [deserialize_docs](deserialize_docs.md) | (static) | deserializes all YAML documents into basic_node objects. |
103-
| [operator>>](extraction_operator.md) | | deserializes an input stream into a basic_node. |
104-
| [serialize](serialize.md) | (static) | serializes a basic_node into a YAML formatted string. |
105-
| [serialize_docs](serialize_docs.md) | (static) | serializes basic_node objects into a YAML formatted string. |
106-
| [operator<<](insertion_operator.md) | | serializes a basic_node into an output stream. |
107-
| [get_value](get_value.md) | | converts a basic_node into a target type. |
108-
| [get_value_inplace](get_value_inplace.md) | | converts a basic_node into a target type and write it to a destination. |
109-
| [as_seq](as_seq.md) | | get reference to the sequence node value. |
110-
| [as_map](as_map.md) | | get reference to the mapping node value. |
111-
| [as_bool](as_bool.md) | | get reference to the boolean node value. |
112-
| [as_int](as_int.md) | | get reference to the integer node value. |
113-
| [as_float](as_float.md) | | get reference to the float node value. |
114-
| [as_str](as_str.md) | | get reference to the string node value. |
115-
| [get_value_ref](get_value_ref.md) | | **(DEPRECATED)** converts a basic_node into reference to a target type. |
99+
| Name | | Description |
100+
| ----------------------------------------- | -------- | ------------------------------------------------------------------------------------------------- |
101+
| [deserialize](deserialize.md) | (static) | deserializes the first YAML document into a basic_node. |
102+
| [deserialize_docs](deserialize_docs.md) | (static) | deserializes all YAML documents into basic_node objects. |
103+
| [operator>>](extraction_operator.md) | | deserializes an input stream into a basic_node. |
104+
| [serialize](serialize.md) | (static) | serializes a basic_node into a YAML formatted string. |
105+
| [serialize_docs](serialize_docs.md) | (static) | serializes basic_node objects into a YAML formatted string. |
106+
| [operator<<](insertion_operator.md) | | serializes a basic_node into an output stream. |
107+
| [get_value](get_value.md) | | converts a basic_node into a target type. |
108+
| [get_value_inplace](get_value_inplace.md) | | converts a basic_node into a target type and write it to a destination. |
109+
| [get_value_or](get_value_or.md) | | tries to convert a basic_node into a target type.<br>returns a default value if conversion fails. |
110+
| [as_seq](as_seq.md) | | get reference to the sequence node value. |
111+
| [as_map](as_map.md) | | get reference to the mapping node value. |
112+
| [as_bool](as_bool.md) | | get reference to the boolean node value. |
113+
| [as_int](as_int.md) | | get reference to the integer node value. |
114+
| [as_float](as_float.md) | | get reference to the float node value. |
115+
| [as_str](as_str.md) | | get reference to the string node value. |
116+
| [get_value_ref](get_value_ref.md) | | **(DEPRECATED)** converts a basic_node into reference to a target type. |
116117
117118
### Iterators
118119
| Name | Description |

docs/docs/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ fkYAML is a C++ header-only library to deserialize, serialize and build YAML doc
1616
It is also carefully desinged and tested to work with various compilers, C++ standards and platforms.
1717
So, if you want portability & development speed-up, fkYAML is the way to go.
1818
You can add YAML support into your projects by just including the header file(s).
19-
This simple example deserializes a YAML string into a document, make simple modifications, and finally serializes the modified documentation to a YAML string.
19+
This simple example (1) deserializes a YAML string into a document, (2) modifies the documentation, and finally (3) serializes the modified documentation to a YAML string.
2020
```cpp
2121
#include <iostream>
2222
#include <string>
2323
#include <fkYAML/node.hpp>
2424

2525
int main() {
26-
// 1. deserialize a YAML string into a document.
26+
// 1. Deserialize a YAML string into a document.
2727
// The input can be other container types or their iterators.
2828
// `deserialize` accepts `FILE*`, `std::istream` as well.
2929
std::string yaml = R"(
@@ -40,7 +40,7 @@ works on:
4040
node["maintainer"] = "fktn-k";
4141
node.at("works on").as_seq().emplace_back("Windows");
4242

43-
// 3. serialize the modified document to a YAML string and save it.
43+
// 3. Serialize the modified document to a YAML string and save it.
4444
std::ofstream ofs("out.yaml");
4545
ofs << node;
4646

docs/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ nav:
142142
- get_type: api/basic_node/get_type.md
143143
- get_value: api/basic_node/get_value.md
144144
- get_value_inplace: api/basic_node/get_value_inplace.md
145+
- get_value_or: api/basic_node/get_value_or.md
145146
- get_yaml_version_type: api/basic_node/get_yaml_version_type.md
146147
- has_anchor_name: api/basic_node/has_anchor_name.md
147148
- has_tag_name: api/basic_node/has_tag_name.md
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// _______ __ __ __ _____ __ __ __
2+
// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code)
3+
// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
4+
// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
5+
//
6+
// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
7+
// SPDX-License-Identifier: MIT
8+
9+
#include <iostream>
10+
#include <string>
11+
#include <tuple>
12+
#include <unordered_map>
13+
#include <vector>
14+
#include <fkYAML/node.hpp>
15+
16+
int main() {
17+
// try get sequence node values
18+
fkyaml::node seq = {true, false};
19+
20+
// successful case
21+
std::vector<bool> vec_def {false, true};
22+
auto bool_vec = seq.get_value_or<std::vector<bool>>(vec_def);
23+
for (auto b : bool_vec) {
24+
std::cout << std::boolalpha << b << std::endl;
25+
}
26+
27+
// error case
28+
auto tpl_def = std::make_tuple<int, bool, std::string>(0, false, "default");
29+
auto tpl = seq.get_value_or<std::tuple<int, bool, std::string>>(std::move(tpl_def));
30+
std::cout << std::get<0>(tpl) << ", ";
31+
std::cout << std::get<1>(tpl) << ", ";
32+
std::cout << std::get<2>(tpl) << "\n\n";
33+
34+
// try to get mapping node values
35+
fkyaml::node map = {
36+
{0, "foo"},
37+
{1, "bar"},
38+
{2, "baz"},
39+
};
40+
std::unordered_map<uint32_t, std::string> umap_def {{0, "defalt"}};
41+
auto umap = map.get_value_or<std::unordered_map<uint32_t, std::string>>(std::move(umap_def));
42+
for (auto& p : umap) {
43+
std::cout << p.first << " : " << p.second << std::endl;
44+
}
45+
std::cout << std::endl;
46+
47+
// try to get scalar node values.
48+
fkyaml::node scalar = 1.23;
49+
50+
// get the node value (value gets copied).
51+
auto dbl_val = scalar.get_value_or<double>(0.);
52+
auto str_val = scalar.get_value_or<std::string>("default");
53+
54+
std::cout << dbl_val << std::endl;
55+
std::cout << str_val << std::endl;
56+
57+
// Numeric scalar value will be converted to target numeric types inside get_value_or().
58+
auto bool_val = scalar.get_value_or<bool>(false); // 1.23 -> true
59+
auto int_val = scalar.get_value_or<int>(0); // 1.23 -> 1
60+
61+
std::cout << std::boolalpha << bool_val << std::endl;
62+
std::cout << int_val << std::endl;
63+
64+
return 0;
65+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
true
2+
false
3+
0, false, default
4+
5+
2 : baz
6+
1 : bar
7+
0 : foo
8+
9+
1.23
10+
default
11+
true
12+
1

include/fkYAML/node.hpp

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,24 +1299,25 @@ class basic_node {
12991299
/// @brief Get the node value object converted into a given type.
13001300
/// @note This function requires T objects to be default constructible. Also, T cannot be either a reference,
13011301
/// pointer or C-style array type.
1302-
/// @tparam T A compatible value type which might be cv-qualified.
1303-
/// @tparam ValueType A compatible value type with cv-qualifiers removed by default.
1304-
/// @return A compatible native data value converted from the basic_node object.
1302+
/// @tparam T A compatible value type which may be cv-qualified.
1303+
/// @tparam ValueType A compatible value type (T without cv-qualifiers by default).
1304+
/// @return A value converted from this basic_node object.
13051305
/// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value/
13061306
template <
13071307
typename T, typename ValueType = detail::remove_cv_t<T>,
1308-
detail::enable_if_t<std::is_default_constructible<ValueType>::value, int> = 0>
1308+
detail::enable_if_t<
1309+
detail::conjunction<std::is_default_constructible<ValueType>, detail::negation<std::is_pointer<T>>>::value,
1310+
int> = 0>
13091311
T get_value() const noexcept(
13101312
noexcept(std::declval<const basic_node&>().template get_value_impl<ValueType>(std::declval<ValueType&>()))) {
13111313
// emit a compile error if T is either a reference, pointer or C-style array type.
13121314
static_assert(
13131315
!std::is_reference<T>::value,
13141316
"get_value() cannot be called with reference types. "
13151317
"You might want to call one of as_seq(), as_map(), as_bool(), as_int(), as_float() or as_str().");
1316-
static_assert(!std::is_pointer<T>::value, "get_value() cannot be called with pointer types.");
13171318
static_assert(
13181319
!std::is_array<T>::value,
1319-
"get_value() cannot be called with C-style array types. you might want to call get_value_inplace().");
1320+
"get_value() cannot be called with C-style array types. You might want to call get_value_inplace().");
13201321

13211322
auto ret = ValueType();
13221323
resolve_reference().get_value_impl(ret);
@@ -1333,6 +1334,47 @@ class basic_node {
13331334
resolve_reference().get_value_impl(value_ref);
13341335
}
13351336

1337+
/// @brief Get the node value object converted to a given type. If the conversion fails, this function returns a
1338+
/// given default value instead.
1339+
/// @note This function requires T to be default constructible. Also, T cannot be either a reference, pointer or
1340+
/// C-style array type.
1341+
/// @tparam T A compatible value type which may be cv-qualified.
1342+
/// @tparam U A default value type from which T must be constructible.
1343+
/// @param default_value The default value returned if conversion fails.
1344+
/// @return A value converted from this basic_node object if conversion succeeded, the given default value
1345+
/// otherwise.
1346+
/// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value_or/
1347+
template <
1348+
typename T, typename U,
1349+
detail::enable_if_t<
1350+
detail::conjunction<
1351+
std::is_constructible<T, U>, std::is_default_constructible<T>,
1352+
detail::negation<std::is_pointer<T>>>::value,
1353+
int> = 0>
1354+
T get_value_or(U&& default_value) const noexcept {
1355+
static_assert(
1356+
!std::is_reference<T>::value,
1357+
"get_value_or() cannot be called with reference types. "
1358+
"You might want to call one of as_seq(), as_map(), as_bool(), as_int(), as_float() or as_str().");
1359+
static_assert(
1360+
!std::is_array<T>::value,
1361+
"get_value_or() cannot be called with C-style array types. You might want to call get_value_inplace().");
1362+
1363+
// TODO:
1364+
// Ideally, there should be no exception thrown in this kind of function. However, achieving that would require
1365+
// a lot of refactoring and/or some API changes, especially `from_node` interface definition. So, try-catch is
1366+
// used instead for now.
1367+
try {
1368+
return get_value<T>();
1369+
}
1370+
catch (const std::exception& /*unused*/) {
1371+
// Any exception derived from std::exception is interpreted as a conversion failure in some way
1372+
// since user-defined from_node function may throw a different object from a fkyaml::type_error.
1373+
// and std::exception is usually the base class of user-defined exception types.
1374+
return std::forward<U>(default_value);
1375+
}
1376+
}
1377+
13361378
/// @brief Explicit reference access to the internally stored YAML node value.
13371379
/// @tparam ReferenceType Reference type to the target YAML node value.
13381380
/// @return Reference to the internally stored YAML node value.

0 commit comments

Comments
 (0)