diff --git a/docs/examples/ex_basic_node_get_value.cpp b/docs/examples/ex_basic_node_get_value.cpp index 418d3fae..4325b266 100644 --- a/docs/examples/ex_basic_node_get_value.cpp +++ b/docs/examples/ex_basic_node_get_value.cpp @@ -11,17 +11,23 @@ int main() { // create a YAML node. - fkyaml::node n = 123; + fkyaml::node n = 1.23; fkyaml::node n2 = "foo"; - // get references to the value. - auto int_val = n.get_value(); + // get the node value (value gets copied). + auto float_val = n.get_value(); auto str_val = n2.get_value(); - // print the values - std::cout << int_val << std::endl; + std::cout << float_val << std::endl; std::cout << str_val << std::endl; + // Numeric scalar value can be converted to other numeric types inside get_value(). + auto bool_val = n.get_value(); + auto int_val = n.get_value(); + + std::cout << std::boolalpha << bool_val << std::endl; + std::cout << int_val << std::endl; + // specifying incompatible type throws an exception try { auto float_val = n2.get_value(); diff --git a/docs/examples/ex_basic_node_get_value.output b/docs/examples/ex_basic_node_get_value.output index 1f00b91f..a1eec460 100644 --- a/docs/examples/ex_basic_node_get_value.output +++ b/docs/examples/ex_basic_node_get_value.output @@ -1,3 +1,5 @@ -123 +1.23 foo -type_error: The target node value type is not float number type. type=STRING +true +1 +type_error: The target node value type is not compatible with float number type. type=STRING diff --git a/docs/examples/ex_macros_versions.output b/docs/examples/ex_macros_versions.output index 09fd8435..7466728c 100644 --- a/docs/examples/ex_macros_versions.output +++ b/docs/examples/ex_macros_versions.output @@ -1 +1 @@ -fkYAML version 0.3.11 +fkYAML version 0.3.13 diff --git a/docs/mkdocs/docs/api/basic_node/get_value.md b/docs/mkdocs/docs/api/basic_node/get_value.md index d968eed4..e5e64491 100644 --- a/docs/mkdocs/docs/api/basic_node/get_value.md +++ b/docs/mkdocs/docs/api/basic_node/get_value.md @@ -16,7 +16,24 @@ T get_value() const noexcept( Explicit type conversion between the internally stored YAML node value and a compatible value which is [copy-constructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and [default-constructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). The conversion relies on the [`node_value_converter`](../node_value_converter/index.md)::[`from_node`](../node_value_converter/from_node.md). This API makes a copy of the value. -If the copying costs a lot, or if you need an address of the original value, then it is more suitable to call [`get_value_ref`](get_value_ref.md) instead. +If the copying costs a lot, or if you need an address of the original value, then you should call [`get_value_ref`](get_value_ref.md) instead. + +If the YAML node value is a null, boolean, integer or floating point, this function internally executes type conversion according to the following rules which all depend on the template paramter type `T`: +* If the YAML node value is a **null** (node_type::NULL_OBJECT), the value can be converted to: + * `false` (boolean) + * `0` (integer) + * `0.0` (floating point) +* If the YAML node value is a **boolean** (node_type::BOOLEAN), the value can be converted to: + * `1 /*true*/` or `0 /*false*/` (integer) + * `1.0 /*true*/` or `0.0 /*false*/` (floating point) +* If the YAML node value is a **integer** (node_type::INTEGER), the value can be converted to: + * `true /*non-0*/` or `false /*0*/` (boolean) + * `static_cast`ed floating point value (floating point) +* If the YAML node value is a **floating point** (node_type::FLOAT), the value can be converted to: + * `true /*non-0*/` or `false /*0*/` (boolean) + * `static_cast`ed integer value (integer) + +Note that those scalar type cannot be converted to a sequence, mapping, string scalar and throws a [`type_error`](../exception/type_error.md). ## **Template Parameters** diff --git a/include/fkYAML/detail/conversions/from_node.hpp b/include/fkYAML/detail/conversions/from_node.hpp index 9a9f5484..c5126c57 100644 --- a/include/fkYAML/detail/conversions/from_node.hpp +++ b/include/fkYAML/detail/conversions/from_node.hpp @@ -80,6 +80,14 @@ inline void from_node(const BasicNodeType& n, typename BasicNodeType::mapping_ty } } +/// @brief from node function for mappings whose key and value are of both compatible types. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam CompatibleKeyType Mapping key type compatible with BasicNodeType. +/// @tparam CompatibleValueType Mapping value type compatible with BasicNodeType. +/// @tparam Compare Comparator type for mapping keys. +/// @tparam Allocator Allocator type for destination mapping object. +/// @param n A node object. +/// @param m Mapping container object to store converted key/value objects. template < typename BasicNodeType, typename CompatibleKeyType, typename CompatibleValueType, typename Compare, typename Allocator, @@ -100,10 +108,10 @@ inline void from_node(const BasicNodeType& n, std::map::value, int> = 0> inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { // to ensure the target node value type is null. @@ -113,118 +121,221 @@ inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { null = nullptr; } -/// @brief from_node function for BasicNodeType::boolean_type objects. +/// @brief from_node function for booleans. /// @tparam BasicNodeType A basic_node template instance type. -/// @param n A basic_node object. -/// @param b A boolean node value object. +/// @param n A node object. +/// @param b Storage for a boolean value. template ::value, int> = 0> -inline void from_node(const BasicNodeType& n, typename BasicNodeType::boolean_type& b) { - if FK_YAML_UNLIKELY (!n.is_boolean()) { - throw type_error("The target node value type is not boolean type.", n.get_type()); +inline void from_node(const BasicNodeType& n, bool& b) { + switch (n.get_type()) { + case node_type::NULL_OBJECT: + // nullptr is converted to false just as C++ implicitly does. + b = false; + break; + case node_type::BOOLEAN: + b = static_cast(n.template get_value_ref()); + break; + case node_type::INTEGER: + // true: non-zero, false: zero + b = (n.template get_value_ref() != 0); + break; + case node_type::FLOAT: + // true: non-zero, false: zero + using float_type = typename BasicNodeType::float_number_type; + b = (n.template get_value_ref() != static_cast(0.)); + break; + case node_type::SEQUENCE: + case node_type::MAPPING: + case node_type::STRING: + default: + throw type_error("The target node value type is not compatible with boolean type.", n.get_type()); } - b = n.template get_value_ref(); } -/// @brief from_node function for BasicNodeType::integer_type objects. +/// @brief Helper struct for node-to-int conversion. /// @tparam BasicNodeType A basic_node template instance type. -/// @param n A basic_node object. -/// @param i An integer node value object. -template ::value, int> = 0> -inline void from_node(const BasicNodeType& n, typename BasicNodeType::integer_type& i) { - if FK_YAML_UNLIKELY (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.get_type()); +/// @tparam IntType Target integer value type (same as BasicNodeType::integer_type) +template < + typename BasicNodeType, typename IntType, bool = std::is_same::value> +struct from_node_int_helper { + /// @brief Convert node's integer value to the target integer type. + /// @param n A node object. + /// @return An integer value converted from the node's integer value. + static IntType convert(const BasicNodeType& n) { + return n.template get_value_ref(); } - i = n.template get_value_ref(); -} +}; + +/// @brief Helper struct for node-to-int conversion if IntType is not the node's integer value type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam IntType Target integer value type (different from BasicNodeType::integer_type) +template +struct from_node_int_helper { + /// @brief Convert node's integer value to non-uint64_t integer types. + /// @param n A node object. + /// @return An integer value converted from the node's integer value. + static IntType convert(const BasicNodeType& n) { + using node_int_type = typename BasicNodeType::integer_type; + const node_int_type tmp_int = n.template get_value_ref(); + + // under/overflow check. + if (std::is_same::value) { + if FK_YAML_UNLIKELY (tmp_int < 0) { + throw exception("Integer value underflow detected."); + } + } + else { + if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { + throw exception("Integer value underflow detected."); + } + if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { + throw exception("Integer value overflow detected."); + } + } -/// @brief from_node function for other integer objects. (i.e., not BasicNodeType::integer_type) + return static_cast(tmp_int); + } +}; + +/// @brief from_node function for integers. +/// @note If node's value is null, boolean, or float, such a value is converted into an integer internally. /// @tparam BasicNodeType A basic_node template instance type. /// @tparam IntegerType An integer value type. -/// @param n A basic_node object. -/// @param i An integer node value object. +/// @param n A node object. +/// @param i Storage for an integer value. template < typename BasicNodeType, typename IntegerType, - enable_if_t< - conjunction< - is_non_bool_integral, - negation>>::value, - int> = 0> + enable_if_t, is_non_bool_integral>::value, int> = 0> inline void from_node(const BasicNodeType& n, IntegerType& i) { - if FK_YAML_UNLIKELY (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.get_type()); - } + switch (n.get_type()) { + case node_type::NULL_OBJECT: + // nullptr is interpreted as 0 + i = static_cast(0); + break; + case node_type::BOOLEAN: + i = static_cast(n.template get_value_ref()) + ? static_cast(1) + : static_cast(0); + break; + case node_type::INTEGER: + i = from_node_int_helper::convert(n); + break; + case node_type::FLOAT: { + // int64_t should be safe to express integer part values of possible floating point types. + const auto tmp_int = + static_cast(n.template get_value_ref()); + + // under/overflow check. + if (std::is_same::value) { + if FK_YAML_UNLIKELY (tmp_int < 0) { + throw exception("Integer value underflow detected."); + } + } + else { + if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { + throw exception("Integer value underflow detected."); + } + if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { + throw exception("Integer value overflow detected."); + } + } - // under/overflow check. - using node_int_type = typename BasicNodeType::integer_type; - const node_int_type tmp_int = n.template get_value_ref(); - if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { - throw exception("Integer value underflow detected."); + i = static_cast(tmp_int); + break; } - if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { - throw exception("Integer value overflow detected."); + case node_type::SEQUENCE: + case node_type::MAPPING: + case node_type::STRING: + default: + throw type_error("The target node value type is not compatible with integer type.", n.get_type()); } - - i = static_cast(tmp_int); } -/// @brief from_node function for BasicNodeType::float_number_type objects. +/// @brief Helper struct for node-to-float conversion if FloatType is the node's floating point value type. /// @tparam BasicNodeType A basic_node template instance type. -/// @param n A basic_node object. -/// @param f A float number node value object. -template ::value, int> = 0> -inline void from_node(const BasicNodeType& n, typename BasicNodeType::float_number_type& f) { - if FK_YAML_UNLIKELY (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.get_type()); - } - f = n.template get_value_ref(); -} - -/// @brief from_node function for other float number objects. (i.e., not BasicNodeType::float_number_type) -/// @tparam BasicNodeType A basic_node template instance type. -/// @tparam FloatType A float number value type. -/// @param n A basic_node object. -/// @param f A float number node value object. +/// @tparam FloatType Target floating point value type (same as the BasicNodeType::float_number_type) template < typename BasicNodeType, typename FloatType, - enable_if_t< - conjunction< - std::is_floating_point, - negation>>::value, - int> = 0> -inline void from_node(const BasicNodeType& n, FloatType& f) { - if FK_YAML_UNLIKELY (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.get_type()); + bool = std::is_same::value> +struct from_node_float_helper { + /// @brief Convert node's floating point value to the target floating point type. + /// @param n A node object. + /// @return A floating point value converted from the node's floating point value. + static FloatType convert(const BasicNodeType& n) { + return n.template get_value_ref(); } +}; - using node_float_type = typename BasicNodeType::float_number_type; - auto tmp_float = n.template get_value_ref(); +/// @brief Helper struct for node-to-float conversion if IntType is not the node's floating point value type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam FloatType Target floating point value type (different from BasicNodeType::float_number_type) +template +struct from_node_float_helper { + /// @brief Convert node's floating point value to the target floating point type. + /// @param n A node object. + /// @return A floating point value converted from the node's floating point value. + static FloatType convert(const BasicNodeType& n) { + using node_float_type = typename BasicNodeType::float_number_type; + auto tmp_float = n.template get_value_ref(); + + // check if the value is an infinite number (either positive or negative) + if (std::isinf(tmp_float)) { + if (tmp_float == std::numeric_limits::infinity()) { + return std::numeric_limits::infinity(); + } + + return static_cast(-1.) * std::numeric_limits::infinity(); + } - // check if the value is an infinite number (either positive or negative) - if (std::isinf(tmp_float)) { - if (tmp_float == std::numeric_limits::infinity()) { - f = std::numeric_limits::infinity(); - return; + // check if the value is not a number + if (std::isnan(tmp_float)) { + return std::numeric_limits::quiet_NaN(); } - f = -1 * std::numeric_limits::infinity(); - return; - } + // check if the value is expressible as FloatType. + if FK_YAML_UNLIKELY (tmp_float < std::numeric_limits::lowest()) { + throw exception("Floating point value underflow detected."); + } + if FK_YAML_UNLIKELY (std::numeric_limits::max() < tmp_float) { + throw exception("Floating point value overflow detected."); + } - // check if the value is not a number - if (std::isnan(tmp_float)) { - f = std::numeric_limits::quiet_NaN(); - return; + return static_cast(tmp_float); } +}; - // check if the value is expressible as FloatType. - if FK_YAML_UNLIKELY (tmp_float < std::numeric_limits::lowest()) { - throw exception("Floating point value underflow detected."); - } - if FK_YAML_UNLIKELY (std::numeric_limits::max() < tmp_float) { - throw exception("Floating point value overflow detected."); +/// @brief from_node function for floating point values. +/// @note If node's value is null, boolean, or integer, such a value is converted into a floating point internally. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam FloatType A floating point value type. +/// @param n A node object. +/// @param f Storage for a float point value. +template < + typename BasicNodeType, typename FloatType, + enable_if_t, std::is_floating_point>::value, int> = 0> +inline void from_node(const BasicNodeType& n, FloatType& f) { + switch (n.get_type()) { + case node_type::NULL_OBJECT: + // nullptr is interpreted as 0.0 + f = static_cast(0.); + break; + case node_type::BOOLEAN: + f = static_cast(n.template get_value_ref()) + ? static_cast(1.) + : static_cast(0.); + break; + case node_type::INTEGER: + f = static_cast(n.template get_value_ref()); + break; + case node_type::FLOAT: + f = from_node_float_helper::convert(n); + break; + case node_type::SEQUENCE: + case node_type::MAPPING: + case node_type::STRING: + default: + throw type_error("The target node value type is not compatible with float number type.", n.get_type()); } - - f = static_cast(tmp_float); } /// @brief from_node function for BasicNodeType::string_type objects. diff --git a/single_include/fkYAML/node.hpp b/single_include/fkYAML/node.hpp index 9a719c55..d465ce4c 100644 --- a/single_include/fkYAML/node.hpp +++ b/single_include/fkYAML/node.hpp @@ -10388,6 +10388,14 @@ inline void from_node(const BasicNodeType& n, typename BasicNodeType::mapping_ty } } +/// @brief from node function for mappings whose key and value are of both compatible types. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam CompatibleKeyType Mapping key type compatible with BasicNodeType. +/// @tparam CompatibleValueType Mapping value type compatible with BasicNodeType. +/// @tparam Compare Comparator type for mapping keys. +/// @tparam Allocator Allocator type for destination mapping object. +/// @param n A node object. +/// @param m Mapping container object to store converted key/value objects. template < typename BasicNodeType, typename CompatibleKeyType, typename CompatibleValueType, typename Compare, typename Allocator, @@ -10408,10 +10416,10 @@ inline void from_node(const BasicNodeType& n, std::map::value, int> = 0> inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { // to ensure the target node value type is null. @@ -10421,118 +10429,221 @@ inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { null = nullptr; } -/// @brief from_node function for BasicNodeType::boolean_type objects. +/// @brief from_node function for booleans. /// @tparam BasicNodeType A basic_node template instance type. -/// @param n A basic_node object. -/// @param b A boolean node value object. +/// @param n A node object. +/// @param b Storage for a boolean value. template ::value, int> = 0> -inline void from_node(const BasicNodeType& n, typename BasicNodeType::boolean_type& b) { - if FK_YAML_UNLIKELY (!n.is_boolean()) { - throw type_error("The target node value type is not boolean type.", n.get_type()); +inline void from_node(const BasicNodeType& n, bool& b) { + switch (n.get_type()) { + case node_type::NULL_OBJECT: + // nullptr is converted to false just as C++ implicitly does. + b = false; + break; + case node_type::BOOLEAN: + b = static_cast(n.template get_value_ref()); + break; + case node_type::INTEGER: + // true: non-zero, false: zero + b = (n.template get_value_ref() != 0); + break; + case node_type::FLOAT: + // true: non-zero, false: zero + using float_type = typename BasicNodeType::float_number_type; + b = (n.template get_value_ref() != static_cast(0.)); + break; + case node_type::SEQUENCE: + case node_type::MAPPING: + case node_type::STRING: + default: + throw type_error("The target node value type is not compatible with boolean type.", n.get_type()); } - b = n.template get_value_ref(); } -/// @brief from_node function for BasicNodeType::integer_type objects. +/// @brief Helper struct for node-to-int conversion. /// @tparam BasicNodeType A basic_node template instance type. -/// @param n A basic_node object. -/// @param i An integer node value object. -template ::value, int> = 0> -inline void from_node(const BasicNodeType& n, typename BasicNodeType::integer_type& i) { - if FK_YAML_UNLIKELY (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.get_type()); +/// @tparam IntType Target integer value type (same as BasicNodeType::integer_type) +template < + typename BasicNodeType, typename IntType, bool = std::is_same::value> +struct from_node_int_helper { + /// @brief Convert node's integer value to the target integer type. + /// @param n A node object. + /// @return An integer value converted from the node's integer value. + static IntType convert(const BasicNodeType& n) { + return n.template get_value_ref(); } - i = n.template get_value_ref(); -} +}; -/// @brief from_node function for other integer objects. (i.e., not BasicNodeType::integer_type) +/// @brief Helper struct for node-to-int conversion if IntType is not the node's integer value type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam IntType Target integer value type (different from BasicNodeType::integer_type) +template +struct from_node_int_helper { + /// @brief Convert node's integer value to non-uint64_t integer types. + /// @param n A node object. + /// @return An integer value converted from the node's integer value. + static IntType convert(const BasicNodeType& n) { + using node_int_type = typename BasicNodeType::integer_type; + const node_int_type tmp_int = n.template get_value_ref(); + + // under/overflow check. + if (std::is_same::value) { + if FK_YAML_UNLIKELY (tmp_int < 0) { + throw exception("Integer value underflow detected."); + } + } + else { + if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { + throw exception("Integer value underflow detected."); + } + if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { + throw exception("Integer value overflow detected."); + } + } + + return static_cast(tmp_int); + } +}; + +/// @brief from_node function for integers. +/// @note If node's value is null, boolean, or float, such a value is converted into an integer internally. /// @tparam BasicNodeType A basic_node template instance type. /// @tparam IntegerType An integer value type. -/// @param n A basic_node object. -/// @param i An integer node value object. +/// @param n A node object. +/// @param i Storage for an integer value. template < typename BasicNodeType, typename IntegerType, - enable_if_t< - conjunction< - is_non_bool_integral, - negation>>::value, - int> = 0> + enable_if_t, is_non_bool_integral>::value, int> = 0> inline void from_node(const BasicNodeType& n, IntegerType& i) { - if FK_YAML_UNLIKELY (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.get_type()); - } - - // under/overflow check. - using node_int_type = typename BasicNodeType::integer_type; - const node_int_type tmp_int = n.template get_value_ref(); - if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { - throw exception("Integer value underflow detected."); - } - if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { - throw exception("Integer value overflow detected."); - } + switch (n.get_type()) { + case node_type::NULL_OBJECT: + // nullptr is interpreted as 0 + i = static_cast(0); + break; + case node_type::BOOLEAN: + i = static_cast(n.template get_value_ref()) + ? static_cast(1) + : static_cast(0); + break; + case node_type::INTEGER: + i = from_node_int_helper::convert(n); + break; + case node_type::FLOAT: { + // int64_t should be safe to express integer part values of possible floating point types. + const auto tmp_int = + static_cast(n.template get_value_ref()); - i = static_cast(tmp_int); -} + // under/overflow check. + if (std::is_same::value) { + if FK_YAML_UNLIKELY (tmp_int < 0) { + throw exception("Integer value underflow detected."); + } + } + else { + if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { + throw exception("Integer value underflow detected."); + } + if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { + throw exception("Integer value overflow detected."); + } + } -/// @brief from_node function for BasicNodeType::float_number_type objects. -/// @tparam BasicNodeType A basic_node template instance type. -/// @param n A basic_node object. -/// @param f A float number node value object. -template ::value, int> = 0> -inline void from_node(const BasicNodeType& n, typename BasicNodeType::float_number_type& f) { - if FK_YAML_UNLIKELY (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.get_type()); + i = static_cast(tmp_int); + break; + } + case node_type::SEQUENCE: + case node_type::MAPPING: + case node_type::STRING: + default: + throw type_error("The target node value type is not compatible with integer type.", n.get_type()); } - f = n.template get_value_ref(); } -/// @brief from_node function for other float number objects. (i.e., not BasicNodeType::float_number_type) +/// @brief Helper struct for node-to-float conversion if FloatType is the node's floating point value type. /// @tparam BasicNodeType A basic_node template instance type. -/// @tparam FloatType A float number value type. -/// @param n A basic_node object. -/// @param f A float number node value object. +/// @tparam FloatType Target floating point value type (same as the BasicNodeType::float_number_type) template < typename BasicNodeType, typename FloatType, - enable_if_t< - conjunction< - std::is_floating_point, - negation>>::value, - int> = 0> -inline void from_node(const BasicNodeType& n, FloatType& f) { - if FK_YAML_UNLIKELY (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.get_type()); + bool = std::is_same::value> +struct from_node_float_helper { + /// @brief Convert node's floating point value to the target floating point type. + /// @param n A node object. + /// @return A floating point value converted from the node's floating point value. + static FloatType convert(const BasicNodeType& n) { + return n.template get_value_ref(); } +}; - using node_float_type = typename BasicNodeType::float_number_type; - auto tmp_float = n.template get_value_ref(); +/// @brief Helper struct for node-to-float conversion if IntType is not the node's floating point value type. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam FloatType Target floating point value type (different from BasicNodeType::float_number_type) +template +struct from_node_float_helper { + /// @brief Convert node's floating point value to the target floating point type. + /// @param n A node object. + /// @return A floating point value converted from the node's floating point value. + static FloatType convert(const BasicNodeType& n) { + using node_float_type = typename BasicNodeType::float_number_type; + auto tmp_float = n.template get_value_ref(); - // check if the value is an infinite number (either positive or negative) - if (std::isinf(tmp_float)) { - if (tmp_float == std::numeric_limits::infinity()) { - f = std::numeric_limits::infinity(); - return; + // check if the value is an infinite number (either positive or negative) + if (std::isinf(tmp_float)) { + if (tmp_float == std::numeric_limits::infinity()) { + return std::numeric_limits::infinity(); + } + + return static_cast(-1.) * std::numeric_limits::infinity(); } - f = -1 * std::numeric_limits::infinity(); - return; - } + // check if the value is not a number + if (std::isnan(tmp_float)) { + return std::numeric_limits::quiet_NaN(); + } - // check if the value is not a number - if (std::isnan(tmp_float)) { - f = std::numeric_limits::quiet_NaN(); - return; - } + // check if the value is expressible as FloatType. + if FK_YAML_UNLIKELY (tmp_float < std::numeric_limits::lowest()) { + throw exception("Floating point value underflow detected."); + } + if FK_YAML_UNLIKELY (std::numeric_limits::max() < tmp_float) { + throw exception("Floating point value overflow detected."); + } - // check if the value is expressible as FloatType. - if FK_YAML_UNLIKELY (tmp_float < std::numeric_limits::lowest()) { - throw exception("Floating point value underflow detected."); - } - if FK_YAML_UNLIKELY (std::numeric_limits::max() < tmp_float) { - throw exception("Floating point value overflow detected."); + return static_cast(tmp_float); } +}; - f = static_cast(tmp_float); +/// @brief from_node function for floating point values. +/// @note If node's value is null, boolean, or integer, such a value is converted into a floating point internally. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam FloatType A floating point value type. +/// @param n A node object. +/// @param f Storage for a float point value. +template < + typename BasicNodeType, typename FloatType, + enable_if_t, std::is_floating_point>::value, int> = 0> +inline void from_node(const BasicNodeType& n, FloatType& f) { + switch (n.get_type()) { + case node_type::NULL_OBJECT: + // nullptr is interpreted as 0.0 + f = static_cast(0.); + break; + case node_type::BOOLEAN: + f = static_cast(n.template get_value_ref()) + ? static_cast(1.) + : static_cast(0.); + break; + case node_type::INTEGER: + f = static_cast(n.template get_value_ref()); + break; + case node_type::FLOAT: + f = from_node_float_helper::convert(n); + break; + case node_type::SEQUENCE: + case node_type::MAPPING: + case node_type::STRING: + default: + throw type_error("The target node value type is not compatible with float number type.", n.get_type()); + } } /// @brief from_node function for BasicNodeType::string_type objects. diff --git a/test/unit_test/test_node_class.cpp b/test/unit_test/test_node_class.cpp index 15538692..abb9456f 100644 --- a/test/unit_test/test_node_class.cpp +++ b/test/unit_test/test_node_class.cpp @@ -2419,44 +2419,82 @@ TEST_CASE("Node_GetValue") { SECTION("null node value") { fkyaml::node node(nullptr); - SECTION("null value") { + SECTION("null type") { auto null = node.get_value(); REQUIRE(null == nullptr); } - SECTION("non-null values") { + SECTION("non-null compatible types") { + REQUIRE(node.get_value() == false); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0.f); + REQUIRE(node.get_value() == 0.); + REQUIRE(node.get_value() == 0.l); + } + + SECTION("non-null incompatible types") { REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); } } SECTION("boolean node value") { - fkyaml::node node(true); - - SECTION("boolean value") { - REQUIRE(node.get_value() == true); - } - - SECTION("non-boolean values") { - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); + fkyaml::node true_node(true); + fkyaml::node false_node(false); + + SECTION("boolean type") { + REQUIRE(true_node.get_value() == true); + REQUIRE(false_node.get_value() == false); + } + + SECTION("non-boolean compatible types") { + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1); + REQUIRE(true_node.get_value() == 1.f); + REQUIRE(true_node.get_value() == 1.); + REQUIRE(true_node.get_value() == 1.l); + + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0); + REQUIRE(false_node.get_value() == 0.f); + REQUIRE(false_node.get_value() == 0.); + REQUIRE(false_node.get_value() == 0.l); + } + + SECTION("non-boolean incompatible types") { + REQUIRE_THROWS_AS(true_node.get_value(), fkyaml::type_error); + REQUIRE_THROWS_AS(true_node.get_value(), fkyaml::type_error); + REQUIRE_THROWS_AS(true_node.get_value(), fkyaml::type_error); + REQUIRE_THROWS_AS(true_node.get_value(), fkyaml::type_error); + REQUIRE_THROWS_AS(true_node.get_value(), fkyaml::type_error); } } SECTION("integer node value") { fkyaml::node node(123); - SECTION("integer values") { + SECTION("integer types") { REQUIRE(node.get_value() == 123); REQUIRE(node.get_value() == 123); REQUIRE(node.get_value() == 123); @@ -2464,27 +2502,40 @@ TEST_CASE("Node_GetValue") { REQUIRE(node.get_value() == 123); REQUIRE(node.get_value() == 123); REQUIRE(node.get_value() == 123); - // TODO: REQUIRE(node.get_value() == 123); + REQUIRE(node.get_value() == 123); + } + + SECTION("non-integer compatible types") { + REQUIRE(node.get_value() == true); + REQUIRE(node.get_value() == 123.f); + REQUIRE(node.get_value() == 123.); + REQUIRE(node.get_value() == 123.l); + + node = -123; + REQUIRE(node.get_value() == true); + REQUIRE(node.get_value() == -123.f); + REQUIRE(node.get_value() == -123.); + REQUIRE(node.get_value() == -123.l); + + node = 0; + REQUIRE(node.get_value() == false); + REQUIRE(node.get_value() == 0.f); + REQUIRE(node.get_value() == 0.); + REQUIRE(node.get_value() == 0.l); } - SECTION("non-integer values") { + SECTION("non-integer incompatible types") { REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); } - SECTION("non-integer node value") { - fkyaml::node non_int_node(true); - REQUIRE_THROWS_AS(non_int_node.get_value(), fkyaml::type_error); - } - SECTION("underflowable integer type") { fkyaml::node negative_int_node(std::numeric_limits::min()); REQUIRE_THROWS_AS(negative_int_node.get_value(), fkyaml::exception); + REQUIRE_THROWS_AS(negative_int_node.get_value(), fkyaml::exception); } SECTION("overflowable integer type") { @@ -2496,41 +2547,64 @@ TEST_CASE("Node_GetValue") { SECTION("float number node value") { fkyaml::node node(3.14); - SECTION("positive float number values") { + SECTION("positive float values") { REQUIRE(std::abs(node.get_value() - 3.14) < std::numeric_limits::epsilon()); REQUIRE(std::abs(node.get_value() - 3.14) < std::numeric_limits::epsilon()); REQUIRE(std::abs(node.get_value() - 3.14) < std::numeric_limits::epsilon()); } - SECTION("zero float number values") { + SECTION("zero float values") { node = 0.0; REQUIRE(std::abs(node.get_value() - 0.0) < std::numeric_limits::epsilon()); REQUIRE(std::abs(node.get_value() - 0.0) < std::numeric_limits::epsilon()); REQUIRE(std::abs(node.get_value() - 0.0) < std::numeric_limits::epsilon()); } - SECTION("negative float number values") { + SECTION("negative float values") { node = -3.14; REQUIRE(std::abs(node.get_value() - (-3.14)) < std::numeric_limits::epsilon()); REQUIRE(std::abs(node.get_value() - (-3.14)) < std::numeric_limits::epsilon()); REQUIRE(std::abs(node.get_value() - (-3.14)) < std::numeric_limits::epsilon()); } - SECTION("non-float-number values") { + SECTION("non-float compatible types") { + REQUIRE(node.get_value() == true); + REQUIRE(node.get_value() == 3); + REQUIRE(node.get_value() == 3); + REQUIRE(node.get_value() == 3); + REQUIRE(node.get_value() == 3); + REQUIRE(node.get_value() == 3); + REQUIRE(node.get_value() == 3); + REQUIRE(node.get_value() == 3); + REQUIRE(node.get_value() == 3); + + node = -3.14; + REQUIRE(node.get_value() == true); + REQUIRE(node.get_value() == -3); + REQUIRE(node.get_value() == -3); + REQUIRE(node.get_value() == -3); + REQUIRE(node.get_value() == -3); + + node = 0.0; + REQUIRE(node.get_value() == false); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + REQUIRE(node.get_value() == 0); + } + + SECTION("non-float incompatible types") { REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); - REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); REQUIRE_THROWS_AS(node.get_value(), fkyaml::type_error); } - SECTION("non-float-number node value") { - fkyaml::node non_float_num_node(true); - REQUIRE_THROWS_AS(non_float_num_node.get_value(), fkyaml::type_error); - } - SECTION("underflowable float number type") { fkyaml::node negative_float_node(std::numeric_limits::lowest()); REQUIRE_THROWS_AS(negative_float_node.get_value(), fkyaml::exception); @@ -2540,6 +2614,15 @@ TEST_CASE("Node_GetValue") { fkyaml::node large_float_node(std::numeric_limits::max()); REQUIRE_THROWS_AS(large_float_node.get_value(), fkyaml::exception); } + + SECTION("invalid float-to-int conversion") { + node = -3.14; + REQUIRE_THROWS_AS(node.get_value(), fkyaml::exception); + REQUIRE_THROWS_AS(node.get_value(), fkyaml::exception); + + node = 256.0; + REQUIRE_THROWS_AS(node.get_value(), fkyaml::exception); + } } SECTION("string node value") {