From 16845131096bb7996035259cc79f95d128bd2d7f Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 9 Jul 2026 22:00:41 +1000 Subject: [PATCH] ifcparse: parse doubles via C-locale strtod_l on macOS (fix Apple build) parse_num_ used std::from_chars for both integers and doubles, but the floating-point from_chars overload is =deleted in Apple clang's libc++, so the macOS build failed to compile (parse.cpp:136, instantiated for double). Split parse_num_ with `if constexpr`: integers keep std::from_chars everywhere; on macOS, doubles parse via strtod_l with a cached "C" locale (locale-independent, restoring the pre-charconv Apple path). libstdc++ and the MSVC STL have working float from_chars and are left unchanged. Co-Authored-By: Claude Opus 4.8 --- src/ifcparse/parse.cpp | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) 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