diff --git a/include/yaml-cpp/node/convert.h b/include/yaml-cpp/node/convert.h index 45a878a..1329dbc 100644 --- a/include/yaml-cpp/node/convert.h +++ b/include/yaml-cpp/node/convert.h @@ -8,9 +8,11 @@ #endif #include +#include #include #include #include +#include #include #include @@ -24,12 +26,23 @@ namespace YAML { class Binary; struct _Null; -template +template struct convert; + } // namespace YAML namespace YAML { namespace conversion { +static const std::regex re_true("true|True|TRUE"); +static const std::regex re_false("false|False|FALSE"); +static const std::regex re_decimal("[-+]?[0-9]+"); +static const std::regex re_octal("0o[0-7]+"); +static const std::regex re_hex("0x[0-9a-fA-F]+"); +static const std::regex re_float( + "[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?"); +static const std::regex re_inf("[-+]?(\\.inf|\\.Inf|\\.INF)"); +static const std::regex re_nan("\\.nan|\\.NaN|\\.NAN"); + inline bool IsInfinity(const std::string& input) { return input == ".inf" || input == ".Inf" || input == ".INF" || input == "+.inf" || input == "+.Inf" || input == "+.INF"; @@ -76,7 +89,7 @@ struct convert { template struct convert { - static Node encode(const char(&rhs)[N]) { return Node(rhs); } + static Node encode(const char (&rhs)[N]) { return Node(rhs); } }; template <> @@ -88,70 +101,137 @@ struct convert<_Null> { } }; -#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \ - template <> \ - struct convert { \ - static Node encode(const type& rhs) { \ - std::stringstream stream; \ - stream.precision(std::numeric_limits::digits10 + 1); \ - stream << rhs; \ - return Node(stream.str()); \ - } \ - \ - static bool decode(const Node& node, type& rhs) { \ - if (node.Type() != NodeType::Scalar) \ - return false; \ - const std::string& input = node.Scalar(); \ - std::stringstream stream(input); \ - stream.unsetf(std::ios::dec); \ - if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) \ - return true; \ - if (std::numeric_limits::has_infinity) { \ - if (conversion::IsInfinity(input)) { \ - rhs = std::numeric_limits::infinity(); \ - return true; \ - } else if (conversion::IsNegativeInfinity(input)) { \ - rhs = negative_op std::numeric_limits::infinity(); \ - return true; \ - } \ - } \ - \ - if (std::numeric_limits::has_quiet_NaN && \ - conversion::IsNaN(input)) { \ - rhs = std::numeric_limits::quiet_NaN(); \ - return true; \ - } \ - \ - return false; \ - } \ +// signed integral +template +struct convert::value && + std::is_signed::value>::type> { + static Node encode(const type& rhs) { return Node(std::to_string(rhs)); } + + static bool decode(const Node& node, type& rhs) { + if (node.Type() != NodeType::Scalar) + return false; + const std::string& input = node.Scalar(); + long long num; + if (std::regex_match(input, conversion::re_decimal)) { + try { + num = std::stoll(input); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_octal)) { + try { + num = std::stoll(input.substr(2), nullptr, 8); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_hex)) { + try { + num = std::stoll(input.substr(2), nullptr, 16); + } catch (const std::out_of_range&) { + return false; + } + } else { + return false; + } + if (num > std::numeric_limits::max() || + num < std::numeric_limits::min()) { + return false; + } + rhs = num; + return true; + } +}; + +// unsigned integral +template +struct convert::value && + std::is_unsigned::value>::type> { + static Node encode(const type& rhs) { return Node(std::to_string(rhs)); } + + static bool decode(const Node& node, type& rhs) { + if (node.Type() != NodeType::Scalar) + return false; + const std::string& input = node.Scalar(); + unsigned long long num; + if (std::regex_match(input, conversion::re_decimal)) { + try { + num = std::stoull(input); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_octal)) { + try { + num = std::stoull(input.substr(2), nullptr, 8); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_hex)) { + try { + num = std::stoull(input.substr(2), nullptr, 16); + } catch (const std::out_of_range&) { + return false; + } + } else { + return false; + } + if (num > std::numeric_limits::max() || + num < std::numeric_limits::min()) { + return false; + } + rhs = num; + return true; + } +}; + +// floating point +template +struct convert< + type, typename std::enable_if::value>::type> { + static Node encode(const type& rhs) { + if (std::isnan(rhs)) { + return Node(".nan"); + } + if (std::isinf(rhs)) { + if (std::signbit(rhs)) { + return Node("-.inf"); + } + return Node(".inf"); + } + auto str = std::to_string(rhs); + if (std::regex_match(str, conversion::re_decimal)) { + return Node(str + "."); // Disambiguate float from int + } + return Node(std::to_string(rhs)); } -#define YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(type) \ - YAML_DEFINE_CONVERT_STREAMABLE(type, -) - -#define YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(type) \ - YAML_DEFINE_CONVERT_STREAMABLE(type, +) - -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(int); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(short); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long long); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned short); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long long); - -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(char); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(signed char); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned char); - -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(float); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(double); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long double); - -#undef YAML_DEFINE_CONVERT_STREAMABLE_SIGNED -#undef YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED -#undef YAML_DEFINE_CONVERT_STREAMABLE + static bool decode(const Node& node, type& rhs) { + if (node.Type() != NodeType::Scalar) + return false; + const std::string& input = node.Scalar(); + long double num; + if (std::regex_match(input, conversion::re_float)) { + try { + num = std::stold(input); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_inf)) { + if (input[0] == '-') { + num = -std::numeric_limits::infinity(); + } else { + num = std::numeric_limits::infinity(); + } + } else if (std::regex_match(input, conversion::re_nan)) { + num = std::numeric_limits::quiet_NaN(); + } else { + return false; + } + rhs = num; + return true; + } +}; // bool template <> diff --git a/include/yaml-cpp/node/node.h b/include/yaml-cpp/node/node.h index 1ded7d2..e855863 100644 --- a/include/yaml-cpp/node/node.h +++ b/include/yaml-cpp/node/node.h @@ -138,7 +138,7 @@ YAML_CPP_API bool operator==(const Node& lhs, const Node& rhs); YAML_CPP_API Node Clone(const Node& node); -template +template struct convert; }