diff --git a/docs/examples/ex_basic_node_get_value_inplace.cpp b/docs/examples/ex_basic_node_get_value_inplace.cpp new file mode 100644 index 00000000..3aa4cc47 --- /dev/null +++ b/docs/examples/ex_basic_node_get_value_inplace.cpp @@ -0,0 +1,54 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.4.0 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include + +struct not_default_constructible { + not_default_constructible() = delete; + explicit not_default_constructible(int v) noexcept + : value(v) { + } + not_default_constructible(const not_default_constructible&) = default; + + int value {0}; +}; +// Custom implementation of from_node() for not_default_constructible objects. +// This function is called inside get_value_inplace(). +// See https://fktn-k.github.io/fkYAML/api/node_value_converter/from_node/ for details. +inline void from_node(const fkyaml::node& n, not_default_constructible& ndc) { + ndc.value = n.get_value(); +} + +int main() { + // create a sequence node. + fkyaml::node seq = {true, false}; + + // fill the node values into a 1D C-style array + bool bools[2] {}; + seq.get_value_inplace(bools); + for (auto b : bools) { + std::cout << std::boolalpha << b << " "; + } + std::cout << "\n\n"; + + // create an integer scalar node + fkyaml::node integer = 123; + + // get_value_inplace() accepts a type which is not default constructible. + not_default_constructible ndc {0}; + integer.get_value_inplace(ndc); + std::cout << ndc.value << std::endl; + + // of course, you can pass a value of default constructible type as well. + integer = -123; + integer.get_value_inplace(ndc.value); + std::cout << ndc.value << std::endl; + + return 0; +} diff --git a/docs/examples/ex_basic_node_get_value_inplace.output b/docs/examples/ex_basic_node_get_value_inplace.output new file mode 100644 index 00000000..3f4600c4 --- /dev/null +++ b/docs/examples/ex_basic_node_get_value_inplace.output @@ -0,0 +1,4 @@ +true false + +123 +-123 diff --git a/docs/mkdocs/docs/api/basic_node/get_value.md b/docs/mkdocs/docs/api/basic_node/get_value.md index c9d88f0d..909a2625 100644 --- a/docs/mkdocs/docs/api/basic_node/get_value.md +++ b/docs/mkdocs/docs/api/basic_node/get_value.md @@ -11,7 +11,7 @@ template BasicNodeType get_value() const; // (2) ``` -This function converts a [fkyaml::basic_node](./index.md) to either +This function converts a [`fkyaml::basic_node`](./index.md) to either 1. a compatible value ([copy-constructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and [default-constructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible)) The function is equivalent to executing @@ -21,7 +21,8 @@ This function converts a [fkyaml::basic_node](./index.md) to either return ret; ``` This library implements conversions from a node to a number of STL container types and scalar types. (see the notes down below) - Note that ValueType cannot be either a reference, pointer or C-style array type except `std::nullptr_t`. + Note that ValueType cannot be either a reference, pointer or C-style array type except `std::nullptr_t` since safe conversion is impossible with such types. + If you want to convert a node into a C-style array, consider using the [`get_value_inplace`](./get_value_inplace.md) function instead. 2. a [fkyaml::basic_node](./index.md) object The function is equivalent to executing ```cpp @@ -35,15 +36,15 @@ This API makes a copy of the value, and if the copying costs too much, or if you This library implements conversions from a sequence node to a number of STL container types whose element type is not a key-value pair. The implementation can be used for custom container types, but they need to have both `iterator` member type and `insert()` member function. The test suite confirms successful conversions to the following types. - * std::vector, std::deque, std::list *(sequence containers)* - * std::set, std::multiset *(associative containers for keys)* - * std::unordered_set, std::unordered_multiset *(unordered associative containers for keys)* + * [std::vector](https://en.cppreference.com/w/cpp/container/vector), [std::deque](https://en.cppreference.com/w/cpp/container/deque), [std::list](https://en.cppreference.com/w/cpp/container/list) *(sequence containers)* + * [std::set](https://en.cppreference.com/w/cpp/container/set), [std::multiset](https://en.cppreference.com/w/cpp/container/multiset) *(associative containers for keys)* + * [std::unordered_set](https://en.cppreference.com/w/cpp/container/unordered_set), [std::unordered_multiset](https://en.cppreference.com/w/cpp/container/unordered_multiset) *(unordered associative containers for keys)* And you can also convert to these types which do not have `insert()` member function though. - * std::array, std::valarray *(sequence containers)* - * std::stack, std::queue, std::priority_queue *(sequence container adapters)* - * std::pair, std::tuple + * [std::array](https://en.cppreference.com/w/cpp/container/array), [std::valarray](https://en.cppreference.com/w/cpp/numeric/valarray) *(sequence containers)* + * [std::stack](https://en.cppreference.com/w/cpp/container/stack), [std::queue](https://en.cppreference.com/w/cpp/container/queue), [std::priority_queue](https://en.cppreference.com/w/cpp/container/priority_queue) *(sequence container adapters)* + * [std::pair](https://en.cppreference.com/w/cpp/utility/pair), [std::tuple](https://en.cppreference.com/w/cpp/utility/tuple) Note that the above types cannot be converted from a non-sequence node, which results in throwing a [type_error](../exception/type_error.md). @@ -51,8 +52,8 @@ This API makes a copy of the value, and if the copying costs too much, or if you This library implements conversions from a mapping node to STL container types whose element type is a key-value pair. The implementation can be used for custom container types, but they need to have `key_type`, `mapped_type` and `value_type` member types and `emplace()` member function. The test suite confirms successful conversions to the following types. - * std::map, std::multimap *(associative containers for key-value pairs)* - * std::unordered_map, std::unordered_multi_map *(unordered associative containers for key-value pairs)* + * [std::map](https://en.cppreference.com/w/cpp/container/map), [std::multimap](https://en.cppreference.com/w/cpp/container/multimap) *(associative containers for key-value pairs)* + * [std::unordered_map](https://en.cppreference.com/w/cpp/container/unordered_map), [std::unordered_multi_map](https://en.cppreference.com/w/cpp/container/unordered_multimap) *(unordered associative containers for key-value pairs)* ???+ Note "Convert from a Null or Numeric Scalar Node" @@ -78,8 +79,12 @@ This API makes a copy of the value, and if the copying costs too much, or if you String scalar nodes can be converted to STL container types which can be constructible from `const fkyaml::basic_node::string_type&` (`const std::string&` by default). The test suite confirms successful conversions to the following types. - * std::string - * std::string_view (from C++17) + * [std::string](https://en.cppreference.com/w/cpp/string/basic_string) + * [std::string_view](https://en.cppreference.com/w/cpp/string/basic_string_view) (from C++17) + +???+ Note "Convert as an optional value" + + Since C++17, [std::optional](https://en.cppreference.com/w/cpp/utility/optional) can be used as `ValueType` to indicate the conversion result to be optional. In such cases, a returned [std::optional](https://en.cppreference.com/w/cpp/utility/optional) value contains a value only if the conversion was successful, or [`std::nullopt`](https://en.cppreference.com/w/cpp/utility/optional/nullopt) otherwise. ## **Template Parameters** @@ -112,5 +117,6 @@ A compatible native data value converted from the [basic_node](./index.md) objec ## **See Also** * [basic_node](index.md) +* [get_value_inplace](get_value_inplace.md) * [get_value_ref](get_value_ref.md) * [node_value_converter::from_node](../node_value_converter/from_node.md) diff --git a/docs/mkdocs/docs/api/basic_node/get_value_inplace.md b/docs/mkdocs/docs/api/basic_node/get_value_inplace.md new file mode 100644 index 00000000..b2aa4f96 --- /dev/null +++ b/docs/mkdocs/docs/api/basic_node/get_value_inplace.md @@ -0,0 +1,64 @@ +Defined in header [``](https://github.com/fktn-k/fkYAML/blob/develop/include/fkYAML/node.hpp) + +# fkyaml::basic_node::get_value + +```cpp +template +void get_value_inplace(T& value_ref) const noexcept( + noexcept(ConverterType::from_node(std::declval(), std::declval()))); // (1) + +template +void get_value_inplace(BasicNodeType& value_ref) const; // (2) +``` + +This function converts a [`fkyaml::basic_node`](./index.md) to either of the followings and fills the conversion result into the given `value_ref` parameter. + +1. a compatible value ([copy-constructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)) + The function is equivalent to executing + ```cpp + ConverterType::from_node(*this, value_ref); + ``` + Unlike the [`get_value`](./get_value.md) function, this function does not require the template parameter type `T` to be [default-constructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible) since no instantiation of `T` is necessary inside the function to return the conversion result. +2. a [fkyaml::basic_node](./index.md) object + The function is equivalent to executing + ```cpp + value_ref = *this; // Copy-assigns a current basic_node object. + ``` + +This function shares internal implementation with the [`get_value`](./get_value.md). +Thus, all the STL container & scalar types which are supported by that function, is also supported by this function as well. +See the notes there for details. + +???+ Note "Difference from `get_value()`" + + One crutial difference from the [`get_value`](./get_value.md) function is, this function does not require the template parameter type `T` to be [default-constructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible) since no instantiation of `T` is necessary inside the function to return the conversion result anymore. + So, you might prefer this function, for example, if you already created a `T` object as a member variable and want to assign a node value to it. + + Another is C-style array support. + While [`get_value`](./get_value.md) cannot accept any kind of C-style array types since returning such array objects is impossible due to its implementation, this function accepts 1D, 2D and 3D arrays. Note that, if `T` is one of them, the target basic_node object must be a sequence. A [`type_error`](../exception/type_error.md) is thrown otherwise. + +## **Template Parameters** + +***T*** +: A compatible value type. + +***BasicNodeType*** +: A basic_node template instance type. + +???+ Example + + ```cpp + --8<-- "examples/ex_basic_node_get_value_inplace.cpp:9" + ``` + + output: + ```bash + --8<-- "examples/ex_basic_node_get_value_inplace.output" + ``` + +## **See Also** + +* [basic_node](index.md) +* [get_value](get_value.md) +* [get_value_ref](get_value_ref.md) +* [node_value_converter::from_node](../node_value_converter/from_node.md) diff --git a/docs/mkdocs/docs/api/basic_node/index.md b/docs/mkdocs/docs/api/basic_node/index.md index 2a5e7f24..edce0694 100644 --- a/docs/mkdocs/docs/api/basic_node/index.md +++ b/docs/mkdocs/docs/api/basic_node/index.md @@ -93,16 +93,17 @@ This class provides features to handle YAML nodes. | [is_string](is_string.md) | checks if a basic_node has a string node value. | ### Conversions -| Name | | Description | -| --------------------------------------- | -------- | ------------------------------------------------------------------ | -| [deserialize](deserialize.md) | (static) | deserializes the first YAML document into a basic_node. | -| [deserialize_docs](deserialize_docs.md) | (static) | deserializes all YAML documents into basic_node objects. | -| [operator>>](extraction_operator.md) | | deserializes an input stream into a basic_node. | -| [serialize](serialize.md) | (static) | serializes a basic_node into a YAML formatted string. | -| [serialize_docs](serialize_docs.md) | (static) | serializes basic_node objects into a YAML formatted string. | -| [operator<<](insertion_operator.md) | | serializes a basic_node into an output stream. | -| [get_value](get_value.md) | | converts a basic_node into a target native data type. | -| [get_value_ref](get_value_ref.md) | | converts a basic_node into reference to a target native data type. | +| Name | | Description | +| ----------------------------------------- | -------- | ----------------------------------------------------------------------- | +| [deserialize](deserialize.md) | (static) | deserializes the first YAML document into a basic_node. | +| [deserialize_docs](deserialize_docs.md) | (static) | deserializes all YAML documents into basic_node objects. | +| [operator>>](extraction_operator.md) | | deserializes an input stream into a basic_node. | +| [serialize](serialize.md) | (static) | serializes a basic_node into a YAML formatted string. | +| [serialize_docs](serialize_docs.md) | (static) | serializes basic_node objects into a YAML formatted string. | +| [operator<<](insertion_operator.md) | | serializes a basic_node into an output stream. | +| [get_value](get_value.md) | | converts a basic_node into a target type. | +| [get_value_inplace](get_value_inplace.md) | | converts a basic_node into a target type and write it to a destination. | +| [get_value_ref](get_value_ref.md) | | converts a basic_node into reference to a target type. | ### Iterators | Name | Description | diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index d6dc322b..a2e36482 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -123,6 +123,7 @@ nav: - get_tag_name: api/basic_node/get_tag_name.md - get_type: api/basic_node/get_type.md - get_value: api/basic_node/get_value.md + - get_value_inplace: api/basic_node/get_value_inplace.md - get_value_ref: api/basic_node/get_value_ref.md - get_yaml_version: api/basic_node/get_yaml_version.md - get_yaml_version_type: api/basic_node/get_yaml_version_type.md diff --git a/include/fkYAML/detail/conversions/from_node.hpp b/include/fkYAML/detail/conversions/from_node.hpp index 4e1801b6..f06c8f9d 100644 --- a/include/fkYAML/detail/conversions/from_node.hpp +++ b/include/fkYAML/detail/conversions/from_node.hpp @@ -63,8 +63,6 @@ using is_sequence_container_adapter = conjunction< template struct call_reserve_if_available { /// @brief Do nothing since ContainerType does not have reserve function. - /// @param - /// @param static void call(ContainerType& /*unused*/, typename ContainerType::size_type /*unused*/) { } }; @@ -83,6 +81,75 @@ struct call_reserve_if_available +inline auto from_node(const BasicNodeType& n, T (&array)[N]) + -> decltype(n.get_value_inplace(std::declval()), void()) { + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); + } + + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + for (std::size_t i = 0; i < N; i++) { + n.at(i).get_value_inplace(array[i]); + } +} + +/// @brief from_node function for C-style 2D arrays whose element type must be a basic_node template instance type or a +/// compatible type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam T Element type of C-style 2D array. +/// @tparam N0 Size of the outer dimension. +/// @tparam N1 Size of the inner dimension. +/// @param n A basic_node object. +/// @param array An array object. +template +inline auto from_node(const BasicNodeType& n, T (&array)[N0][N1]) + -> decltype(n.get_value_inplace(std::declval()), void()) { + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); + } + + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + for (std::size_t i0 = 0; i0 < N0; i0++) { + for (std::size_t i1 = 0; i1 < N1; i1++) { + n.at(i0).at(i1).get_value_inplace(array[i0][i1]); + } + } +} + +/// @brief from_node function for C-style 2D arrays whose element type must be a basic_node template instance type or a +/// compatible type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam T Element type of C-style 2D array. +/// @tparam N0 Size of the outermost dimension. +/// @tparam N1 Size of the middle dimension. +/// @tparam N2 Size of the innermost dimension. +/// @param n A basic_node object. +/// @param array An array object. +template +inline auto from_node(const BasicNodeType& n, T (&array)[N0][N1][N2]) + -> decltype(n.get_value_inplace(std::declval()), void()) { + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); + } + + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + for (std::size_t i0 = 0; i0 < N0; i0++) { + for (std::size_t i1 = 0; i1 < N1; i1++) { + for (std::size_t i2 = 0; i2 < N2; i2++) { + n.at(i0).at(i1).at(i2).get_value_inplace(array[i0][i1][i2]); + } + } + } +} + /// @brief from_node function for std::array objects whose element type must be a basic_node template instance type or a /// compatible type. This function is necessary since insert function is not implemented for std::array. /// @tparam BasicNodeType A basic_node template instance type. @@ -91,14 +158,15 @@ struct call_reserve_if_available -inline auto from_node(const BasicNodeType& n, std::array& arr) -> decltype(n.template get_value(), void()) { +inline auto from_node(const BasicNodeType& n, std::array& arr) + -> decltype(n.get_value_inplace(std::declval()), void()) { if FK_YAML_UNLIKELY (!n.is_sequence()) { throw type_error("The target node value type is not sequence type.", n.get_type()); } - std::size_t count = std::min(n.size(), N); - for (std::size_t i = 0; i < count; i++) { - arr.at(i) = n.at(i).template get_value(); + for (std::size_t i = 0; i < N; i++) { + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + n.at(i).get_value_inplace(arr.at(i)); } } @@ -109,7 +177,8 @@ inline auto from_node(const BasicNodeType& n, std::array& arr) -> decltype /// @param n A basic_node object. /// @param va A std::valarray object. template -inline auto from_node(const BasicNodeType& n, std::valarray& va) -> decltype(n.template get_value(), void()) { +inline auto from_node(const BasicNodeType& n, std::valarray& va) + -> decltype(n.get_value_inplace(std::declval()), void()) { if FK_YAML_UNLIKELY (!n.is_sequence()) { throw type_error("The target node value type is not sequence type.", n.get_type()); } @@ -117,7 +186,8 @@ inline auto from_node(const BasicNodeType& n, std::valarray& va) -> decltype( std::size_t count = n.size(); va.resize(count); for (std::size_t i = 0; i < count; i++) { - va[i] = n.at(i).template get_value(); + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + n.at(i).get_value_inplace(va[i]); } } @@ -161,7 +231,7 @@ template < enable_if_t< conjunction, is_sequence_container_adapter>::value, int> = 0> inline auto from_node(const BasicNodeType& n, SeqContainerAdapter& ca) - -> decltype(n.template get_value(), ca.emplace(std::declval()), void()) { + -> decltype(n.template get_value(), ca.push(std::declval()), void()) { if FK_YAML_UNLIKELY (!n.is_sequence()) { throw type_error("The target node value is not sequence type.", n.get_type()); } @@ -479,7 +549,9 @@ inline auto from_node(const BasicNodeType& n, std::pair& p) throw type_error("The target node value type is not sequence type.", n.get_type()); } - p = {n.at(0).template get_value(), n.at(1).template get_value()}; + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + n.at(0).get_value_inplace(p.first); + n.at(1).get_value_inplace(p.second); } /// @brief concrete implementation of from_node function for std::tuple objects. diff --git a/include/fkYAML/node.hpp b/include/fkYAML/node.hpp index c9218d43..53744541 100644 --- a/include/fkYAML/node.hpp +++ b/include/fkYAML/node.hpp @@ -1341,7 +1341,8 @@ class basic_node { } /// @brief Get the node value object converted into a given type. - /// @note This function requires T objects to be default constructible. + /// @note This function requires T objects to be default constructible. Also, T cannot be either a reference, + /// pointer or C-style array type. /// @tparam T A compatible value type which might be cv-qualified. /// @tparam ValueType A compatible value type with cv-qualifiers removed by default. /// @return A compatible native data value converted from the basic_node object. @@ -1350,13 +1351,15 @@ class basic_node { typename T, typename ValueType = detail::remove_cv_t, detail::enable_if_t::value, int> = 0> T get_value() const noexcept( - noexcept(std::declval().template get_value_impl(std::declval()))) { + noexcept(std::declval().template get_value_impl(std::declval()))) { // emit a compile error if T is either a reference, pointer or C-style array type. static_assert( !std::is_reference::value, "get_value() cannot be called with reference types. you might want to call get_value_ref()."); static_assert(!std::is_pointer::value, "get_value() cannot be called with pointer types."); - static_assert(!std::is_array::value, "get_value() cannot be called with C-style array types."); + static_assert( + !std::is_array::value, + "get_value() cannot be called with C-style array types. you might want to call get_value_inplace()."); auto ret = ValueType(); if (has_anchor_name()) { @@ -1370,6 +1373,23 @@ class basic_node { return ret; } + /// @brief Get the node value object converted into a given type. The conversion result is filled into `value_ref`. + /// @tparam T A compatible value type. + /// @param value_ref A storage into which the conversion result is filled. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value_inplace/ + template + void get_value_inplace(T& value_ref) const + noexcept(noexcept(std::declval().template get_value_impl(std::declval()))) { + if (has_anchor_name()) { + auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); + itr->second.get_value_impl(value_ref); + } + else { + get_value_impl(value_ref); + } + } + /// @brief Explicit reference access to the internally stored YAML node value. /// @tparam ReferenceType Reference type to the target YAML node value. /// @return Reference to the internally stored YAML node value. diff --git a/single_include/fkYAML/node.hpp b/single_include/fkYAML/node.hpp index 77badf72..10967c1e 100644 --- a/single_include/fkYAML/node.hpp +++ b/single_include/fkYAML/node.hpp @@ -11056,8 +11056,6 @@ using is_sequence_container_adapter = conjunction< template struct call_reserve_if_available { /// @brief Do nothing since ContainerType does not have reserve function. - /// @param - /// @param static void call(ContainerType& /*unused*/, typename ContainerType::size_type /*unused*/) { } }; @@ -11076,6 +11074,75 @@ struct call_reserve_if_available +inline auto from_node(const BasicNodeType& n, T (&array)[N]) + -> decltype(n.get_value_inplace(std::declval()), void()) { + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); + } + + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + for (std::size_t i = 0; i < N; i++) { + n.at(i).get_value_inplace(array[i]); + } +} + +/// @brief from_node function for C-style 2D arrays whose element type must be a basic_node template instance type or a +/// compatible type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam T Element type of C-style 2D array. +/// @tparam N0 Size of the outer dimension. +/// @tparam N1 Size of the inner dimension. +/// @param n A basic_node object. +/// @param array An array object. +template +inline auto from_node(const BasicNodeType& n, T (&array)[N0][N1]) + -> decltype(n.get_value_inplace(std::declval()), void()) { + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); + } + + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + for (std::size_t i0 = 0; i0 < N0; i0++) { + for (std::size_t i1 = 0; i1 < N1; i1++) { + n.at(i0).at(i1).get_value_inplace(array[i0][i1]); + } + } +} + +/// @brief from_node function for C-style 2D arrays whose element type must be a basic_node template instance type or a +/// compatible type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam T Element type of C-style 2D array. +/// @tparam N0 Size of the outermost dimension. +/// @tparam N1 Size of the middle dimension. +/// @tparam N2 Size of the innermost dimension. +/// @param n A basic_node object. +/// @param array An array object. +template +inline auto from_node(const BasicNodeType& n, T (&array)[N0][N1][N2]) + -> decltype(n.get_value_inplace(std::declval()), void()) { + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); + } + + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + for (std::size_t i0 = 0; i0 < N0; i0++) { + for (std::size_t i1 = 0; i1 < N1; i1++) { + for (std::size_t i2 = 0; i2 < N2; i2++) { + n.at(i0).at(i1).at(i2).get_value_inplace(array[i0][i1][i2]); + } + } + } +} + /// @brief from_node function for std::array objects whose element type must be a basic_node template instance type or a /// compatible type. This function is necessary since insert function is not implemented for std::array. /// @tparam BasicNodeType A basic_node template instance type. @@ -11084,14 +11151,15 @@ struct call_reserve_if_available -inline auto from_node(const BasicNodeType& n, std::array& arr) -> decltype(n.template get_value(), void()) { +inline auto from_node(const BasicNodeType& n, std::array& arr) + -> decltype(n.get_value_inplace(std::declval()), void()) { if FK_YAML_UNLIKELY (!n.is_sequence()) { throw type_error("The target node value type is not sequence type.", n.get_type()); } - std::size_t count = std::min(n.size(), N); - for (std::size_t i = 0; i < count; i++) { - arr.at(i) = n.at(i).template get_value(); + for (std::size_t i = 0; i < N; i++) { + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + n.at(i).get_value_inplace(arr.at(i)); } } @@ -11102,7 +11170,8 @@ inline auto from_node(const BasicNodeType& n, std::array& arr) -> decltype /// @param n A basic_node object. /// @param va A std::valarray object. template -inline auto from_node(const BasicNodeType& n, std::valarray& va) -> decltype(n.template get_value(), void()) { +inline auto from_node(const BasicNodeType& n, std::valarray& va) + -> decltype(n.get_value_inplace(std::declval()), void()) { if FK_YAML_UNLIKELY (!n.is_sequence()) { throw type_error("The target node value type is not sequence type.", n.get_type()); } @@ -11110,7 +11179,8 @@ inline auto from_node(const BasicNodeType& n, std::valarray& va) -> decltype( std::size_t count = n.size(); va.resize(count); for (std::size_t i = 0; i < count; i++) { - va[i] = n.at(i).template get_value(); + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + n.at(i).get_value_inplace(va[i]); } } @@ -11154,7 +11224,7 @@ template < enable_if_t< conjunction, is_sequence_container_adapter>::value, int> = 0> inline auto from_node(const BasicNodeType& n, SeqContainerAdapter& ca) - -> decltype(n.template get_value(), ca.emplace(std::declval()), void()) { + -> decltype(n.template get_value(), ca.push(std::declval()), void()) { if FK_YAML_UNLIKELY (!n.is_sequence()) { throw type_error("The target node value is not sequence type.", n.get_type()); } @@ -11472,7 +11542,9 @@ inline auto from_node(const BasicNodeType& n, std::pair& p) throw type_error("The target node value type is not sequence type.", n.get_type()); } - p = {n.at(0).template get_value(), n.at(1).template get_value()}; + // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created. + n.at(0).get_value_inplace(p.first); + n.at(1).get_value_inplace(p.second); } /// @brief concrete implementation of from_node function for std::tuple objects. @@ -13433,7 +13505,8 @@ class basic_node { } /// @brief Get the node value object converted into a given type. - /// @note This function requires T objects to be default constructible. + /// @note This function requires T objects to be default constructible. Also, T cannot be either a reference, + /// pointer or C-style array type. /// @tparam T A compatible value type which might be cv-qualified. /// @tparam ValueType A compatible value type with cv-qualifiers removed by default. /// @return A compatible native data value converted from the basic_node object. @@ -13442,13 +13515,15 @@ class basic_node { typename T, typename ValueType = detail::remove_cv_t, detail::enable_if_t::value, int> = 0> T get_value() const noexcept( - noexcept(std::declval().template get_value_impl(std::declval()))) { + noexcept(std::declval().template get_value_impl(std::declval()))) { // emit a compile error if T is either a reference, pointer or C-style array type. static_assert( !std::is_reference::value, "get_value() cannot be called with reference types. you might want to call get_value_ref()."); static_assert(!std::is_pointer::value, "get_value() cannot be called with pointer types."); - static_assert(!std::is_array::value, "get_value() cannot be called with C-style array types."); + static_assert( + !std::is_array::value, + "get_value() cannot be called with C-style array types. you might want to call get_value_inplace()."); auto ret = ValueType(); if (has_anchor_name()) { @@ -13462,6 +13537,23 @@ class basic_node { return ret; } + /// @brief Get the node value object converted into a given type. The conversion result is filled into `value_ref`. + /// @tparam T A compatible value type. + /// @param value_ref A storage into which the conversion result is filled. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value_inplace/ + template + void get_value_inplace(T& value_ref) const + noexcept(noexcept(std::declval().template get_value_impl(std::declval()))) { + if (has_anchor_name()) { + auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); + itr->second.get_value_impl(value_ref); + } + else { + get_value_impl(value_ref); + } + } + /// @brief Explicit reference access to the internally stored YAML node value. /// @tparam ReferenceType Reference type to the target YAML node value. /// @return Reference to the internally stored YAML node value. diff --git a/test/unit_test/test_node_class.cpp b/test/unit_test/test_node_class.cpp index 1773361a..c865436c 100644 --- a/test/unit_test/test_node_class.cpp +++ b/test/unit_test/test_node_class.cpp @@ -2364,23 +2364,78 @@ TEST_CASE("Node_AddTagName") { // test cases for value getters (copy) // +struct not_default_constructible { + not_default_constructible() = delete; + explicit not_default_constructible(int i) + : value(i) { + } + not_default_constructible(const not_default_constructible&) = default; + int value; +}; +inline void from_node(const fkyaml::node& n, not_default_constructible& ndc) noexcept { + ndc.value = n.get_value(); +} + struct string_wrap { string_wrap() = default; string_wrap& operator=(const std::string& _str) { str = _str; return *this; } - std::string str; + std::string str {}; }; template using get_fn_t = decltype(std::declval().template get()); -TEST_CASE("Node_GetValue") { - +TEST_CASE("Node_GetValue_GetValueInplace") { SECTION("sequence") { fkyaml::node node {true, false}; + SECTION("sequence value (1D C-style array)") { + STATIC_REQUIRE_FALSE(fkyaml::detail::is_detected::value); + + int ints_1d[2] {0, 0}; + fkyaml::node array_1d {1, 2}; + array_1d.get_value_inplace(ints_1d); + REQUIRE(ints_1d[0] == 1); + REQUIRE(ints_1d[1] == 2); + } + + SECTION("sequence value (2D C-style array)") { + STATIC_REQUIRE_FALSE(fkyaml::detail::is_detected::value); + + int ints_2d[3][3] {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + fkyaml::node array_2d {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + array_2d.get_value_inplace(ints_2d); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + REQUIRE(ints_2d[i][j] == i * 3 + j + 1); + } + } + } + + SECTION("sequence value (3D C-style array)") { + STATIC_REQUIRE_FALSE(fkyaml::detail::is_detected::value); + + int ints_3d[3][3][3] { + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, + {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; + fkyaml::node array_3d { + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, + {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}, + {{19, 20, 21}, {22, 23, 24}, {25, 26, 27}}}; + array_3d.get_value_inplace(ints_3d); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + REQUIRE(ints_3d[i][j][k] == i * 9 + j * 3 + k + 1); + } + } + } + } + SECTION("sequence value (std::vector)") { auto vector_node = node.get_value>(); REQUIRE(vector_node.size() == 2); @@ -2389,10 +2444,24 @@ TEST_CASE("Node_GetValue") { REQUIRE(vector_node[1].is_boolean()); REQUIRE(vector_node[1].get_value() == false); + std::vector vector_node_inplace {}; + node.get_value_inplace(vector_node_inplace); + REQUIRE(vector_node_inplace.size() == 2); + REQUIRE(vector_node_inplace[0].is_boolean()); + REQUIRE(vector_node_inplace[0].get_value() == true); + REQUIRE(vector_node_inplace[1].is_boolean()); + REQUIRE(vector_node_inplace[1].get_value() == false); + auto vector_bool = node.get_value>(); REQUIRE(vector_bool.size() == 2); REQUIRE(vector_bool[0] == true); REQUIRE(vector_bool[1] == false); + + std::vector vector_bool_inplace {}; + node.get_value_inplace(vector_bool_inplace); + REQUIRE(vector_bool_inplace.size() == 2); + REQUIRE(vector_bool_inplace[0] == true); + REQUIRE(vector_bool_inplace[1] == false); } SECTION("sequence value (std::array)") { @@ -2402,9 +2471,21 @@ TEST_CASE("Node_GetValue") { REQUIRE(array_node[1].is_boolean()); REQUIRE(array_node[1].get_value() == false); + std::array array_node_inplace {{}}; + node.get_value_inplace(array_node_inplace); + REQUIRE(array_node_inplace[0].is_boolean()); + REQUIRE(array_node_inplace[0].get_value() == true); + REQUIRE(array_node_inplace[1].is_boolean()); + REQUIRE(array_node_inplace[1].get_value() == false); + auto array_bool = node.get_value>(); REQUIRE(array_bool[0] == true); REQUIRE(array_bool[1] == false); + + std::array array_bool_inplace {{}}; + node.get_value_inplace(array_bool_inplace); + REQUIRE(array_bool_inplace[0] == true); + REQUIRE(array_bool_inplace[1] == false); } SECTION("sequence value (std::valarray)") { @@ -2414,9 +2495,21 @@ TEST_CASE("Node_GetValue") { REQUIRE(valarray_node[1].is_boolean()); REQUIRE(valarray_node[1].get_value() == false); + std::valarray valarray_node_inplace {}; + node.get_value_inplace(valarray_node_inplace); + REQUIRE(valarray_node_inplace[0].is_boolean()); + REQUIRE(valarray_node_inplace[0].get_value() == true); + REQUIRE(valarray_node_inplace[1].is_boolean()); + REQUIRE(valarray_node_inplace[1].get_value() == false); + auto valarray_bool = node.get_value>(); REQUIRE(valarray_bool[0] == true); REQUIRE(valarray_bool[1] == false); + + std::valarray valarray_bool_inplace {}; + node.get_value_inplace(valarray_bool_inplace); + REQUIRE(valarray_bool_inplace[0] == true); + REQUIRE(valarray_bool_inplace[1] == false); } SECTION("sequence value (std::deque)") { @@ -2427,10 +2520,24 @@ TEST_CASE("Node_GetValue") { REQUIRE(deque_node[1].is_boolean()); REQUIRE(deque_node[1].get_value() == false); + std::deque deque_node_inplace {}; + node.get_value_inplace(deque_node_inplace); + REQUIRE(deque_node_inplace.size() == 2); + REQUIRE(deque_node_inplace[0].is_boolean()); + REQUIRE(deque_node_inplace[0].get_value() == true); + REQUIRE(deque_node_inplace[1].is_boolean()); + REQUIRE(deque_node_inplace[1].get_value() == false); + auto deque_bool = node.get_value>(); REQUIRE(deque_bool.size() == 2); REQUIRE(deque_bool[0] == true); REQUIRE(deque_bool[1] == false); + + std::deque deque_bool_inplace {}; + node.get_value_inplace(deque_bool_inplace); + REQUIRE(deque_bool_inplace.size() == 2); + REQUIRE(deque_bool_inplace[0] == true); + REQUIRE(deque_bool_inplace[1] == false); } SECTION("sequence value (std::list)") { @@ -2443,10 +2550,26 @@ TEST_CASE("Node_GetValue") { REQUIRE(list_node_itr->is_boolean()); REQUIRE(list_node_itr->get_value() == false); + std::list list_node_inplace {}; + node.get_value_inplace(list_node_inplace); + REQUIRE(list_node_inplace.size() == 2); + auto list_node_inplace_itr = list_node_inplace.begin(); + REQUIRE(list_node_inplace_itr->is_boolean()); + REQUIRE(list_node_inplace_itr->get_value() == true); + list_node_inplace_itr++; + REQUIRE(list_node_inplace_itr->is_boolean()); + REQUIRE(list_node_inplace_itr->get_value() == false); + auto list_bool = node.get_value>(); REQUIRE(list_bool.size() == 2); REQUIRE(*list_bool.begin() == true); REQUIRE(*(std::next(list_bool.begin())) == false); + + std::list list_bool_inplace {}; + node.get_value_inplace(list_bool_inplace); + REQUIRE(list_bool_inplace.size() == 2); + REQUIRE(*list_bool_inplace.begin() == true); + REQUIRE(*(std::next(list_bool_inplace.begin())) == false); } SECTION("sequence value (std::set)") { @@ -2455,10 +2578,22 @@ TEST_CASE("Node_GetValue") { REQUIRE(set_node.find(fkyaml::node(true)) != set_node.end()); REQUIRE(set_node.find(fkyaml::node(false)) != set_node.end()); + std::set set_node_inplace {}; + node.get_value_inplace(set_node_inplace); + REQUIRE(set_node_inplace.size() == 2); + REQUIRE(set_node_inplace.find(fkyaml::node(true)) != set_node_inplace.end()); + REQUIRE(set_node_inplace.find(fkyaml::node(false)) != set_node_inplace.end()); + auto set_bool = node.get_value>(); REQUIRE(set_bool.size() == 2); REQUIRE(set_bool.find(true) != set_bool.end()); REQUIRE(set_bool.find(false) != set_bool.end()); + + std::set set_bool_inplace {}; + node.get_value_inplace(set_bool_inplace); + REQUIRE(set_bool_inplace.size() == 2); + REQUIRE(set_bool_inplace.find(true) != set_bool_inplace.end()); + REQUIRE(set_bool_inplace.find(false) != set_bool_inplace.end()); } SECTION("sequence value (std::multiset)") { @@ -2467,10 +2602,22 @@ TEST_CASE("Node_GetValue") { REQUIRE(mset_node.find(fkyaml::node(true)) != mset_node.end()); REQUIRE(mset_node.find(fkyaml::node(false)) != mset_node.end()); + std::multiset mset_node_inplace {}; + node.get_value_inplace(mset_node_inplace); + REQUIRE(mset_node_inplace.size() == 2); + REQUIRE(mset_node_inplace.find(fkyaml::node(true)) != mset_node_inplace.end()); + REQUIRE(mset_node_inplace.find(fkyaml::node(false)) != mset_node_inplace.end()); + auto mset_bool = node.get_value>(); REQUIRE(mset_bool.size() == 2); REQUIRE(mset_bool.find(true) != mset_bool.end()); REQUIRE(mset_bool.find(false) != mset_bool.end()); + + std::multiset mset_bool_inplace {}; + node.get_value_inplace(mset_bool_inplace); + REQUIRE(mset_bool_inplace.size() == 2); + REQUIRE(mset_bool_inplace.find(true) != mset_bool_inplace.end()); + REQUIRE(mset_bool_inplace.find(false) != mset_bool_inplace.end()); } SECTION("sequence value (std::unordered_set)") { @@ -2479,10 +2626,22 @@ TEST_CASE("Node_GetValue") { REQUIRE(uset_node.find(fkyaml::node(true)) != uset_node.end()); REQUIRE(uset_node.find(fkyaml::node(false)) != uset_node.end()); + std::unordered_set uset_node_inplace {}; + node.get_value_inplace(uset_node_inplace); + REQUIRE(uset_node_inplace.size() == 2); + REQUIRE(uset_node_inplace.find(fkyaml::node(true)) != uset_node_inplace.end()); + REQUIRE(uset_node_inplace.find(fkyaml::node(false)) != uset_node_inplace.end()); + auto uset_bool = node.get_value>(); REQUIRE(uset_bool.size() == 2); REQUIRE(uset_bool.find(true) != uset_bool.end()); REQUIRE(uset_bool.find(false) != uset_bool.end()); + + std::unordered_set uset_bool_inplace {}; + node.get_value_inplace(uset_bool_inplace); + REQUIRE(uset_bool_inplace.size() == 2); + REQUIRE(uset_bool_inplace.find(true) != uset_bool_inplace.end()); + REQUIRE(uset_bool_inplace.find(false) != uset_bool_inplace.end()); } SECTION("sequence value (std::unordered_set)") { @@ -2491,10 +2650,22 @@ TEST_CASE("Node_GetValue") { REQUIRE(umset_node.find(fkyaml::node(true)) != umset_node.end()); REQUIRE(umset_node.find(fkyaml::node(false)) != umset_node.end()); + std::unordered_multiset umset_node_inplace {}; + node.get_value_inplace(umset_node_inplace); + REQUIRE(umset_node_inplace.size() == 2); + REQUIRE(umset_node_inplace.find(fkyaml::node(true)) != umset_node_inplace.end()); + REQUIRE(umset_node_inplace.find(fkyaml::node(false)) != umset_node_inplace.end()); + auto umset_bool = node.get_value>(); REQUIRE(umset_bool.size() == 2); REQUIRE(umset_bool.find(true) != umset_bool.end()); REQUIRE(umset_bool.find(false) != umset_bool.end()); + + std::unordered_multiset umset_bool_inplace {}; + node.get_value_inplace(umset_bool_inplace); + REQUIRE(umset_bool_inplace.size() == 2); + REQUIRE(umset_bool_inplace.find(true) != umset_bool_inplace.end()); + REQUIRE(umset_bool_inplace.find(false) != umset_bool_inplace.end()); } SECTION("sequence value (std::stack)") { @@ -2506,11 +2677,27 @@ TEST_CASE("Node_GetValue") { REQUIRE(stack_node.top().is_boolean()); REQUIRE(stack_node.top().get_value() == true); + std::stack stack_node_inplace {}; + node.get_value_inplace(stack_node_inplace); + REQUIRE(stack_node_inplace.size() == 2); + REQUIRE(stack_node_inplace.top().is_boolean()); + REQUIRE(stack_node_inplace.top().get_value() == false); + stack_node_inplace.pop(); + REQUIRE(stack_node_inplace.top().is_boolean()); + REQUIRE(stack_node_inplace.top().get_value() == true); + auto stack_bool = node.get_value>(); REQUIRE(stack_bool.size() == 2); REQUIRE(stack_bool.top() == false); stack_bool.pop(); REQUIRE(stack_bool.top() == true); + + std::stack stack_bool_inplace {}; + node.get_value_inplace(stack_bool_inplace); + REQUIRE(stack_bool_inplace.size() == 2); + REQUIRE(stack_bool_inplace.top() == false); + stack_bool_inplace.pop(); + REQUIRE(stack_bool_inplace.top() == true); } SECTION("sequence value (std::queue)") { @@ -2522,14 +2709,30 @@ TEST_CASE("Node_GetValue") { REQUIRE(queue_node.front().is_boolean()); REQUIRE(queue_node.front().get_value() == false); + std::queue queue_node_inplace {}; + node.get_value_inplace(queue_node_inplace); + REQUIRE(queue_node_inplace.size() == 2); + REQUIRE(queue_node_inplace.front().is_boolean()); + REQUIRE(queue_node_inplace.front().get_value() == true); + queue_node_inplace.pop(); + REQUIRE(queue_node_inplace.front().is_boolean()); + REQUIRE(queue_node_inplace.front().get_value() == false); + auto queue_bool = node.get_value>(); REQUIRE(queue_bool.size() == 2); REQUIRE(queue_bool.front() == true); queue_bool.pop(); REQUIRE(queue_bool.front() == false); + + std::queue queue_bool_inplace {}; + node.get_value_inplace(queue_bool_inplace); + REQUIRE(queue_bool_inplace.size() == 2); + REQUIRE(queue_bool_inplace.front() == true); + queue_bool_inplace.pop(); + REQUIRE(queue_bool_inplace.front() == false); } - SECTION("sequence value (std::queue)") { + SECTION("sequence value (std::priority_queue)") { auto pqueue_node = node.get_value>(); REQUIRE(pqueue_node.size() == 2); REQUIRE(pqueue_node.top().is_boolean()); @@ -2538,11 +2741,27 @@ TEST_CASE("Node_GetValue") { REQUIRE(pqueue_node.top().is_boolean()); REQUIRE(pqueue_node.top().get_value() == false); + std::priority_queue pqueue_node_inplace {}; + node.get_value_inplace(pqueue_node_inplace); + REQUIRE(pqueue_node_inplace.size() == 2); + REQUIRE(pqueue_node_inplace.top().is_boolean()); + REQUIRE(pqueue_node_inplace.top().get_value() == true); + pqueue_node_inplace.pop(); + REQUIRE(pqueue_node_inplace.top().is_boolean()); + REQUIRE(pqueue_node_inplace.top().get_value() == false); + auto pqueue_bool = node.get_value>(); REQUIRE(pqueue_bool.size() == 2); REQUIRE(pqueue_bool.top() == true); pqueue_bool.pop(); REQUIRE(pqueue_bool.top() == false); + + std::priority_queue pqueue_bool_inplace {}; + node.get_value_inplace(pqueue_bool_inplace); + REQUIRE(pqueue_bool_inplace.size() == 2); + REQUIRE(pqueue_bool_inplace.top() == true); + pqueue_bool_inplace.pop(); + REQUIRE(pqueue_bool_inplace.top() == false); } SECTION("non-sequence value") { @@ -2569,12 +2788,30 @@ TEST_CASE("Node_GetValue") { REQUIRE(map_node.at("foo").is_integer()); REQUIRE(map_node.at("foo").get_value() == -456); + std::map map_node_inplace {}; + node.get_value_inplace(map_node_inplace); + REQUIRE(map_node_inplace.size() == 2); + REQUIRE(map_node_inplace.find("test") != map_node_inplace.end()); + REQUIRE(map_node_inplace.at("test").is_integer()); + REQUIRE(map_node_inplace.at("test").get_value() == 123); + REQUIRE(map_node_inplace.find("foo") != map_node_inplace.end()); + REQUIRE(map_node_inplace.at("foo").is_integer()); + REQUIRE(map_node_inplace.at("foo").get_value() == -456); + auto map_compat = node.get_value>(); REQUIRE(map_compat.size() == 2); REQUIRE(map_compat.find("test") != map_compat.end()); REQUIRE(map_compat.at("test") == 123); REQUIRE(map_compat.find("foo") != map_compat.end()); REQUIRE(map_compat.at("foo") == -456); + + std::map map_compat_inplace {}; + node.get_value_inplace(map_compat_inplace); + REQUIRE(map_compat_inplace.size() == 2); + REQUIRE(map_compat_inplace.find("test") != map_compat_inplace.end()); + REQUIRE(map_compat_inplace.at("test") == 123); + REQUIRE(map_compat_inplace.find("foo") != map_compat_inplace.end()); + REQUIRE(map_compat_inplace.at("foo") == -456); } SECTION("mapping value (std::multimap)") { @@ -2591,6 +2828,20 @@ TEST_CASE("Node_GetValue") { REQUIRE(mmap_node_foo_range.first->second.is_integer()); REQUIRE(mmap_node_foo_range.first->second.get_value() == -456); + std::multimap mmap_node_inplace {}; + node.get_value_inplace(mmap_node_inplace); + REQUIRE(mmap_node_inplace.size() == 2); + REQUIRE(mmap_node_inplace.find("test") != mmap_node_inplace.end()); + auto mmap_node_inplace_test_range = mmap_node_inplace.equal_range("test"); + REQUIRE(std::distance(mmap_node_inplace_test_range.first, mmap_node_inplace_test_range.second) == 1); + REQUIRE(mmap_node_inplace_test_range.first->second.is_integer()); + REQUIRE(mmap_node_inplace_test_range.first->second.get_value() == 123); + REQUIRE(mmap_node_inplace.find("foo") != mmap_node_inplace.end()); + auto mmap_node_inplace_foo_range = mmap_node_inplace.equal_range("foo"); + REQUIRE(std::distance(mmap_node_inplace_test_range.first, mmap_node_inplace_test_range.second) == 1); + REQUIRE(mmap_node_inplace_foo_range.first->second.is_integer()); + REQUIRE(mmap_node_inplace_foo_range.first->second.get_value() == -456); + auto mmap_compat = node.get_value>(); REQUIRE(mmap_compat.size() == 2); REQUIRE(mmap_compat.find("test") != mmap_compat.end()); @@ -2601,6 +2852,18 @@ TEST_CASE("Node_GetValue") { auto mmap_compat_foo_range = mmap_compat.equal_range("foo"); REQUIRE(std::distance(mmap_compat_test_range.first, mmap_compat_test_range.second) == 1); REQUIRE(mmap_compat_foo_range.first->second == -456); + + std::multimap mmap_compat_inplace {}; + node.get_value_inplace(mmap_compat_inplace); + REQUIRE(mmap_compat_inplace.size() == 2); + REQUIRE(mmap_compat_inplace.find("test") != mmap_compat_inplace.end()); + auto mmap_compat_inplace_test_range = mmap_compat_inplace.equal_range("test"); + REQUIRE(std::distance(mmap_compat_inplace_test_range.first, mmap_compat_inplace_test_range.second) == 1); + REQUIRE(mmap_compat_inplace_test_range.first->second == 123); + REQUIRE(mmap_compat_inplace.find("foo") != mmap_compat_inplace.end()); + auto mmap_compat_inplace_foo_range = mmap_compat_inplace.equal_range("foo"); + REQUIRE(std::distance(mmap_compat_inplace_test_range.first, mmap_compat_inplace_test_range.second) == 1); + REQUIRE(mmap_compat_inplace_foo_range.first->second == -456); } SECTION("mapping value (std::unordered_map)") { @@ -2613,6 +2876,16 @@ TEST_CASE("Node_GetValue") { REQUIRE(umap_node.at("foo").is_integer()); REQUIRE(umap_node.at("foo").get_value() == -456); + std::unordered_map umap_node_inplace {}; + node.get_value_inplace(umap_node_inplace); + REQUIRE(umap_node_inplace.size() == 2); + REQUIRE(umap_node_inplace.find("test") != umap_node_inplace.end()); + REQUIRE(umap_node_inplace.at("test").is_integer()); + REQUIRE(umap_node_inplace.at("test").get_value() == 123); + REQUIRE(umap_node_inplace.find("foo") != umap_node_inplace.end()); + REQUIRE(umap_node_inplace.at("foo").is_integer()); + REQUIRE(umap_node_inplace.at("foo").get_value() == -456); + auto umap_compat = node.get_value>(); REQUIRE(umap_compat.size() == 2); REQUIRE(umap_compat.find("test") != umap_compat.end()); @@ -2620,16 +2893,13 @@ TEST_CASE("Node_GetValue") { REQUIRE(umap_compat.find("foo") != umap_compat.end()); REQUIRE(umap_compat.at("foo") == -456); - fkyaml::node various_type_nodes = { - {nullptr, nullptr}, - {true, nullptr}, - {123, nullptr}, - {3.14, nullptr}, - {"foo", nullptr}, - {{{"foo", "bar"}}, nullptr}, - {{"foo", "bar"}, nullptr}, - }; - auto umap = various_type_nodes.get_value>(); + std::unordered_map umap_compat_inplace {}; + node.get_value_inplace(umap_compat_inplace); + REQUIRE(umap_compat_inplace.size() == 2); + REQUIRE(umap_compat_inplace.find("test") != umap_compat_inplace.end()); + REQUIRE(umap_compat_inplace.at("test") == 123); + REQUIRE(umap_compat_inplace.find("foo") != umap_compat_inplace.end()); + REQUIRE(umap_compat_inplace.at("foo") == -456); } SECTION("mapping value (std::unordered_multimap)") { @@ -2646,6 +2916,20 @@ TEST_CASE("Node_GetValue") { REQUIRE(ummap_node_foo_range.first->second.is_integer()); REQUIRE(ummap_node_foo_range.first->second.get_value() == -456); + std::unordered_multimap ummap_node_inplace {}; + node.get_value_inplace(ummap_node_inplace); + REQUIRE(ummap_node_inplace.size() == 2); + REQUIRE(ummap_node_inplace.find("test") != ummap_node_inplace.end()); + auto ummap_node_inplace_test_range = ummap_node_inplace.equal_range("test"); + REQUIRE(std::distance(ummap_node_inplace_test_range.first, ummap_node_inplace_test_range.second) == 1); + REQUIRE(ummap_node_inplace_test_range.first->second.is_integer()); + REQUIRE(ummap_node_inplace_test_range.first->second.get_value() == 123); + REQUIRE(ummap_node_inplace.find("foo") != ummap_node_inplace.end()); + auto ummap_node_inplace_foo_range = ummap_node_inplace.equal_range("foo"); + REQUIRE(std::distance(ummap_node_inplace_test_range.first, ummap_node_inplace_test_range.second) == 1); + REQUIRE(ummap_node_inplace_foo_range.first->second.is_integer()); + REQUIRE(ummap_node_inplace_foo_range.first->second.get_value() == -456); + auto ummap_compat = node.get_value>(); REQUIRE(ummap_compat.size() == 2); REQUIRE(ummap_compat.find("test") != ummap_compat.end()); @@ -2656,6 +2940,18 @@ TEST_CASE("Node_GetValue") { auto ummap_compat_foo_range = ummap_compat.equal_range("foo"); REQUIRE(std::distance(ummap_compat_test_range.first, ummap_compat_test_range.second) == 1); REQUIRE(ummap_compat_foo_range.first->second == -456); + + std::unordered_multimap ummap_compat_inplace {}; + node.get_value_inplace(ummap_compat_inplace); + REQUIRE(ummap_compat_inplace.size() == 2); + REQUIRE(ummap_compat_inplace.find("test") != ummap_compat_inplace.end()); + auto ummap_compat_inplace_test_range = ummap_compat_inplace.equal_range("test"); + REQUIRE(std::distance(ummap_compat_inplace_test_range.first, ummap_compat_inplace_test_range.second) == 1); + REQUIRE(ummap_compat_inplace_test_range.first->second == 123); + REQUIRE(ummap_compat_inplace.find("foo") != ummap_compat_inplace.end()); + auto ummap_compat_inplace_foo_range = ummap_compat_inplace.equal_range("foo"); + REQUIRE(std::distance(ummap_compat_inplace_test_range.first, ummap_compat_inplace_test_range.second) == 1); + REQUIRE(ummap_compat_inplace_foo_range.first->second == -456); } SECTION("non-mapping values") { @@ -2683,21 +2979,72 @@ TEST_CASE("Node_GetValue") { SECTION("null type") { auto null = node.get_value(); REQUIRE(null == nullptr); + + std::nullptr_t null_inplace; + node.get_value_inplace(null_inplace); + REQUIRE(null_inplace == nullptr); } SECTION("non-null compatible types") { REQUIRE(node.get_value() == false); + bool bool_inplace = true; + node.get_value_inplace(bool_inplace); + REQUIRE(bool_inplace == false); + REQUIRE(node.get_value() == 0); + uint8_t ui8_inplace = 1; + node.get_value_inplace(ui8_inplace); + REQUIRE(ui8_inplace == 0); + REQUIRE(node.get_value() == 0); + uint16_t ui16_inplace = 1; + node.get_value_inplace(ui16_inplace); + REQUIRE(ui16_inplace == 0); + REQUIRE(node.get_value() == 0); + uint32_t ui32_inplace = 1; + node.get_value_inplace(ui32_inplace); + REQUIRE(ui32_inplace == 0); + REQUIRE(node.get_value() == 0); + uint64_t ui64_inplace = 1; + node.get_value_inplace(ui64_inplace); + REQUIRE(ui64_inplace == 0); + REQUIRE(node.get_value() == 0); + int8_t i8_inplace = 1; + node.get_value_inplace(i8_inplace); + REQUIRE(i8_inplace == 0); + REQUIRE(node.get_value() == 0); + int16_t i16_inplace = 1; + node.get_value_inplace(i16_inplace); + REQUIRE(i16_inplace == 0); + REQUIRE(node.get_value() == 0); + int32_t i32_inplace = 1; + node.get_value_inplace(i32_inplace); + REQUIRE(i32_inplace == 0); + REQUIRE(node.get_value() == 0); + int64_t i64_inplace = 1; + node.get_value_inplace(i64_inplace); + REQUIRE(i64_inplace == 0); + REQUIRE(node.get_value() == 0.f); + float float_inplace = 1; + node.get_value_inplace(float_inplace); + REQUIRE(float_inplace == 0.f); + REQUIRE(node.get_value() == 0.); + double double_inplace = 1; + node.get_value_inplace(double_inplace); + REQUIRE(double_inplace == 0.); + REQUIRE(node.get_value() == 0.l); + long double long_double_inplace = 1; + node.get_value_inplace(long_double_inplace); + REQUIRE(long_double_inplace == 0.l); } SECTION("non-null incompatible types") { @@ -2715,32 +3062,127 @@ TEST_CASE("Node_GetValue") { SECTION("boolean type") { REQUIRE(true_node.get_value() == true); REQUIRE(false_node.get_value() == false); + + bool true_inplace = false; + bool false_inplace = true; + true_node.get_value_inplace(true_inplace); + false_node.get_value_inplace(false_inplace); + REQUIRE(true_inplace == true); + REQUIRE(false_inplace == false); } - SECTION("non-boolean compatible types") { + SECTION("non-boolean compatible types (true)") { REQUIRE(true_node.get_value() == 1); + uint8_t ui8_inplace = 0; + true_node.get_value_inplace(ui8_inplace); + REQUIRE(ui8_inplace == 1); + REQUIRE(true_node.get_value() == 1); + uint16_t ui16_inplace = 0; + true_node.get_value_inplace(ui16_inplace); + REQUIRE(ui16_inplace == 1); + REQUIRE(true_node.get_value() == 1); + uint32_t ui32_inplace = 0; + true_node.get_value_inplace(ui32_inplace); + REQUIRE(ui32_inplace == 1); + REQUIRE(true_node.get_value() == 1); + uint64_t ui64_inplace = 0; + true_node.get_value_inplace(ui64_inplace); + REQUIRE(ui64_inplace == 1); + REQUIRE(true_node.get_value() == 1); + int8_t i8_inplace = 0; + true_node.get_value_inplace(i8_inplace); + REQUIRE(i8_inplace == 1); + REQUIRE(true_node.get_value() == 1); + int16_t i16_inplace = 0; + true_node.get_value_inplace(i16_inplace); + REQUIRE(i16_inplace == 1); + REQUIRE(true_node.get_value() == 1); + int32_t i32_inplace = 0; + true_node.get_value_inplace(i32_inplace); + REQUIRE(i32_inplace == 1); + REQUIRE(true_node.get_value() == 1); + int64_t i64_inplace = 0; + true_node.get_value_inplace(i64_inplace); + REQUIRE(i64_inplace == 1); + REQUIRE(true_node.get_value() == 1.f); + float float_inplace = 0; + true_node.get_value_inplace(float_inplace); + REQUIRE(float_inplace == 1.f); + REQUIRE(true_node.get_value() == 1.); + double double_inplace = 0; + true_node.get_value_inplace(double_inplace); + REQUIRE(double_inplace == 1.); + REQUIRE(true_node.get_value() == 1.l); + long double long_double_inplace = 0; + true_node.get_value_inplace(long_double_inplace); + REQUIRE(long_double_inplace == 1.l); + } + SECTION("non-boolean compatible types (false)") { REQUIRE(false_node.get_value() == 0); + uint8_t ui8_inplace = 1; + false_node.get_value_inplace(ui8_inplace); + REQUIRE(ui8_inplace == 0); + REQUIRE(false_node.get_value() == 0); + uint16_t ui16_inplace = 1; + false_node.get_value_inplace(ui16_inplace); + REQUIRE(ui16_inplace == 0); + REQUIRE(false_node.get_value() == 0); + uint32_t ui32_inplace = 1; + false_node.get_value_inplace(ui32_inplace); + REQUIRE(ui32_inplace == 0); + REQUIRE(false_node.get_value() == 0); + uint64_t ui64_inplace = 1; + false_node.get_value_inplace(ui64_inplace); + REQUIRE(ui64_inplace == 0); + REQUIRE(false_node.get_value() == 0); + int8_t i8_inplace = 1; + false_node.get_value_inplace(i8_inplace); + REQUIRE(i8_inplace == 0); + REQUIRE(false_node.get_value() == 0); + int16_t i16_inplace = 1; + false_node.get_value_inplace(i16_inplace); + REQUIRE(i16_inplace == 0); + REQUIRE(false_node.get_value() == 0); + int32_t i32_inplace = 1; + false_node.get_value_inplace(i32_inplace); + REQUIRE(i32_inplace == 0); + REQUIRE(false_node.get_value() == 0); + int64_t i64_inplace = 1; + false_node.get_value_inplace(i64_inplace); + REQUIRE(i64_inplace == 0); + REQUIRE(false_node.get_value() == 0.f); + float float_inplace = 1; + false_node.get_value_inplace(float_inplace); + REQUIRE(float_inplace == 0.f); + REQUIRE(false_node.get_value() == 0.); + double double_inplace = 1; + false_node.get_value_inplace(double_inplace); + REQUIRE(double_inplace == 0.); + REQUIRE(false_node.get_value() == 0.l); + long double long_double_inplace = 1; + false_node.get_value_inplace(long_double_inplace); + REQUIRE(long_double_inplace == 0.l); } SECTION("non-boolean incompatible types") { @@ -2757,32 +3199,112 @@ TEST_CASE("Node_GetValue") { SECTION("integer types") { REQUIRE(node.get_value() == 123); + int8_t i8_inplace = 0; + node.get_value_inplace(i8_inplace); + REQUIRE(i8_inplace == 123); + REQUIRE(node.get_value() == 123); + int16_t i16_inplace = 0; + node.get_value_inplace(i16_inplace); + REQUIRE(i16_inplace == 123); + REQUIRE(node.get_value() == 123); + int32_t i32_inplace = 0; + node.get_value_inplace(i32_inplace); + REQUIRE(i32_inplace == 123); + REQUIRE(node.get_value() == 123); + int64_t i64_inplace = 0; + node.get_value_inplace(i64_inplace); + REQUIRE(i64_inplace == 123); + REQUIRE(node.get_value() == 123); + uint8_t ui8_inplace = 0; + node.get_value_inplace(ui8_inplace); + REQUIRE(ui8_inplace == 123); + REQUIRE(node.get_value() == 123); + uint16_t ui16_inplace = 0; + node.get_value_inplace(ui16_inplace); + REQUIRE(ui16_inplace == 123); + REQUIRE(node.get_value() == 123); + uint32_t ui32_inplace = 0; + node.get_value_inplace(ui32_inplace); + REQUIRE(ui32_inplace == 123); + REQUIRE(node.get_value() == 123); + uint64_t ui64_inplace = 0; + node.get_value_inplace(ui64_inplace); + REQUIRE(ui64_inplace == 123); } - SECTION("non-integer compatible types") { + SECTION("non-integer compatible types (positive)") { REQUIRE(node.get_value() == true); + bool bool_inplace = false; + node.get_value_inplace(bool_inplace); + REQUIRE(bool_inplace == true); + REQUIRE(node.get_value() == 123.f); + float float_inplace = 0.f; + node.get_value_inplace(float_inplace); + REQUIRE(float_inplace == 123.f); + REQUIRE(node.get_value() == 123.); + double double_inplace = 0.; + node.get_value_inplace(double_inplace); + REQUIRE(double_inplace == 123.); + REQUIRE(node.get_value() == 123.l); + long double long_double_inplace = 0.l; + node.get_value_inplace(long_double_inplace); + REQUIRE(long_double_inplace == 123.l); + } + SECTION("non-integer compatible types (negative)") { node = -123; REQUIRE(node.get_value() == true); + bool bool_inplace = false; + node.get_value_inplace(bool_inplace); + REQUIRE(bool_inplace == true); + REQUIRE(node.get_value() == -123.f); + float float_inplace = 0.f; + node.get_value_inplace(float_inplace); + REQUIRE(float_inplace == -123.f); + REQUIRE(node.get_value() == -123.); + double double_inplace = 0.; + node.get_value_inplace(double_inplace); + REQUIRE(double_inplace == -123.); + REQUIRE(node.get_value() == -123.l); + long double long_double_inplace = 0.l; + node.get_value_inplace(long_double_inplace); + REQUIRE(long_double_inplace == -123.l); + } + SECTION("non-integer compatible types (zero)") { node = 0; REQUIRE(node.get_value() == false); + bool bool_inplace = true; + node.get_value_inplace(bool_inplace); + REQUIRE(bool_inplace == false); + REQUIRE(node.get_value() == 0.f); + float float_inplace = 1.f; + node.get_value_inplace(float_inplace); + REQUIRE(float_inplace == 0.f); + REQUIRE(node.get_value() == 0.); + double double_inplace = 1.; + node.get_value_inplace(double_inplace); + REQUIRE(double_inplace == 0.); + REQUIRE(node.get_value() == 0.l); + long double long_double_inplace = 1.l; + node.get_value_inplace(long_double_inplace); + REQUIRE(long_double_inplace == 0.l); } SECTION("non-integer incompatible types") { @@ -2810,52 +3332,178 @@ TEST_CASE("Node_GetValue") { SECTION("positive float values") { REQUIRE(std::abs(node.get_value() - 3.14) < std::numeric_limits::epsilon()); + float float_inplace = 0.f; + node.get_value_inplace(float_inplace); + REQUIRE(std::abs(float_inplace - 3.14) < std::numeric_limits::epsilon()); + REQUIRE(std::abs(node.get_value() - 3.14) < std::numeric_limits::epsilon()); + double double_inplace = 0.; + node.get_value_inplace(double_inplace); + REQUIRE(std::abs(double_inplace - 3.14) < std::numeric_limits::epsilon()); + REQUIRE(std::abs(node.get_value() - 3.14) < std::numeric_limits::epsilon()); + long double long_double_inplace = 0.; + node.get_value_inplace(long_double_inplace); + REQUIRE(std::abs(long_double_inplace - 3.14) < std::numeric_limits::epsilon()); } SECTION("zero float values") { node = 0.0; REQUIRE(std::abs(node.get_value() - 0.0) < std::numeric_limits::epsilon()); + float float_inplace = 1.f; + node.get_value_inplace(float_inplace); + REQUIRE(std::abs(float_inplace - 0.0) < std::numeric_limits::epsilon()); + REQUIRE(std::abs(node.get_value() - 0.0) < std::numeric_limits::epsilon()); + double double_inplace = 1.; + node.get_value_inplace(double_inplace); + REQUIRE(std::abs(double_inplace - 0.0) < std::numeric_limits::epsilon()); + REQUIRE(std::abs(node.get_value() - 0.0) < std::numeric_limits::epsilon()); + long double long_double_inplace = 1.l; + node.get_value_inplace(long_double_inplace); + REQUIRE(std::abs(long_double_inplace - 0.0) < std::numeric_limits::epsilon()); } SECTION("negative float values") { node = -3.14; REQUIRE(std::abs(node.get_value() - (-3.14)) < std::numeric_limits::epsilon()); + float float_inplace = 0.f; + node.get_value_inplace(float_inplace); + REQUIRE(std::abs(float_inplace - (-3.14)) < std::numeric_limits::epsilon()); + REQUIRE(std::abs(node.get_value() - (-3.14)) < std::numeric_limits::epsilon()); + double double_inplace = 0.; + node.get_value_inplace(double_inplace); + REQUIRE(std::abs(double_inplace - (-3.14)) < std::numeric_limits::epsilon()); + REQUIRE(std::abs(node.get_value() - (-3.14)) < std::numeric_limits::epsilon()); + long double long_double_inplace = 0.; + node.get_value_inplace(long_double_inplace); + REQUIRE(std::abs(long_double_inplace - (-3.14)) < std::numeric_limits::epsilon()); } - SECTION("non-float compatible types") { + SECTION("non-float compatible types (positive)") { REQUIRE(node.get_value() == true); + bool bool_inplace = false; + node.get_value_inplace(bool_inplace); + REQUIRE(bool_inplace == true); + REQUIRE(node.get_value() == 3); + uint8_t ui8_inplace = 0; + node.get_value_inplace(ui8_inplace); + REQUIRE(ui8_inplace == 3); + REQUIRE(node.get_value() == 3); + uint16_t ui16_inplace = 0; + node.get_value_inplace(ui16_inplace); + REQUIRE(ui16_inplace == 3); + REQUIRE(node.get_value() == 3); + uint32_t ui32_inplace = 0; + node.get_value_inplace(ui32_inplace); + REQUIRE(ui32_inplace == 3); + REQUIRE(node.get_value() == 3); + uint64_t ui64_inplace = 0; + node.get_value_inplace(ui64_inplace); + REQUIRE(ui64_inplace == 3); + REQUIRE(node.get_value() == 3); + int8_t i8_inplace = 0; + node.get_value_inplace(i8_inplace); + REQUIRE(i8_inplace == 3); + REQUIRE(node.get_value() == 3); + int16_t i16_inplace = 0; + node.get_value_inplace(i16_inplace); + REQUIRE(i16_inplace == 3); + REQUIRE(node.get_value() == 3); + int32_t i32_inplace = 0; + node.get_value_inplace(i32_inplace); + REQUIRE(i32_inplace == 3); + REQUIRE(node.get_value() == 3); + int64_t i64_inplace = 0; + node.get_value_inplace(i64_inplace); + REQUIRE(i64_inplace == 3); + } + SECTION("non-float compatible types (negative)") { node = -3.14; REQUIRE(node.get_value() == true); + bool bool_inplace = false; + node.get_value_inplace(bool_inplace); + REQUIRE(bool_inplace == true); + REQUIRE(node.get_value() == -3); + int8_t i8_inplace = 0; + node.get_value_inplace(i8_inplace); + REQUIRE(i8_inplace == -3); + REQUIRE(node.get_value() == -3); + int16_t i16_inplace = 0; + node.get_value_inplace(i16_inplace); + REQUIRE(i16_inplace == -3); + REQUIRE(node.get_value() == -3); + int32_t i32_inplace = 0; + node.get_value_inplace(i32_inplace); + REQUIRE(i32_inplace == -3); + REQUIRE(node.get_value() == -3); + int64_t i64_inplace = 0; + node.get_value_inplace(i64_inplace); + REQUIRE(i64_inplace == -3); + } + SECTION("non-float compatible types (zero)") { node = 0.0; REQUIRE(node.get_value() == false); + bool bool_inplace = true; + node.get_value_inplace(bool_inplace); + REQUIRE(bool_inplace == false); + REQUIRE(node.get_value() == 0); + uint8_t ui8_inplace = 1; + node.get_value_inplace(ui8_inplace); + REQUIRE(ui8_inplace == 0); + REQUIRE(node.get_value() == 0); + uint16_t ui16_inplace = 1; + node.get_value_inplace(ui16_inplace); + REQUIRE(ui16_inplace == 0); + REQUIRE(node.get_value() == 0); + uint32_t ui32_inplace = 1; + node.get_value_inplace(ui32_inplace); + REQUIRE(ui32_inplace == 0); + REQUIRE(node.get_value() == 0); + uint64_t ui64_inplace = 1; + node.get_value_inplace(ui64_inplace); + REQUIRE(ui64_inplace == 0); + REQUIRE(node.get_value() == 0); + int8_t i8_inplace = 1; + node.get_value_inplace(i8_inplace); + REQUIRE(i8_inplace == 0); + REQUIRE(node.get_value() == 0); + int16_t i16_inplace = 1; + node.get_value_inplace(i16_inplace); + REQUIRE(i16_inplace == 0); + REQUIRE(node.get_value() == 0); + int32_t i32_inplace = 1; + node.get_value_inplace(i32_inplace); + REQUIRE(i32_inplace == 0); + REQUIRE(node.get_value() == 0); + int64_t i64_inplace = 1; + node.get_value_inplace(i64_inplace); + REQUIRE(i64_inplace == 0); } SECTION("non-float incompatible types") { @@ -2893,18 +3541,33 @@ TEST_CASE("Node_GetValue") { auto str = node.get_value(); REQUIRE(str.size() == 4); REQUIRE(str == "test"); + + std::string str_inplace {}; + node.get_value_inplace(str_inplace); + REQUIRE(str_inplace.size() == 4); + REQUIRE(str_inplace == "test"); } SECTION("string value (string_wrap)") { auto str_wrap = node.get_value(); REQUIRE(str_wrap.str.size() == 4); REQUIRE(str_wrap.str == "test"); + + string_wrap str_wrap_inplace {}; + node.get_value_inplace(str_wrap_inplace); + REQUIRE(str_wrap_inplace.str.size() == 4); + REQUIRE(str_wrap_inplace.str == "test"); } SECTION("string value (fkyaml::detail::str_view)") { auto str_view = node.get_value(); REQUIRE(str_view.size() == 4); REQUIRE(str_view == "test"); + + fkyaml::detail::str_view str_view_inplace {}; + node.get_value_inplace(str_view_inplace); + REQUIRE(str_view_inplace.size() == 4); + REQUIRE(str_view_inplace == "test"); } #ifdef FK_YAML_HAS_CXX_17 @@ -2912,6 +3575,11 @@ TEST_CASE("Node_GetValue") { auto str_view = node.get_value(); REQUIRE(str_view.size() == 4); REQUIRE(str_view == "test"); + + std::string_view str_view_inplace {}; + node.get_value_inplace(str_view_inplace); + REQUIRE(str_view_inplace.size() == 4); + REQUIRE(str_view_inplace == "test"); } #endif @@ -2934,9 +3602,21 @@ TEST_CASE("Node_GetValue") { REQUIRE(pair_node.second.is_string()); REQUIRE(pair_node.second.get_value() == "test"); + std::pair pair_node_inplace {}; + n.get_value_inplace(pair_node_inplace); + REQUIRE(pair_node_inplace.first.is_integer()); + REQUIRE(pair_node_inplace.first.get_value() == 123); + REQUIRE(pair_node_inplace.second.is_string()); + REQUIRE(pair_node_inplace.second.get_value() == "test"); + auto pair_val = n.get_value>(); REQUIRE(pair_val.first == 123); REQUIRE(pair_val.second == "test"); + + std::pair pair_val_inplace {}; + n.get_value_inplace(pair_val_inplace); + REQUIRE(pair_val_inplace.first == 123); + REQUIRE(pair_val_inplace.second == "test"); } SECTION("std::tuple") { @@ -2950,29 +3630,79 @@ TEST_CASE("Node_GetValue") { REQUIRE(std::get<2>(tuple_node).is_boolean()); REQUIRE(std::get<2>(tuple_node).get_value() == true); + std::tuple tuple_node_inplace {}; + n.get_value_inplace(tuple_node_inplace); + REQUIRE(std::get<0>(tuple_node_inplace).is_integer()); + REQUIRE(std::get<0>(tuple_node_inplace).get_value() == 123); + REQUIRE(std::get<1>(tuple_node_inplace).is_string()); + REQUIRE(std::get<1>(tuple_node_inplace).get_value() == "test"); + REQUIRE(std::get<2>(tuple_node_inplace).is_boolean()); + REQUIRE(std::get<2>(tuple_node_inplace).get_value() == true); + auto tuple_val = n.get_value>(); REQUIRE(std::get<0>(tuple_val) == 123); REQUIRE(std::get<1>(tuple_val) == "test"); REQUIRE(std::get<2>(tuple_val) == true); + + std::tuple tuple_val_inplace {}; + n.get_value_inplace(tuple_val_inplace); + REQUIRE(std::get<0>(tuple_val_inplace) == 123); + REQUIRE(std::get<1>(tuple_val_inplace) == "test"); + REQUIRE(std::get<2>(tuple_val_inplace) == true); } #ifdef FK_YAML_HAS_CXX_17 SECTION("std::optional") { fkyaml::node n {true, false}; + auto opt_vec = n.get_value>>(); REQUIRE(opt_vec.has_value()); REQUIRE(opt_vec.value().size() == 2); REQUIRE(opt_vec.value().at(0) == true); REQUIRE(opt_vec.value().at(1) == false); + std::optional> opt_vec_inplace {}; + n.get_value_inplace(opt_vec_inplace); + REQUIRE(opt_vec_inplace.has_value()); + REQUIRE(opt_vec_inplace.value().size() == 2); + REQUIRE(opt_vec_inplace.value().at(0) == true); + REQUIRE(opt_vec_inplace.value().at(1) == false); + auto opt_bool = n.get_value>(); REQUIRE_FALSE(opt_bool.has_value()); + + std::optional opt_bool_inplace {}; + n.get_value_inplace(opt_bool_inplace); + REQUIRE_FALSE(opt_bool_inplace.has_value()); } #endif + SECTION("from alias node") { + fkyaml::node anchor = 123; + anchor.add_anchor_name("anchor"); + fkyaml::node alias = fkyaml::node::alias_of(anchor); + REQUIRE(alias.get_value() == 123); + + int int_inplace = 0; + alias.get_value_inplace(int_inplace); + REQUIRE(int_inplace == 123); + } + + SECTION("not default constructible type") { + // get_value() requires its output type to be default constructible + STATIC_REQUIRE_FALSE( + fkyaml::detail::is_detected::value); + + // but get_value_inplace() accepts types which are not default constructible. + not_default_constructible ndc(0); + fkyaml::node int_node = 1; + int_node.get_value_inplace(ndc); + REQUIRE(ndc.value == 1); + } + SECTION("unsupported types") { STATIC_REQUIRE_FALSE(fkyaml::detail::is_detected::value); - STATIC_REQUIRE_FALSE(fkyaml::detail::is_detected::value); + STATIC_REQUIRE_FALSE(fkyaml::detail::is_detected::value); STATIC_REQUIRE_FALSE(fkyaml::detail::is_detected::value); } }