add strings::try_parse function for arithmetic types

This commit is contained in:
2019-07-12 15:25:42 +07:00
parent ae5205c1b3
commit e2c96d8511
4 changed files with 124 additions and 0 deletions

View File

@@ -8,6 +8,7 @@
#include <cmath>
#include <cstdio>
#include <cerrno>
#include <climits>
#include <cstring>
#include <cstdint>
@@ -34,9 +35,11 @@
#include <limits>
#include <numeric>
#include <utility>
#include <charconv>
#include <iterator>
#include <exception>
#include <stdexcept>
#include <algorithm>
#include <functional>
#include <type_traits>
#include <system_error>

View File

@@ -84,6 +84,21 @@ namespace e2d
str32_hash make_hash(str32_view src) noexcept;
}
namespace e2d::strings
{
template < typename T >
std::enable_if_t<std::is_integral_v<T>, bool>
try_parse(str_view src, T& dst) noexcept;
template < typename T >
std::enable_if_t<std::is_same_v<T, f32>, T>
try_parse(str_view src, T& dst) noexcept;
template < typename T >
std::enable_if_t<std::is_same_v<T, f64>, T>
try_parse(str_view src, T& dst) noexcept;
}
namespace e2d::strings
{
class format_error;

View File

@@ -180,6 +180,58 @@ namespace std
};
}
namespace e2d::strings
{
template < typename T >
std::enable_if_t<std::is_integral_v<T>, bool>
try_parse(str_view src, T& dst) noexcept {
T tmp{0};
std::from_chars_result res = std::from_chars(
src.data(), src.data() + src.size(), tmp);
if ( res.ptr != src.data() + src.size() || res.ec != std::errc() ) {
return false;
}
dst = tmp;
return true;
}
template < typename T >
std::enable_if_t<std::is_same_v<T, f32>, T>
try_parse(str_view src, T& dst) noexcept {
if ( src.size() >= 128 ) {
return false;
}
char* str = static_cast<char*>(E2D_CLEAR_ALLOCA(src.size() + 1));
std::memcpy(str, src.data(), src.size());
errno = 0;
char* end = nullptr;
T tmp = std::strtof(str, &end);
if ( end != str + src.size() || errno ) {
return false;
}
dst = tmp;
return true;
}
template < typename T >
std::enable_if_t<std::is_same_v<T, f64>, T>
try_parse(str_view src, T& dst) noexcept {
if ( src.size() >= 512 ) {
return false;
}
char* str = static_cast<char*>(E2D_CLEAR_ALLOCA(src.size() + 1));
std::memcpy(str, src.data(), src.size());
errno = 0;
char* end = nullptr;
T tmp = std::strtod(str, &end);
if ( end != str + src.size() || errno ) {
return false;
}
dst = tmp;
return true;
}
}
namespace e2d::strings
{
//

View File

@@ -99,6 +99,60 @@ TEST_CASE("strings") {
REQUIRE(make_utf32(str16_view(null_utf16, 0)) == make_utf32(u""));
REQUIRE(make_utf32(str32_view(null_utf32, 0)) == make_utf32(U""));
}
{
using strings::try_parse;
{
i8 v{111};
REQUIRE((try_parse("42", v) && v == 42));
REQUIRE((try_parse("127", v) && v == 127));
REQUIRE((try_parse("0", v) && v == 0));
REQUIRE((try_parse("-128", v) && v == -128));
u8 uv{111};
REQUIRE((try_parse("42", uv) && uv == 42));
REQUIRE((try_parse("255", uv) && uv == 255));
REQUIRE((try_parse("0", uv) && uv == 0));
}
{
i8 v{111};
REQUIRE((!try_parse(str_view(), v) && v == 111));
REQUIRE((!try_parse("", v) && v == 111));
REQUIRE((!try_parse("42hello", v) && v == 111));
REQUIRE((!try_parse("world42", v) && v == 111));
REQUIRE((!try_parse("42 ", v) && v == 111));
REQUIRE((!try_parse(" 42", v) && v == 111));
REQUIRE((!try_parse("-129", v) && v == 111));
REQUIRE((!try_parse("128", v) && v == 111));
REQUIRE((!try_parse("4.2", v) && v == 111));
}
{
f32 v32{11.22f};
REQUIRE((try_parse("4.23E10", v32) && math::approximately(v32, 4.23E10f)));
REQUIRE((try_parse("42", v32) && math::approximately(v32, 42.f)));
REQUIRE((try_parse("-2.43", v32) && math::approximately(v32, -2.43f)));
REQUIRE((try_parse("-24", v32) && math::approximately(v32, -24.f)));
f64 v64{11.22f};
REQUIRE((try_parse("4.23E200", v64) && math::approximately(v64, 4.23e200)));
REQUIRE((try_parse("42", v64) && math::approximately(v64, 42.)));
REQUIRE((try_parse("-2.43", v64) && math::approximately(v64, -2.43)));
REQUIRE((try_parse("-24", v64) && math::approximately(v64, -24.)));
}
{
f32 v32{11.22f};
REQUIRE((!try_parse("1.0E100", v32) && math::approximately(v32, 11.22f)));
REQUIRE((!try_parse("1..4", v32) && math::approximately(v32, 11.22f)));
REQUIRE((!try_parse("..14", v32) && math::approximately(v32, 11.22f)));
REQUIRE((!try_parse("14..", v32) && math::approximately(v32, 11.22f)));
f64 v64{11.22};
REQUIRE((!try_parse("1.0E400", v64) && math::approximately(v64, 11.22)));
REQUIRE((!try_parse("1..4", v64) && math::approximately(v64, 11.22)));
REQUIRE((!try_parse("..14", v64) && math::approximately(v64, 11.22)));
REQUIRE((!try_parse("14..", v64) && math::approximately(v64, 11.22)));
}
}
{
using strings::wildcard_match;