From db67ad3fd67dbb52964639dee666af7b008f9da2 Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Mon, 20 Oct 2025 16:47:46 +0200 Subject: [PATCH] Align to_static_[w]string() with C++26 std::to_[w]string() C++26 specifies that std::to_string() and std::to_wstring() format floating point values as if using std::format(). This commit updates the internal conversion helpers to match that behavior, using std::format_to_n() for efficient, allocation-free formatting directly into the static_string/static_wstring buffer. Fallbacks using snprintf()/swprintf() remain active for pre-C++26 builds. This ensures consistent formatting across standard and Boost APIs. --- include/boost/static_string/config.hpp | 4 + include/boost/static_string/static_string.hpp | 109 ++++++++++++++++-- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/include/boost/static_string/config.hpp b/include/boost/static_string/config.hpp index a0f0095..a5b84ec 100644 --- a/include/boost/static_string/config.hpp +++ b/include/boost/static_string/config.hpp @@ -278,4 +278,8 @@ using basic_string_view = } // boost #endif +#if defined(__cpp_lib_to_string) && __cpp_lib_to_string >= 202306L // std::to_[w]string() redefined in terms of std::format() +#define BOOST_STATIC_STRING_USE_STD_FORMAT +#endif + #endif \ No newline at end of file diff --git a/include/boost/static_string/static_string.hpp b/include/boost/static_string/static_string.hpp index 092d90a..6382e0a 100644 --- a/include/boost/static_string/static_string.hpp +++ b/include/boost/static_string/static_string.hpp @@ -34,6 +34,9 @@ #include #include #include +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) +#include +#endif #include #include #include @@ -608,17 +611,42 @@ count_digits(std::size_t value) #pragma GCC diagnostic ignored "-Wformat-truncation" #endif +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) + +template +inline +static_string +cpp26_to_static_string(FloatingPoint value) noexcept +{ + using size_type = typename static_string::size_type; + static_string result; + result.resize_and_overwrite( + N, + [&](char* buffer, size_type) -> size_type + { + const auto formatted = std::format_to_n(buffer, N, "{}", value); + return formatted.size; + } + ); + return result; +} + +#endif + template inline static_string to_static_string_float_impl(double value) noexcept { +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) + return cpp26_to_static_string(value); +#else using size_type = typename static_string::size_type; + static_string result; // we have to assume here that no reasonable implementation // will require more than 2^63 chars to represent a float value. const long long narrow = static_cast(N); - static_string result; result.resize_and_overwrite( N, [&](char* buffer, size_type) -> size_type @@ -642,6 +670,19 @@ to_static_string_float_impl(double value) noexcept } ); return result; +#endif +} + +template +inline +static_string +to_static_string_float_impl(float value) noexcept +{ +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) + return cpp26_to_static_string(value); +#else + return to_static_string_float_impl(static_cast(value)); +#endif } template @@ -649,12 +690,15 @@ inline static_string to_static_string_float_impl(long double value) noexcept { +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) + return cpp26_to_static_string(value); +#else using size_type = typename static_string::size_type; + static_string result; // we have to assume here that no reasonable implementation // will require more than 2^63 chars to represent a float value. const long long narrow = static_cast(N); - static_string result; result.resize_and_overwrite( N, [&](char* buffer, size_type)->size_type @@ -680,20 +724,46 @@ to_static_string_float_impl(long double value) noexcept } ); return result; +#endif } #ifdef BOOST_STATIC_STRING_HAS_WCHAR + +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) +template +inline +static_wstring +cpp26_to_static_wstring(FloatingPoint value) noexcept +{ + using size_type = typename static_wstring::size_type; + static_wstring result; + result.resize_and_overwrite( + N, + [&](wchar_t* buffer, size_type) -> size_type + { + const auto formatted = std::format_to_n(buffer, N, L"{}", value); + return formatted.size; + } + ); + return result; +} + +#endif + template inline static_wstring to_static_wstring_float_impl(double value) noexcept { +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) + return cpp26_to_static_wstring(value); +#else using size_type = typename static_wstring::size_type; + static_wstring result; // we have to assume here that no reasonable implementation // will require more than 2^63 chars to represent a float value. const long long narrow = static_cast(N); - static_wstring result; result.resize_and_overwrite( N, [&](wchar_t* buffer, size_type) -> size_type @@ -724,6 +794,19 @@ to_static_wstring_float_impl(double value) noexcept } ); return result; +#endif +} + +template +inline +static_wstring +to_static_wstring_float_impl(float value) noexcept +{ +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) + return cpp26_to_static_wstring(value); +#else + return to_static_wstring_float_impl(static_cast(value)); +#endif } template @@ -731,12 +814,15 @@ inline static_wstring to_static_wstring_float_impl(long double value) noexcept { +#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT) + return cpp26_to_static_wstring(value); +#else using size_type = typename static_wstring::size_type; + static_wstring result; // we have to assume here that no reasonable implementation // will require more than 2^63 chars to represent a float value. const long long narrow = static_cast(N); - static_wstring result; result.resize_and_overwrite( N, [&](wchar_t* buffer, size_type) -> size_type @@ -767,6 +853,7 @@ to_static_wstring_float_impl(long double value) noexcept } ); return result; +#endif } #endif @@ -6213,11 +6300,13 @@ operator<<( // Unsigned overloads have a + 1, for the missing digit. -// Floating point overloads have a +7 (for float), + 8 +// Floating point overloads have a +8 (for float), + 8 // (for double), and +10 for long double (that accounts // for the sign of the integral part, the missing digit, // the decimal point, the sign of the exponent, the 'e' -// and up to two, three or five digits of exponent). +// and up to two, three or five digits of exponent---float +// uses the same value as double, because we sometimes +// reuse the conversion from double for floats). /// Converts `value` to a `static_string` static_string::digits10 + 2> @@ -6274,12 +6363,12 @@ to_static_string(unsigned long long value) noexcept } /// Converts `value` to a `static_string` -static_string::max_digits10 + 7> +static_string::max_digits10 + 8> inline to_static_string(float value) noexcept { return detail::to_static_string_float_impl< - std::numeric_limits::max_digits10 + 7>(value); + std::numeric_limits::max_digits10 + 8>(value); } /// Converts `value` to a `static_string` @@ -6356,12 +6445,12 @@ to_static_wstring(unsigned long long value) noexcept } /// Converts `value` to a `static_wstring` -static_wstring::max_digits10 + 7> +static_wstring::max_digits10 + 8> inline to_static_wstring(float value) noexcept { return detail::to_static_wstring_float_impl< - std::numeric_limits::max_digits10 + 7>(value); + std::numeric_limits::max_digits10 + 8>(value); } /// Converts `value` to a `static_wstring`