From 71eb0741f6cca2d30af167af715313c0009bdbed Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Fri, 23 Jan 2026 14:37:21 +0800 Subject: [PATCH] fix: try fix clang libcxx charconv issue. - add compile checker in cmake to detect charconv support status. - and expose macro for yycc to enable different part of charconv repectively to prevent duplicated defines. --- CMakeLists.txt | 3 + cmake/check_charconv.cmake | 30 ++++++++++ cmake/check_charconv/chars_format.cpp | 8 +++ cmake/check_charconv/from_chars_float.cpp | 16 ++++++ cmake/check_charconv/from_chars_int.cpp | 41 ++++++++++++++ cmake/check_charconv/from_chars_result.cpp | 9 +++ cmake/check_charconv/to_chars_float.cpp | 16 ++++++ cmake/check_charconv/to_chars_int.cpp | 41 ++++++++++++++ cmake/check_charconv/to_chars_result.cpp | 9 +++ src/CMakeLists.txt | 9 +++ src/yycc/num/parse.hpp | 5 +- src/yycc/num/stringify.hpp | 5 +- src/yycc/patch/libcxx/charconv.hpp | 66 +++++++++++++++------- 13 files changed, 231 insertions(+), 27 deletions(-) create mode 100644 cmake/check_charconv.cmake create mode 100644 cmake/check_charconv/chars_format.cpp create mode 100644 cmake/check_charconv/from_chars_float.cpp create mode 100644 cmake/check_charconv/from_chars_int.cpp create mode 100644 cmake/check_charconv/from_chars_result.cpp create mode 100644 cmake/check_charconv/to_chars_float.cpp create mode 100644 cmake/check_charconv/to_chars_int.cpp create mode 100644 cmake/check_charconv/to_chars_result.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a737d3c..4d5430a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,9 @@ set(YYCC_INSTALL_BIN_PATH ${CMAKE_INSTALL_BINDIR} CACHE PATH set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH "Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.") +# Test charconv support due to shitty clang's libcxx. +include(${CMAKE_CURRENT_LIST_DIR}/cmake/check_charconv.cmake) + # Include dependency. # GTest is required if we build test if (YYCC_BUILD_TEST) diff --git a/cmake/check_charconv.cmake b/cmake/check_charconv.cmake new file mode 100644 index 0000000..d730110 --- /dev/null +++ b/cmake/check_charconv.cmake @@ -0,0 +1,30 @@ +message(STATUS "Checking charconv implementation...") +include(CheckCXXSourceCompiles) + +file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/chars_format.cpp" TEST_CODE_SNIPPET) +check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_CHARS_FORMAT) +message(STATUS "Support std::chars_format: ${YYCC_CHARCONV_HAS_CHARS_FORMAT}") + +file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_result.cpp" TEST_CODE_SNIPPET) +check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_RESULT) +message(STATUS "Support std::from_chars_result: ${YYCC_CHARCONV_HAS_FROM_CHARS_RESULT}") + +file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_result.cpp" TEST_CODE_SNIPPET) +check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_RESULT) +message(STATUS "Support std::to_chars_result: ${YYCC_CHARCONV_HAS_TO_CHARS_RESULT}") + +file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_int.cpp" TEST_CODE_SNIPPET) +check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_INT) +message(STATUS "Support std::from_chars with integral type: ${YYCC_CHARCONV_HAS_FROM_CHARS_INT}") + +file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_float.cpp" TEST_CODE_SNIPPET) +check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT) +message(STATUS "Suppoer std::from_chars with float point type: ${YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT}") + +file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_int.cpp" TEST_CODE_SNIPPET) +check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_INT) +message(STATUS "Support std::to_chars with integral type: ${YYCC_CHARCONV_HAS_TO_CHARS_INT}") + +file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_float.cpp" TEST_CODE_SNIPPET) +check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_FLOAT) +message(STATUS "Support std::to_chars with float point type: ${YYCC_CHARCONV_HAS_TO_CHARS_FLOAT}") diff --git a/cmake/check_charconv/chars_format.cpp b/cmake/check_charconv/chars_format.cpp new file mode 100644 index 0000000..644c3fb --- /dev/null +++ b/cmake/check_charconv/chars_format.cpp @@ -0,0 +1,8 @@ +#include + +int main(int argc, char **argv) { + auto scientific = std::chars_format::scientific; + auto fixed = std::chars_format::fixed; + auto general = std::chars_format::general; + auto hex = std::chars_format::hex; +} diff --git a/cmake/check_charconv/from_chars_float.cpp b/cmake/check_charconv/from_chars_float.cpp new file mode 100644 index 0000000..4489aba --- /dev/null +++ b/cmake/check_charconv/from_chars_float.cpp @@ -0,0 +1,16 @@ +#include + +int main(int argc, char **argv) { + const char probe[] = "0.0"; + const char* first = probe; + const char* last = first + sizeof(probe); + + { + float value; + auto rv = std::from_chars(first, last, value, std::chars_format::general); + } + { + double value; + auto rv = std::from_chars(first, last, value, std::chars_format::general); + } +} diff --git a/cmake/check_charconv/from_chars_int.cpp b/cmake/check_charconv/from_chars_int.cpp new file mode 100644 index 0000000..576e897 --- /dev/null +++ b/cmake/check_charconv/from_chars_int.cpp @@ -0,0 +1,41 @@ +#include +#include + +int main(int argc, char **argv) { + const char probe[] = "0"; + const char* first = probe; + const char* last = first + sizeof(probe); + + { + std::int8_t value; + auto rv = std::from_chars(first, last, value, 10); + } + { + std::int16_t value; + auto rv = std::from_chars(first, last, value, 10); + } + { + std::int32_t value; + auto rv = std::from_chars(first, last, value, 10); + } + { + std::int64_t value; + auto rv = std::from_chars(first, last, value, 10); + } + { + std::uint8_t value; + auto rv = std::from_chars(first, last, value, 10); + } + { + std::uint16_t value; + auto rv = std::from_chars(first, last, value, 10); + } + { + std::uint32_t value; + auto rv = std::from_chars(first, last, value, 10); + } + { + std::uint64_t value; + auto rv = std::from_chars(first, last, value, 10); + } +} diff --git a/cmake/check_charconv/from_chars_result.cpp b/cmake/check_charconv/from_chars_result.cpp new file mode 100644 index 0000000..288b3df --- /dev/null +++ b/cmake/check_charconv/from_chars_result.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main(int argc, char **argv) { + std::from_chars_result result { + .ptr = nullptr, + .ec = std::errc{}, + }; +} diff --git a/cmake/check_charconv/to_chars_float.cpp b/cmake/check_charconv/to_chars_float.cpp new file mode 100644 index 0000000..2b58885 --- /dev/null +++ b/cmake/check_charconv/to_chars_float.cpp @@ -0,0 +1,16 @@ +#include + +int main(int argc, char **argv) { + char buffer[1024]; + char* first = buffer; + char* last = first + sizeof(buffer); + + { + float value = 0; + auto rv = std::to_chars(first, last, value, std::chars_format::general, 6); + } + { + double value = 0; + auto rv = std::to_chars(first, last, value, std::chars_format::general, 6); + } +} diff --git a/cmake/check_charconv/to_chars_int.cpp b/cmake/check_charconv/to_chars_int.cpp new file mode 100644 index 0000000..5b4160a --- /dev/null +++ b/cmake/check_charconv/to_chars_int.cpp @@ -0,0 +1,41 @@ +#include +#include + +int main(int argc, char **argv) { + char buffer[1024]; + char* first = buffer; + char* last = first + sizeof(buffer); + + { + std::int8_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } + { + std::int16_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } + { + std::int32_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } + { + std::int64_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } + { + std::uint8_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } + { + std::uint16_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } + { + std::uint32_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } + { + std::uint64_t value = 0; + auto rv = std::to_chars(first, last, value, 10); + } +} diff --git a/cmake/check_charconv/to_chars_result.cpp b/cmake/check_charconv/to_chars_result.cpp new file mode 100644 index 0000000..3d1478e --- /dev/null +++ b/cmake/check_charconv/to_chars_result.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main(int argc, char **argv) { + std::to_chars_result result { + .ptr = nullptr, + .ec = std::errc{}, + }; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1406e95..20411e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -167,6 +167,15 @@ PUBLIC # Fix Windows header file shit $<$:WIN32_LEAN_AND_MEAN> $<$:NOMINMAX> +PUBLIC + $<$:YYCC_CHARCONV_HAS_CHARS_FORMAT> + $<$:YYCC_CHARCONV_HAS_FROM_CHARS_RESULT> + $<$:YYCC_CHARCONV_HAS_TO_CHARS_RESULT> + $<$:YYCC_CHARCONV_HAS_FROM_CHARS_INT> + $<$:YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT> + $<$:YYCC_CHARCONV_HAS_TO_CHARS_INT> + $<$:YYCC_CHARCONV_HAS_TO_CHARS_FLOAT> + ) target_compile_options(YYCCommonplace PUBLIC diff --git a/src/yycc/num/parse.hpp b/src/yycc/num/parse.hpp index e0a6cb9..11863d9 100644 --- a/src/yycc/num/parse.hpp +++ b/src/yycc/num/parse.hpp @@ -5,11 +5,10 @@ #include #include #include +#include #if defined(YYCC_STL_CLANGSTL) -#include "patch/libcxx/charconv.hpp" -#else -#include +#include "../patch/libcxx/charconv.hpp" #endif #define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret diff --git a/src/yycc/num/stringify.hpp b/src/yycc/num/stringify.hpp index f426fcf..4851c75 100644 --- a/src/yycc/num/stringify.hpp +++ b/src/yycc/num/stringify.hpp @@ -4,11 +4,10 @@ #include #include #include +#include #if defined(YYCC_STL_CLANGSTL) -#include "patch/libcxx/charconv.hpp" -#else -#include +#include "../patch/libcxx/charconv.hpp" #endif #define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret diff --git a/src/yycc/patch/libcxx/charconv.hpp b/src/yycc/patch/libcxx/charconv.hpp index 2a02dfa..c87077b 100644 --- a/src/yycc/patch/libcxx/charconv.hpp +++ b/src/yycc/patch/libcxx/charconv.hpp @@ -1,7 +1,7 @@ #pragma once #include "../../macro/stl_detector.hpp" -#if defined(YYCC_STL_CLANGSTL) +//#if defined(YYCC_STL_CLANGSTL) /** * @private @@ -13,6 +13,7 @@ * This polyfill is generated by AI. */ +#include #include #include #include @@ -26,6 +27,8 @@ namespace std { +#if !defined(YYCC_CHARCONV_HAS_CHARS_FORMAT) + enum class chars_format : unsigned int { scientific = 1, fixed = 2, @@ -33,6 +36,10 @@ namespace std { general = fixed | scientific // This should be 6 (fixed|scientific) }; +#endif + +#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_RESULT) + struct from_chars_result { const char* ptr; std::errc ec; @@ -41,6 +48,10 @@ namespace std { constexpr explicit operator bool() const noexcept { return ec == std::errc{}; } }; +#endif + +#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_RESULT) + struct to_chars_result { char* ptr; std::errc ec; @@ -49,6 +60,10 @@ namespace std { constexpr explicit operator bool() const noexcept { return ec == std::errc{}; } }; +#endif + +#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_INT) + /// @private enum class __integral_type { u8, @@ -172,16 +187,21 @@ namespace std { // Use snprintf with appropriate format const auto max_buffer_size = static_cast(last - first); const char* format_string = __get_int_format(integral_base_type); - int written = std::snprintf(first, max_buffer_size, format_string, val); + int written = std::snprintf(first, max_buffer_size, format_string, value); if (written < 0 || static_cast(written) >= max_buffer_size) { return {last, std::errc::value_too_large}; } return {first + written, std::errc{}}; } +#endif + +#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_FLOAT) + /// @private enum class __float_type { - f32, f64, + f32, + f64, }; /// @private @@ -195,12 +215,10 @@ namespace std { static_assert(false, "Unsupported floating point type"); } } - + /// @private - enum class __float_fmt_type { - general, scientific, fixed, hex - }; - + enum class __float_fmt_type { general, scientific, fixed, hex }; + /// @private std::optional<__float_fmt_type> __classify_float_fmt(chars_format fmt) { if (fmt == chars_format::general) { @@ -265,7 +283,7 @@ namespace std { const auto float_fmt_type = std::move(opt_float_fmt_type.value()); const auto max_buffer_size = static_cast(last - first); - const char *format_string = __get_float_format(float_fmt_type); + const char* format_string = __get_float_format(float_fmt_type); int written = std::snprintf(first, max_buffer_size, format_string, precision, value); if (written < 0 || static_cast(written) >= max_buffer_size) { return {last, std::errc::value_too_large}; @@ -273,10 +291,12 @@ namespace std { return {first + written, std::errc{}}; } +#endif + +#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_INT) + /// @private - enum class __strtoi_cluster { - tol, toll, toul, toull - }; + enum class __strtoi_cluster { tol, toll, toul, toull }; /// @private template @@ -330,19 +350,19 @@ namespace std { errno = 0; char* end_ptr = const_cast(first); - auto rv = __execute_strtoi(buffer.data(), &end_ptr, base); + auto result = __execute_strtoi(buffer.data(), &end_ptr, base); if (errno == ERANGE) { return {end_ptr, std::errc::result_out_of_range}; } - using strtoi_cluster_rvtype = decltype(rv); + using strtoi_cluster_rvtype = decltype(result); // Check if result fits in T if (result < static_cast(std::numeric_limits::min()) || result > static_cast(std::numeric_limits::max())) { return {end_ptr, std::errc::result_out_of_range}; } - if (end_ptr == buffer.data) { + if (end_ptr == buffer.data()) { return {first, std::errc::invalid_argument}; } @@ -355,10 +375,12 @@ namespace std { return {first + (end_ptr - buffer.data()), std::errc{}}; } +#endif + +#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT) + /// @private - enum class __strtof_cluster { - tof, tod - }; + enum class __strtof_cluster { tof, tod }; /// @private template @@ -399,12 +421,12 @@ namespace std { errno = 0; char* end_ptr = const_cast(first); - auto rv = __execute_strtof(buffer.data(), &end_ptr); + auto result = __execute_strtof(buffer.data(), &end_ptr); if (errno == ERANGE) { return {end_ptr, std::errc::result_out_of_range}; } - if (end_ptr == buffer.data) { + if (end_ptr == buffer.data()) { return {first, std::errc::invalid_argument}; } @@ -417,6 +439,8 @@ namespace std { return {first + (end_ptr - buffer.data()), std::errc{}}; } +#endif + } // namespace std -#endif +//#endif