Skip to content

Commit e709b30

Browse files
committed
[main] optparse: only use std::from_chars from C++20 onwards
See comment in the code as to why
1 parent 3e4fe8b commit e709b30

File tree

1 file changed

+33
-1
lines changed

1 file changed

+33
-1
lines changed

main/src/optparse.hxx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include <charconv>
5555
#include <cstring>
5656
#include <iostream>
57+
#include <limits>
5758
#include <optional>
5859
#include <sstream>
5960
#include <stdexcept>
@@ -194,9 +195,14 @@ public:
194195
static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>);
195196

196197
if (auto val = GetFlagValue(name); !val.empty()) {
198+
// NOTE: on paper std::from_chars is supported since C++17, however some compilers don't properly support it
199+
// (e.g. it's not available at all on MacOS < 26 and only the integer overload is available in AlmaLinux 8).
200+
// There is also no compiler define that we can use to determine the availability, so we just use it only
201+
// from C++20 and hope for the best.
202+
#if __cplusplus >= 202002L
197203
T converted;
198204
auto res = std::from_chars(val.data(), val.data() + val.size(), converted);
199-
if (res.ptr == val.end() && res.ec == std::errc{}) {
205+
if (res.ptr == val.data() + val.size() && res.ec == std::errc{}) {
200206
return converted;
201207
} else {
202208
std::stringstream err;
@@ -211,6 +217,32 @@ public:
211217
else
212218
throw std::invalid_argument(err.str());
213219
}
220+
#else
221+
std::conditional_t<std::is_integral_v<T>, long long, long double> converted;
222+
std::size_t unconvertedPos;
223+
if constexpr (std::is_integral_v<T>) {
224+
converted = std::stoll(std::string(val), &unconvertedPos);
225+
} else {
226+
converted = std::stold(std::string(val), &unconvertedPos);
227+
}
228+
229+
const bool isOor = converted > std::numeric_limits<T>::max();
230+
if (unconvertedPos != val.size() || isOor) {
231+
std::stringstream err;
232+
err << "Failed to parse flag `" << name << "` with value `" << val << "`";
233+
if constexpr (std::is_integral_v<T>)
234+
err << " as an integer.\n";
235+
else
236+
err << " as a floating point number.\n";
237+
238+
if (isOor)
239+
throw std::out_of_range(err.str());
240+
else
241+
throw std::invalid_argument(err.str());
242+
}
243+
244+
return converted;
245+
#endif
214246
}
215247
return std::nullopt;
216248
}

0 commit comments

Comments
 (0)