|
|
|
@ -6,55 +6,45 @@
|
|
|
|
|
#include <cinttypes>
|
|
|
|
|
#include <type_traits>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <charconv>
|
|
|
|
|
#include <array>
|
|
|
|
|
|
|
|
|
|
namespace YYCC::ParserHelper {
|
|
|
|
|
|
|
|
|
|
template<class>
|
|
|
|
|
constexpr bool g_AlwaysFalse = false;
|
|
|
|
|
// Reference: https://zh.cppreference.com/w/cpp/utility/from_chars
|
|
|
|
|
|
|
|
|
|
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
|
|
|
|
bool TryParse(const std::string& strl, _Ty& num) {
|
|
|
|
|
try {
|
|
|
|
|
// float types
|
|
|
|
|
if constexpr (std::is_same_v<_Ty, float>) {
|
|
|
|
|
num = std::stof(strl, nullptr);
|
|
|
|
|
} else if constexpr (std::is_same_v<_Ty, double>) {
|
|
|
|
|
num = std::stod(strl, nullptr);
|
|
|
|
|
} else if constexpr (std::is_same_v<_Ty, long double>) {
|
|
|
|
|
num = std::stold(strl, nullptr);
|
|
|
|
|
} else {
|
|
|
|
|
static_assert(g_AlwaysFalse<_Ty>, "Invalid float type.");
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
} catch (const std::invalid_argument&) {
|
|
|
|
|
auto [ptr, ec] = std::from_chars(strl.c_str(), strl.c_str() + strl.size(), num, std::chars_format::general);
|
|
|
|
|
if (ec == std::errc()) {
|
|
|
|
|
// check whether the full string is matched
|
|
|
|
|
return ptr == strl.c_str() + strl.size();
|
|
|
|
|
} else if (ec == std::errc::invalid_argument) {
|
|
|
|
|
// given string is invalid
|
|
|
|
|
return false;
|
|
|
|
|
} catch (const std::out_of_range&) {
|
|
|
|
|
} else if (ec == std::errc::result_out_of_range) {
|
|
|
|
|
// given string is out of range
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
// unreachable
|
|
|
|
|
throw std::runtime_error("unreachable code.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
|
|
|
bool TryParse(const std::string& strl, _Ty& num, int base = 10) {
|
|
|
|
|
try {
|
|
|
|
|
// integer type
|
|
|
|
|
// decide integer type
|
|
|
|
|
using container_t = std::conditional_t<std::is_unsigned_v<_Ty>, unsigned long long, long long>;
|
|
|
|
|
// parse it from string according to whether integer type is signed.
|
|
|
|
|
container_t cache;
|
|
|
|
|
if constexpr (std::is_unsigned_v<_Ty>) {
|
|
|
|
|
cache = std::stoull(strl, nullptr, base);
|
|
|
|
|
} else {
|
|
|
|
|
cache = std::stoll(strl, nullptr, base);
|
|
|
|
|
}
|
|
|
|
|
// check its range
|
|
|
|
|
if (cache < std::numeric_limits<_Ty>::min() || cache > std::numeric_limits<_Ty>::max())
|
|
|
|
|
return false;
|
|
|
|
|
num = static_cast<_Ty>(cache);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (const std::invalid_argument&) {
|
|
|
|
|
auto [ptr, ec] = std::from_chars(strl.c_str(), strl.c_str() + strl.size(), num, base);
|
|
|
|
|
if (ec == std::errc()) {
|
|
|
|
|
// check whether the full string is matched
|
|
|
|
|
return ptr == strl.c_str() + strl.size();
|
|
|
|
|
} else if (ec == std::errc::invalid_argument) {
|
|
|
|
|
// given string is invalid
|
|
|
|
|
return false;
|
|
|
|
|
} catch (const std::out_of_range&) {
|
|
|
|
|
} else if (ec == std::errc::result_out_of_range) {
|
|
|
|
|
// given string is out of range
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
// unreachable
|
|
|
|
|
throw std::runtime_error("unreachable code.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
|
|
|
@ -72,12 +62,25 @@ namespace YYCC::ParserHelper {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty>, int> = 0>
|
|
|
|
|
// Reference: https://en.cppreference.com/w/cpp/utility/to_chars
|
|
|
|
|
|
|
|
|
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
|
|
|
std::string ToString(_Ty num) {
|
|
|
|
|
return std::to_string(num);
|
|
|
|
|
std::array<char, 64> buffer;
|
|
|
|
|
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), num);
|
|
|
|
|
if (ec == std::errc()) {
|
|
|
|
|
return std::string(buffer.data(), ptr - buffer.data());
|
|
|
|
|
} else if (ec == std::errc::value_too_large) {
|
|
|
|
|
// too short buffer
|
|
|
|
|
// this should not happend
|
|
|
|
|
throw std::out_of_range("ToString() buffer is not sufficient.");
|
|
|
|
|
} else {
|
|
|
|
|
// unreachable
|
|
|
|
|
throw std::runtime_error("unreachable code.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
template<>
|
|
|
|
|
std::string ToString<bool>(bool num) {
|
|
|
|
|
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
|
|
|
|
std::string ToString(_Ty num) {
|
|
|
|
|
if (num) return std::string("true");
|
|
|
|
|
else return std::string("false");
|
|
|
|
|
}
|
|
|
|
|