Replace macro in convert.h with partial template specialization.

Dealing with floating points is also closer to YAML 1.2 core schema.
This commit is contained in:
Lenard Szolnoki 2017-07-03 15:43:13 +02:00
parent 1d26aa7e9f
commit 027f684264
2 changed files with 145 additions and 65 deletions

View File

@ -8,9 +8,11 @@
#endif #endif
#include <array> #include <array>
#include <cmath>
#include <limits> #include <limits>
#include <list> #include <list>
#include <map> #include <map>
#include <regex>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
@ -24,12 +26,23 @@
namespace YAML { namespace YAML {
class Binary; class Binary;
struct _Null; struct _Null;
template <typename T> template <typename T, typename Enable>
struct convert; struct convert;
} // namespace YAML } // namespace YAML
namespace YAML { namespace YAML {
namespace conversion { 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) { inline bool IsInfinity(const std::string& input) {
return input == ".inf" || input == ".Inf" || input == ".INF" || return input == ".inf" || input == ".Inf" || input == ".INF" ||
input == "+.inf" || input == "+.Inf" || input == "+.INF"; input == "+.inf" || input == "+.Inf" || input == "+.INF";
@ -76,7 +89,7 @@ struct convert<const char*> {
template <std::size_t N> template <std::size_t N>
struct convert<const char[N]> { struct convert<const char[N]> {
static Node encode(const char(&rhs)[N]) { return Node(rhs); } static Node encode(const char (&rhs)[N]) { return Node(rhs); }
}; };
template <> template <>
@ -88,70 +101,137 @@ struct convert<_Null> {
} }
}; };
#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \ // signed integral
template <> \ template <typename type>
struct convert<type> { \ struct convert<type,
static Node encode(const type& rhs) { \ typename std::enable_if<std::is_integral<type>::value &&
std::stringstream stream; \ std::is_signed<type>::value>::type> {
stream.precision(std::numeric_limits<type>::digits10 + 1); \ static Node encode(const type& rhs) { return Node(std::to_string(rhs)); }
stream << rhs; \
return Node(stream.str()); \ static bool decode(const Node& node, type& rhs) {
} \ if (node.Type() != NodeType::Scalar)
\ return false;
static bool decode(const Node& node, type& rhs) { \ const std::string& input = node.Scalar();
if (node.Type() != NodeType::Scalar) \ long long num;
return false; \ if (std::regex_match(input, conversion::re_decimal)) {
const std::string& input = node.Scalar(); \ try {
std::stringstream stream(input); \ num = std::stoll(input);
stream.unsetf(std::ios::dec); \ } catch (const std::out_of_range&) {
if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) \ return false;
return true; \ }
if (std::numeric_limits<type>::has_infinity) { \ } else if (std::regex_match(input, conversion::re_octal)) {
if (conversion::IsInfinity(input)) { \ try {
rhs = std::numeric_limits<type>::infinity(); \ num = std::stoll(input.substr(2), nullptr, 8);
return true; \ } catch (const std::out_of_range&) {
} else if (conversion::IsNegativeInfinity(input)) { \ return false;
rhs = negative_op std::numeric_limits<type>::infinity(); \ }
return true; \ } else if (std::regex_match(input, conversion::re_hex)) {
} \ try {
} \ num = std::stoll(input.substr(2), nullptr, 16);
\ } catch (const std::out_of_range&) {
if (std::numeric_limits<type>::has_quiet_NaN && \ return false;
conversion::IsNaN(input)) { \ }
rhs = std::numeric_limits<type>::quiet_NaN(); \ } else {
return true; \ return false;
} \ }
\ if (num > std::numeric_limits<type>::max() ||
return false; \ num < std::numeric_limits<type>::min()) {
} \ return false;
}
rhs = num;
return true;
}
};
// unsigned integral
template <typename type>
struct convert<type,
typename std::enable_if<std::is_integral<type>::value &&
std::is_unsigned<type>::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<type>::max() ||
num < std::numeric_limits<type>::min()) {
return false;
}
rhs = num;
return true;
}
};
// floating point
template <typename type>
struct convert<
type, typename std::enable_if<std::is_floating_point<type>::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) \ static bool decode(const Node& node, type& rhs) {
YAML_DEFINE_CONVERT_STREAMABLE(type, -) if (node.Type() != NodeType::Scalar)
return false;
#define YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(type) \ const std::string& input = node.Scalar();
YAML_DEFINE_CONVERT_STREAMABLE(type, +) long double num;
if (std::regex_match(input, conversion::re_float)) {
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(int); try {
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(short); num = std::stold(input);
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long); } catch (const std::out_of_range&) {
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long long); return false;
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned); }
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned short); } else if (std::regex_match(input, conversion::re_inf)) {
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long); if (input[0] == '-') {
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long long); num = -std::numeric_limits<long double>::infinity();
} else {
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(char); num = std::numeric_limits<long double>::infinity();
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(signed char); }
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned char); } else if (std::regex_match(input, conversion::re_nan)) {
num = std::numeric_limits<long double>::quiet_NaN();
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(float); } else {
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(double); return false;
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long double); }
rhs = num;
#undef YAML_DEFINE_CONVERT_STREAMABLE_SIGNED return true;
#undef YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED }
#undef YAML_DEFINE_CONVERT_STREAMABLE };
// bool // bool
template <> template <>

View File

@ -138,7 +138,7 @@ YAML_CPP_API bool operator==(const Node& lhs, const Node& rhs);
YAML_CPP_API Node Clone(const Node& node); YAML_CPP_API Node Clone(const Node& node);
template <typename T> template <typename T, typename Enable = void>
struct convert; struct convert;
} }