On Mon, 1 Jun 2026 at 16:10, Tomasz Kamiński <[email protected]> wrote:
>
> From: Ivan Lazaric <[email protected]>
>
> In C++26 paper P3391, "constexpr formatting", has been adopted,
> part of which marks std::to_string & std::to_wstring for integers
> as constexpr. The __cpp_lib_constexpr_string FTM value is updated
> per resolution of LWG4531, "Should there be a feature-test macro
> update for constexpr std::to_(w)string?".
>
> Since pre-cxx11 copy-on-write string is not constexpr-enabled,
> restricting this constexpr-ification to cxx11 ABI strings.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/version.def (constexpr_string): Bump to 202511.
>         * include/bits/versin.h: Regenerate.
>         * include/bits/basic_string.h (std::to_string, std::to_wstring)
>         [__glibcxx_constexpr_string >= 202511L]: Mark as constexpr.
>         * 
> testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc:
>         New test.
>         * 
> testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc:
>         New test.
>         * testsuite/21_strings/basic_string/cons/char/constexpr.cc: Update
>         __cpp_lib_constexpr_string check.
>         testsuite/21_strings/basic_string/cons/wchar_t/constexpr.c: Likewise.
>         * 21_strings/basic_string/version.cc: Add check for value of
>         __cpp_lib_constexpr_string in C++26.
>
> Co-authored-by: Tomasz Kamiński <[email protected]>
> ---
> v3 introduces _GLIBCXX_TO_STRING_CONSTEXPR as suggested by
> Nathan.

OK, thanks


