diff --git a/src/ifcparse/parse.cpp b/src/ifcparse/parse.cpp index 1b02dc3cc5..cf99dd98ff 100644 --- a/src/ifcparse/parse.cpp +++ b/src/ifcparse/parse.cpp @@ -40,6 +40,16 @@ #include #include #include +#include + +// Apple clang's libc++ has no floating-point std::from_chars overload (it's +// =deleted), so on macOS doubles are parsed via strtod_l with a cached "C" +// locale — locale-independent, unlike strtod. Other platforms (libstdc++, +// MSVC STL) have working float from_chars and are left unchanged. +#if defined(__APPLE__) +#include +#include +#endif #ifdef USE_MMAP #include @@ -121,6 +131,13 @@ std::string& spf_lexer::get_temp_string() const { namespace { +#if defined(__APPLE__) +double parse_double_c(const char* start, char** end) { + static const locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); + return strtod_l(start, end, loc); +} +#endif + template bool parse_num_(const char* pStart, size_t size, T& val) { if (size == 0) { @@ -133,11 +150,27 @@ bool parse_num_(const char* pStart, size_t size, T& val) { return false; } } - auto re = std::from_chars(pStart, pStart + size, val); - if (re.ec != std::errc() || re.ptr != pStart + size) { - return false; + if constexpr (std::is_floating_point_v) { +#if defined(__APPLE__) + // pStart is NUL-terminated at pStart + size (callers pass c_str()), so + // strtod_l stops exactly at the end of a well-formed number. from_chars + // is not instantiated for double here — its float overload is =deleted + // in Apple's libc++. + char* pEnd = nullptr; + const double result = parse_double_c(pStart, &pEnd); + if (pEnd != pStart + size) { + return false; + } + val = static_cast(result); + return true; +#else + auto re = std::from_chars(pStart, pStart + size, val); + return re.ec == std::errc() && re.ptr == pStart + size; +#endif + } else { + auto re = std::from_chars(pStart, pStart + size, val); + return re.ec == std::errc() && re.ptr == pStart + size; } - return true; } } // namespace