Skip to content

Commit

Permalink
use strto*
Browse files Browse the repository at this point in the history
  • Loading branch information
PragmaTwice committed Jan 17, 2023
1 parent 370836d commit be65233
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 17 deletions.
46 changes: 34 additions & 12 deletions src/common/parse_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@

#pragma once

#include <charconv>
#include <cstdlib>
#include <limits>
#include <string>
#include <system_error>
#include <tuple>

#include "status.h"
Expand Down Expand Up @@ -98,7 +96,7 @@ StatusOr<ParseResultAndPos<T>> TryParseInt(const char *v, int base = 0) {
}

if (errno) {
return {Status::NotOK, std::strerror(errno)};
return Status::FromErrno();
}

if (!std::is_same<T, decltype(res)>::value &&
Expand Down Expand Up @@ -147,27 +145,51 @@ StatusOr<T> ParseInt(const std::string &v, NumericRange<T> range, int base = 0)
// available units: K, M, G, T, P
StatusOr<std::uint64_t> ParseSizeAndUnit(const std::string &v);

template <typename>
struct ParseFloatFunc;

template <>
struct ParseFloatFunc<float> {
constexpr static const auto value = strtof;
};

template <>
struct ParseFloatFunc<double> {
constexpr static const auto value = strtod;
};

template <>
struct ParseFloatFunc<long double> {
constexpr static const auto value = strtold;
};

// TryParseFloat parses a string to a floating-point number,
// it returns the first unmatched character position instead of an error status
template <typename T = double> // float or double
StatusOr<ParseResultAndPos<T>> TryParseFloat(std::string_view str, std::chars_format fmt = std::chars_format::general) {
T result = 0;
StatusOr<ParseResultAndPos<T>> TryParseFloat(const char *str) {
char *end = nullptr;

auto stat = std::from_chars(str.begin(), str.end(), result, fmt);
errno = 0;
T result = ParseFloatFunc<T>::value(str, &end);

if (str == end) {
return {Status::NotOK, "not started as a number"};
}

if (stat.ec != std::errc{}) {
return {Status::NotOK, std::make_error_code(stat.ec).message()};
if (errno) {
return Status::FromErrno();
}

return {result, stat.ptr};
return {result, end};
}

// ParseFloat parses a string to a floating-point number
template <typename T = double> // float or double
StatusOr<T> ParseFloat(std::string_view str, std::chars_format fmt = std::chars_format::general) {
auto [result, pos] = GET_OR_RET(TryParseFloat<T>(str, fmt));
StatusOr<T> ParseFloat(const std::string &str) {
const char *begin = str.c_str();
auto [result, pos] = GET_OR_RET(TryParseFloat<T>(begin));

if (pos != str.end()) {
if (pos != begin + str.size()) {
return {Status::NotOK, "encounter non-number characters"};
}

Expand Down
13 changes: 8 additions & 5 deletions tests/cppunit/parse_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,19 @@ TEST(ParseUtil, ParseSizeAndUnit) {
}

TEST(ParseUtil, ParseFloat) {
std::string_view v = "1.23";
ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(1.23, v.end()));
std::string v = "1.23";
ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(1.23, v.c_str() + v.size()));

v = "25345.346e65hello";
ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(25345.346e65, v.end() - 5));
ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(25345.346e65, v.c_str() + v.size() - 5));

ASSERT_EQ(TryParseFloat("eeeeeeee").Msg(), "Invalid argument");
ASSERT_FALSE(TryParseFloat("eeeeeeee"));
ASSERT_FALSE(TryParseFloat(" "));
ASSERT_FALSE(TryParseFloat(""));
ASSERT_FALSE(TryParseFloat(" abcd"));

v = " 1e8 ";
ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(1e8, v.end() - 3));
ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(1e8, v.c_str() + v.size() - 3));

ASSERT_EQ(*ParseFloat("1.23"), 1.23);
ASSERT_EQ(*ParseFloat("1.23e2"), 1.23e2);
Expand Down

0 comments on commit be65233

Please sign in to comment.