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]>
---
v2 changed:
- bumped constexpr_string per LWG4531, and used it for preprocessor
check in basic_string
- updated checks for __cpp_lib_constexpr_string in existing tests
- used require cxx11_abi target instead of if predefines in test files
Tested on x86_64-linux. OK for trunk?
libstdc++-v3/include/bits/basic_string.h | 36 +++++++++++
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, 194 insertions(+), 5 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 af4e5d9486f..39477157858 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -4581,6 +4581,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
// DR 1261. Insufficent overloads for to_string / to_wstring
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline string
to_string(int __val)
#if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32
@@ -4600,6 +4603,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
}
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline string
to_string(unsigned __val)
#if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32
@@ -4616,6 +4622,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
}
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline string
to_string(long __val)
#if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32
@@ -4635,6 +4644,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
}
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline string
to_string(unsigned long __val)
#if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32
@@ -4651,6 +4663,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
}
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline string
to_string(long long __val)
{
@@ -4668,6 +4683,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
}
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline string
to_string(unsigned long long __val)
{
@@ -4892,31 +4910,49 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#pragma GCC diagnostic pop
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline wstring
to_wstring(int __val)
{ return std::__to_wstring_numeric(std::to_string(__val)); }
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline wstring
to_wstring(unsigned __val)
{ return std::__to_wstring_numeric(std::to_string(__val)); }
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline wstring
to_wstring(long __val)
{ return std::__to_wstring_numeric(std::to_string(__val)); }
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline wstring
to_wstring(unsigned long __val)
{ return std::__to_wstring_numeric(std::to_string(__val)); }
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline wstring
to_wstring(long long __val)
{ return std::__to_wstring_numeric(std::to_string(__val)); }
_GLIBCXX_NODISCARD
+#if __glibcxx_constexpr_string >= 202511L
+ constexpr
+#endif
inline wstring
to_wstring(unsigned long long __val)
{ return std::__to_wstring_numeric(std::to_string(__val)); }
diff --git a/libstdc++-v3/include/bits/version.def
b/libstdc++-v3/include/bits/version.def
index 1f0d3a2670e..6d789987642 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 66ac0ebef68..c44fe07ec60 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