Skip to content

Commit 9826147

Browse files
authored
fix: use strtod fallback when std::from_chars(float) unavailable (#572)
1 parent 63e4ec0 commit 9826147

File tree

1 file changed

+35
-1
lines changed

1 file changed

+35
-1
lines changed

src/iceberg/util/string_util.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
#pragma once
2121

2222
#include <algorithm>
23+
#include <cerrno>
2324
#include <charconv>
2425
#include <ranges>
2526
#include <string>
2627
#include <string_view>
28+
#include <type_traits>
2729
#include <typeinfo>
2830
#include <utility>
2931

@@ -32,6 +34,9 @@
3234

3335
namespace iceberg {
3436

37+
template <typename T>
38+
concept FromChars = requires(const char* p, T& v) { std::from_chars(p, p, v); };
39+
3540
class ICEBERG_EXPORT StringUtils {
3641
public:
3742
static std::string ToLower(std::string_view str) {
@@ -68,7 +73,7 @@ class ICEBERG_EXPORT StringUtils {
6873
}
6974

7075
template <typename T>
71-
requires std::is_arithmetic_v<T> && (!std::same_as<T, bool>)
76+
requires std::is_arithmetic_v<T> && FromChars<T> && (!std::same_as<T, bool>)
7277
static Result<T> ParseNumber(std::string_view str) {
7378
T value = 0;
7479
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
@@ -85,6 +90,35 @@ class ICEBERG_EXPORT StringUtils {
8590
}
8691
std::unreachable();
8792
}
93+
94+
template <typename T>
95+
requires std::is_floating_point_v<T> && (!FromChars<T>)
96+
static Result<T> ParseNumber(std::string_view str) {
97+
T value{};
98+
// strto* require null-terminated input; string_view does not guarantee it.
99+
std::string owned(str);
100+
const char* start = owned.c_str();
101+
char* end = nullptr;
102+
errno = 0;
103+
104+
if constexpr (std::same_as<T, float>) {
105+
value = std::strtof(start, &end);
106+
} else if constexpr (std::same_as<T, double>) {
107+
value = std::strtod(start, &end);
108+
} else {
109+
value = std::strtold(start, &end);
110+
}
111+
112+
if (end == start || end != start + static_cast<std::ptrdiff_t>(owned.size())) {
113+
return InvalidArgument("Failed to parse {} from string '{}': invalid argument",
114+
typeid(T).name(), str);
115+
}
116+
if (errno == ERANGE) {
117+
return InvalidArgument("Failed to parse {} from string '{}': value out of range",
118+
typeid(T).name(), str);
119+
}
120+
return value;
121+
}
88122
};
89123

90124
/// \brief Transparent hash function that supports std::string_view as lookup key

0 commit comments

Comments
 (0)