>
>  libstdc++-v3/include/bits/basic_string.h      | 31 +++++----
>  libstdc++-v3/include/bits/version.def         |  8 +++
>  libstdc++-v3/include/bits/version.h           |  7 +-
>  .../basic_string/cons/char/constexpr.cc       |  4 +-
>  .../basic_string/cons/wchar_t/constexpr.cc    |  4 +-
>  .../char/to_string_constexpr.cc               | 64 +++++++++++++++++++
>  .../wchar_t/to_wstring_constexpr.cc           | 64 +++++++++++++++++++
>  .../21_strings/basic_string/version.cc        | 12 ++++
>  8 files changed, 177 insertions(+), 17 deletions(-)
>  create mode 100644 
> libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
>  create mode 100644 
> libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
>
> diff --git a/libstdc++-v3/include/bits/basic_string.h 
> b/libstdc++-v3/include/bits/basic_string.h
> index 23e6979f03d..65d92ebfbcf 100644
> --- a/libstdc++-v3/include/bits/basic_string.h
> +++ b/libstdc++-v3/include/bits/basic_string.h
> @@ -4607,11 +4607,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>    { return std::stod(__str, __idx); }
>  #endif
>
> +#if __glibcxx_constexpr_string >= 202511L
> +# define _GLIBCXX_TO_STRING_CONSTEXPR constexpr
> +#else
> +# define _GLIBCXX_TO_STRING_CONSTEXPR inline
> +#endif
> +
>    // _GLIBCXX_RESOLVE_LIB_DEFECTS
>    // DR 1261. Insufficient overloads for to_string / to_wstring
>
>    _GLIBCXX_NODISCARD
> -  inline string
> +  _GLIBCXX_TO_STRING_CONSTEXPR string
>    to_string(int __val)
>  #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32
>    noexcept // any 32-bit value fits in the SSO buffer
> @@ -4630,7 +4636,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>    }
>
>    _GLIBCXX_NODISCARD
> -  inline string
> +  _GLIBCXX_TO_STRING_CONSTEXPR string
>    to_string(unsigned __val)
>  #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32
>    noexcept // any 32-bit value fits in the SSO buffer
> @@ -4646,7 +4652,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>    }
>
>    _GLIBCXX_NODISCARD
> -  inline string
> +  _GLIBCXX_TO_STRING_CONSTEXPR string
>    to_string(long __val)
>  #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32
>    noexcept // any 32-bit value fits in the SSO buffer
> @@ -4665,7 +4671,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>    }
>
>    _GLIBCXX_NODISCARD
> -  inline string
> +  _GLIBCXX_TO_STRING_CONSTEXPR string
>    to_string(unsigned long __val)
>  #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32
>    noexcept // any 32-bit value fits in the SSO buffer
> @@ -4681,7 +4687,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>    }
>
>    _GLIBCXX_NODISCARD
> -  inline string
> +  _GLIBCXX_TO_STRING_CONSTEXPR string
>    to_string(long long __val)
>    {
>      const bool __neg = __val < 0;
> @@ -4698,7 +4704,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>    }
>
>    _GLIBCXX_NODISCARD
> -  inline string
> +  _GLIBCXX_TO_STRING_CONSTEXPR string
>    to_string(unsigned long long __val)
>    {
>      const auto __len = __detail::__to_chars_len(__val);
> @@ -4922,32 +4928,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>  #pragma GCC diagnostic pop
>
>    _GLIBCXX_NODISCARD
> -  inline wstring
> +  _GLIBCXX_TO_STRING_CONSTEXPR wstring
>    to_wstring(int __val)
>    { return std::__to_wstring_numeric(std::to_string(__val)); }
>
>    _GLIBCXX_NODISCARD
> -  inline wstring
> +  _GLIBCXX_TO_STRING_CONSTEXPR wstring
>    to_wstring(unsigned __val)
>    { return std::__to_wstring_numeric(std::to_string(__val)); }
>
>    _GLIBCXX_NODISCARD
> -  inline wstring
> +  _GLIBCXX_TO_STRING_CONSTEXPR wstring
>    to_wstring(long __val)
>    { return std::__to_wstring_numeric(std::to_string(__val)); }
>
>    _GLIBCXX_NODISCARD
> -  inline wstring
> +  _GLIBCXX_TO_STRING_CONSTEXPR wstring
>    to_wstring(unsigned long __val)
>    { return std::__to_wstring_numeric(std::to_string(__val)); }
>
>    _GLIBCXX_NODISCARD
> -  inline wstring
> +  _GLIBCXX_TO_STRING_CONSTEXPR wstring
>    to_wstring(long long __val)
>    { return std::__to_wstring_numeric(std::to_string(__val)); }
>
>    _GLIBCXX_NODISCARD
> -  inline wstring
> +  _GLIBCXX_TO_STRING_CONSTEXPR wstring
>    to_wstring(unsigned long long __val)
>    { return std::__to_wstring_numeric(std::to_string(__val)); }
>
> @@ -4968,6 +4974,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>    { return std::__to_wstring_numeric(std::to_string(__val)); }
>  #endif
>  #endif // _GLIBCXX_USE_WCHAR_T
> +#undef _GLIBCXX_TO_STRING_CONSTEXPR
>
>  _GLIBCXX_END_NAMESPACE_CXX11
>  _GLIBCXX_END_NAMESPACE_VERSION
> diff --git a/libstdc++-v3/include/bits/version.def 
> b/libstdc++-v3/include/bits/version.def
> index 97f7d686519..ea8560778e5 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1398,6 +1398,14 @@ ftms = {
>
>  ftms = {
>    name = constexpr_string;
> +  // 202511 LWG4531 Should there be a feature-test macro update for 
> constexpr std::to_(w)string?
> +  //        P3391R2 constexpr std::format
> +  values = {
> +    v = 202511;
> +    cxxmin = 26;
> +    hosted = yes;
> +    cxx11abi = yes;
> +  };
>    values = {
>      v = 201907;
>      cxxmin = 20;
> diff --git a/libstdc++-v3/include/bits/version.h 
> b/libstdc++-v3/include/bits/version.h
> index 33eecbfb83b..59d5d9217d7 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -1542,7 +1542,12 @@
>  #undef __glibcxx_want_constexpr_flat_set
>
>  #if !defined(__cpp_lib_constexpr_string)
> -# if (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI && _GLIBCXX_HOSTED 
> && (defined(__glibcxx_is_constant_evaluated))
> +# if (__cplusplus >  202302L) && _GLIBCXX_USE_CXX11_ABI && _GLIBCXX_HOSTED
> +#  define __glibcxx_constexpr_string 202511L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_string)
> +#   define __cpp_lib_constexpr_string 202511L
> +#  endif
> +# elif (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI && _GLIBCXX_HOSTED 
> && (defined(__glibcxx_is_constant_evaluated))
>  #  define __glibcxx_constexpr_string 201907L
>  #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_string)
>  #   define __cpp_lib_constexpr_string 201907L
> diff --git 
> a/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc 
> b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc
> index 7822c89497b..34abfe60bc5 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc
> @@ -6,8 +6,8 @@
>
>  #ifndef __cpp_lib_constexpr_string
>  # error "Feature-test macro for constexpr std::string missing in <string>"
> -#elif __cpp_lib_constexpr_string != 201907L
> -# error "Feature-test macro for constexpr std::string has wrong value in 
> <string>"
> +#elif __cpp_lib_constexpr_string < 201907L
> +# error "Feature-test macro for constexpr std::string has too small value in 
> <string>"
>  #endif
>
>  #include <testsuite_hooks.h>
> diff --git 
> a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc 
> b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
> index 44c8391ebc2..59d5ee270cc 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
> @@ -6,8 +6,8 @@
>
>  #ifndef __cpp_lib_constexpr_string
>  # error "Feature-test macro for constexpr std::string missing in <string>"
> -#elif __cpp_lib_constexpr_string != 201907L
> -# error "Feature-test macro for constexpr std::string has wrong value in 
> <string>"
> +#elif __cpp_lib_constexpr_string < 201907L
> +# error "Feature-test macro for constexpr std::string too small value in 
> <string>"
>  #endif
>
>  #include <testsuite_hooks.h>
> diff --git 
> a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
>  
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
> new file mode 100644
> index 00000000000..1b5a616df54
> --- /dev/null
> +++ 
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
> @@ -0,0 +1,64 @@
> +// { dg-do compile { target c++26 } }
> +// { dg-require-effective-target cxx11_abi }
> +
> +#include <string>
> +#include <type_traits>
> +#include <testsuite_hooks.h>
> +
> +template<typename T>
> +constexpr void
> +test()
> +{
> +  using namespace std;
> +  string res;
> +  T value;
> +
> +  value = 0;
> +  res = to_string(value);
> +  VERIFY( res == "0" );
> +
> +  value = 1;
> +  res = to_string(value);
> +  VERIFY( res == "1" );
> +
> +  value = 10;
> +  res = to_string(value);
> +  VERIFY( res == "10" );
> +
> +  value = 3000;
> +  res = to_string(value);
> +  VERIFY( res == "3000" );
> +
> +  value = 32767;
> +  res = to_string(value);
> +  VERIFY( res == "32767" );
> +
> +  if (is_unsigned_v<T>)
> +    return;
> +
> +  value = -1;
> +  res = to_string(value);
> +  VERIFY( res == "-1" );
> +
> +  value = -40;
> +  res = to_string(value);
> +  VERIFY( res == "-40" );
> +
> +  value = -32768;
> +  res = to_string(value);
> +  VERIFY( res == "-32768" );
> +}
> +
> +constexpr bool
> +test_all()
> +{
> +  test<int>();
> +  test<unsigned int>();
> +  test<long>();
> +  test<unsigned long>();
> +  test<long long>();
> +  test<unsigned long long>();
> +  return true;
> +}
> +
> +static_assert(test_all());
> diff --git 
> a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
>  
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
> new file mode 100644
> index 00000000000..f884fc61ff5
> --- /dev/null
> +++ 
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
> @@ -0,0 +1,64 @@
> +// { dg-do compile { target c++26 } }
> +// { dg-require-effective-target cxx11_abi }
> +
> +#include <string>
> +#include <type_traits>
> +#include <testsuite_hooks.h>
> +
> +template<typename T>
> +constexpr void
> +test()
> +{
> +  using namespace std;
> +  wstring res;
> +  T value;
> +
> +  value = 0;
> +  res = to_wstring(value);
> +  VERIFY( res == L"0" );
> +
> +  value = 1;
> +  res = to_wstring(value);
> +  VERIFY( res == L"1" );
> +
> +  value = 10;
> +  res = to_wstring(value);
> +  VERIFY( res == L"10" );
> +
> +  value = 3000;
> +  res = to_wstring(value);
> +  VERIFY( res == L"3000" );
> +
> +  value = 32767;
> +  res = to_wstring(value);
> +  VERIFY( res == L"32767" );
> +
> +  if (is_unsigned_v<T>)
> +    return;
> +
> +  value = -1;
> +  res = to_wstring(value);
> +  VERIFY( res == L"-1" );
> +
> +  value = -40;
> +  res = to_wstring(value);
> +  VERIFY( res == L"-40" );
> +
> +  value = -32768;
> +  res = to_wstring(value);
> +  VERIFY( res == L"-32768" );
> +}
> +
> +constexpr bool
> +test_all()
> +{
> +  test<int>();
> +  test<unsigned int>();
> +  test<long>();
> +  test<unsigned long>();
> +  test<long long>();
> +  test<unsigned long long>();
> +  return true;
> +}
> +
> +static_assert(test_all());
> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/version.cc 
> b/libstdc++-v3/testsuite/21_strings/basic_string/version.cc
> index 71dd4dfb67f..b0cdf04274e 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/version.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/version.cc
> @@ -25,3 +25,15 @@
>  #  endif
>  # endif
>  #endif
> +
> +#if __cplusplus > 202302L
> +# if _GLIBCXX_USE_CXX11_ABI
> +#  if __cpp_lib_constexpr_string != 202511L
> +#   error "Feature-test macro for constexpr std::string has wrong value for 
> C++20 in <version>"
> +#  endif
> +# else // COW strings
> +#  if __cpp_lib_constexpr_string != 201811L
> +#   error "Feature-test macro for constexpr std::string has wrong value for 
> C++20 in <version>"
> +#  endif
> +# endif
> +#endif
> --
> 2.54.0
>

Reply via email to