[libcxx-commits] [libcxx] [libc++] P3391R2: `constexpr` integral overloads of `to_(w)string` (PR #205025)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Jun 21 20:23:18 PDT 2026
https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/205025
Implements a small part of P3391R2. Also implements related LWG4531.
Fixes #204454.
>From 5b0c184b98c5cdce7f20e76b8208a3c219e29b94 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 22 Jun 2026 11:17:25 +0800
Subject: [PATCH] [libc++] P3391R2: `constexpr` integral overloads of
`to_(w)string`
Also implements related LWG4531.
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +
libcxx/docs/Status/Cxx26Papers.csv | 2 +-
libcxx/docs/Status/Cxx29Issues.csv | 2 +-
libcxx/include/string | 55 +++++++++++
libcxx/include/version | 5 +-
libcxx/src/string.cpp | 50 +++++-----
.../string.version.compile.pass.cpp | 4 +-
.../version.version.compile.pass.cpp | 4 +-
.../string.conversions/to_string.pass.cpp | 15 ++-
.../string.conversions/to_wstring.pass.cpp | 15 ++-
libcxx/test/support/parse_integer.h | 99 +++++++++++++++----
.../generate_feature_test_macro_components.py | 2 +-
12 files changed, 197 insertions(+), 58 deletions(-)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8e26bdcd860c1..3baa0acb5c06a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -450,6 +450,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_constexpr_queue`` ``202502L``
---------------------------------------------------------- -----------------
+ ``__cpp_lib_constexpr_string`` ``202511L``
+ ---------------------------------------------------------- -----------------
``__cpp_lib_constrained_equality`` ``202411L``
---------------------------------------------------------- -----------------
``__cpp_lib_copyable_function`` *unimplemented*
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index a951ac4c6c833..8dce1ed74811a 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -175,7 +175,7 @@
"`P3887R1 <https://wg21.link/P3887R1>`__","Make ``when_all`` a Ronseal Algorithm","2025-11 (Kona)","","","`#171289 <https://github.com/llvm/llvm-project/issues/171289>`__",""
"`P3923R0 <https://wg21.link/P3923R0>`__","Additional NB comment resolutions for Kona 2025","2025-11 (Kona)","","","`#171290 <https://github.com/llvm/llvm-project/issues/171290>`__",""
"`P3371R5 <https://wg21.link/P3371R5>`__","Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS","2025-11 (Kona)","","","`#171291 <https://github.com/llvm/llvm-project/issues/171291>`__",""
-"`P3391R2 <https://wg21.link/P3391R2>`__","``constexpr`` ``std::format``","2025-11 (Kona)","","","`#171292 <https://github.com/llvm/llvm-project/issues/171292>`__",""
+"`P3391R2 <https://wg21.link/P3391R2>`__","``constexpr`` ``std::format``","2025-11 (Kona)","|Partial|","","`#171292 <https://github.com/llvm/llvm-project/issues/171292>`__","``std::to_(w)string`` parts are implemented in LLVM 23."
"`P3913R1 <https://wg21.link/P3913R1>`__","Optimize ``for std::optional`` in range adaptors","2025-11 (Kona)","","","`#171294 <https://github.com/llvm/llvm-project/issues/171294>`__",""
"","","","","","",""
"`P3726R2 <https://wg21.link/P3726R2>`__","Adjustments to Union Lifetime Rules","2026-03 (Croydon)","","","`#189560 <https://github.com/llvm/llvm-project/issues/189560>`__",""
diff --git a/libcxx/docs/Status/Cxx29Issues.csv b/libcxx/docs/Status/Cxx29Issues.csv
index d9e245a9a11c0..072ab8effa121 100644
--- a/libcxx/docs/Status/Cxx29Issues.csv
+++ b/libcxx/docs/Status/Cxx29Issues.csv
@@ -13,7 +13,7 @@
"`LWG4487 <https://wg21.link/LWG4487>`__","Is member ``is_steady`` of a *Cpp17Clock* type required to be usable in constant expressions?","2026-06 (Brno)","","","`#204451 <https://github.com/llvm/llvm-project/issues/204451>`__",""
"`LWG4490 <https://wg21.link/LWG4490>`__","Allow calling ``std::ranges::size`` in ranges algorithms","2026-06 (Brno)","","","`#204452 <https://github.com/llvm/llvm-project/issues/204452>`__",""
"`LWG4521 <https://wg21.link/LWG4521>`__","Improve [atomics.order] p10 to have a consistent way with [intro.races]","2026-06 (Brno)","","","`#204453 <https://github.com/llvm/llvm-project/issues/204453>`__",""
-"`LWG4531 <https://wg21.link/LWG4531>`__","Should there be a feature-test macro update for ``constexpr`` ``std::to_(w)string``?","2026-06 (Brno)","","","`#204454 <https://github.com/llvm/llvm-project/issues/204454>`__",""
+"`LWG4531 <https://wg21.link/LWG4531>`__","Should there be a feature-test macro update for ``constexpr`` ``std::to_(w)string``?","2026-06 (Brno)","|Complete|","23","`#204454 <https://github.com/llvm/llvm-project/issues/204454>`__",""
"`LWG4567 <https://wg21.link/LWG4567>`__","Feature test macro value for ``apply_result``, ``is_applicable``","2026-06 (Brno)","","","`#204455 <https://github.com/llvm/llvm-project/issues/204455>`__",""
"`LWG4568 <https://wg21.link/LWG4568>`__","``std::execution::spawn_future`` is mishandling dependent senders","2026-06 (Brno)","","","`#204456 <https://github.com/llvm/llvm-project/issues/204456>`__",""
"`LWG4579 <https://wg21.link/LWG4579>`__","``make-state<Rcvr>::state-type::complete`` uses ``Env`` which is not in scope","2026-06 (Brno)","","","`#204457 <https://github.com/llvm/llvm-project/issues/204457>`__",""
diff --git a/libcxx/include/string b/libcxx/include/string
index e5bc854cd34f3..531ee2458b944 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -663,6 +663,12 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
# include <cwchar>
# endif
+# if _LIBCPP_STD_VER >= 26
+# include <__charconv/to_chars_integral.h>
+# include <__charconv/to_chars_result.h>
+# include <__system_error/errc.h>
+# endif
+
// standard-mandated includes
// [iterator.range]
@@ -3774,12 +3780,41 @@ stoull(const string& __str, size_t* __idx = nullptr, int __base = 10);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const string& __str, size_t* __idx = nullptr);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const string& __str, size_t* __idx = nullptr);
+# if _LIBCPP_STD_VER >= 26
+
+template <class _String, class _Integer>
+constexpr _String __integer_to_string(_Integer __val) {
+ // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
+ // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented), so we need +1 here.
+ constexpr size_t __buf_size = numeric_limits<_Integer>::digits10 + 2; // +1 for minus, +1 for digits10
+ char __buf[__buf_size];
+ const auto [__ptr, __ec] = std::to_chars(__buf, __buf + __buf_size, __val);
+ _LIBCPP_ASSERT_INTERNAL(__ec == errc(), "__buf_size must be large enough to accomodate the value");
+ return _String(__buf, __ptr);
+}
+
+inline namespace __cpp26 {
+
+[[nodiscard]] constexpr string to_string(int __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(unsigned __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(long __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(unsigned long __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(long long __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(unsigned long long __val) { return std::__integer_to_string<string>(__val); }
+
+} // namespace __cpp26
+
+# else // _LIBCPP_STD_VER >= 26
+
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long __val);
+
+# endif // _LIBCPP_STD_VER >= 26
+
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(float __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(double __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long double __val);
@@ -3798,12 +3833,32 @@ stoull(const wstring& __str, size_t* __idx = nullptr, int __base = 10);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const wstring& __str, size_t* __idx = nullptr);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const wstring& __str, size_t* __idx = nullptr);
+# if _LIBCPP_STD_VER >= 26
+
+inline namespace __cpp26 {
+
+[[nodiscard]] constexpr wstring to_wstring(int __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(unsigned __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(long __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(unsigned long __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(long long __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(unsigned long long __val) {
+ return std::__integer_to_string<wstring>(__val);
+}
+
+} // namespace __cpp26
+
+# else // _LIBCPP_STD_VER >= 26
+
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long __val);
+
+# endif // _LIBCPP_STD_VER >= 26
+
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(float __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(double __val);
[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long double __val);
diff --git a/libcxx/include/version b/libcxx/include/version
index 7f2dc9e4b72ab..ed278dff0f59f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -84,7 +84,8 @@ __cpp_lib_constexpr_memory 202202L <memory>
__cpp_lib_constexpr_new 202406L <new>
__cpp_lib_constexpr_numeric 201911L <numeric>
__cpp_lib_constexpr_queue 202502L <queue>
-__cpp_lib_constexpr_string 201907L <string>
+__cpp_lib_constexpr_string 202511L <string>
+ 201907L // C++20
__cpp_lib_constexpr_string_view 201811L <string_view>
__cpp_lib_constexpr_tuple 201811L <tuple>
__cpp_lib_constexpr_typeinfo 202106L <typeinfo>
@@ -575,6 +576,8 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_constexpr_new 202406L
# endif
# define __cpp_lib_constexpr_queue 202502L
+# undef __cpp_lib_constexpr_string
+# define __cpp_lib_constexpr_string 202511L
# define __cpp_lib_constrained_equality 202411L
// # define __cpp_lib_copyable_function 202306L
// # define __cpp_lib_debugging 202311L
diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp
index b55f9ff5c9a43..71a0689eecc17 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -328,34 +328,36 @@ inline wide_printf get_swprintf() {
}
#endif // _LIBCPP_HAS_WIDE_CHARACTERS
-template <typename S, typename V>
-S i_to_string(V v) {
- // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
- // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented),
- // so we need +1 here.
- constexpr size_t bufsize = numeric_limits<V>::digits10 + 2; // +1 for minus, +1 for digits10
- char buf[bufsize];
- const auto res = to_chars(buf, buf + bufsize, v);
- _LIBCPP_ASSERT_INTERNAL(res.ec == errc(), "bufsize must be large enough to accomodate the value");
- return S(buf, res.ptr);
-}
-
} // unnamed namespace
-string to_string(int val) { return i_to_string< string>(val); }
-string to_string(long val) { return i_to_string< string>(val); }
-string to_string(long long val) { return i_to_string< string>(val); }
-string to_string(unsigned val) { return i_to_string< string>(val); }
-string to_string(unsigned long val) { return i_to_string< string>(val); }
-string to_string(unsigned long long val) { return i_to_string< string>(val); }
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long);
+
+string to_string(int val) { return __cpp26::to_string(val); }
+string to_string(long val) { return __cpp26::to_string(val); }
+string to_string(long long val) { return __cpp26::to_string(val); }
+string to_string(unsigned val) { return __cpp26::to_string(val); }
+string to_string(unsigned long val) { return __cpp26::to_string(val); }
+string to_string(unsigned long long val) { return __cpp26::to_string(val); }
#if _LIBCPP_HAS_WIDE_CHARACTERS
-wstring to_wstring(int val) { return i_to_string<wstring>(val); }
-wstring to_wstring(long val) { return i_to_string<wstring>(val); }
-wstring to_wstring(long long val) { return i_to_string<wstring>(val); }
-wstring to_wstring(unsigned val) { return i_to_string<wstring>(val); }
-wstring to_wstring(unsigned long val) { return i_to_string<wstring>(val); }
-wstring to_wstring(unsigned long long val) { return i_to_string<wstring>(val); }
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long);
+
+wstring to_wstring(int val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(long val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(long long val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(unsigned val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(unsigned long val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(unsigned long long val) { return __cpp26::to_wstring(val); }
#endif
string to_string(float val) { return as_string(snprintf, initial_string< string>()(), "%f", val); }
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
index 147854eead2cc..324dc4dbe3200 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
@@ -417,8 +417,8 @@
# ifndef __cpp_lib_constexpr_string
# error "__cpp_lib_constexpr_string should be defined in c++26"
# endif
-# if __cpp_lib_constexpr_string != 201907L
-# error "__cpp_lib_constexpr_string should have the value 201907L in c++26"
+# if __cpp_lib_constexpr_string != 202511L
+# error "__cpp_lib_constexpr_string should have the value 202511L in c++26"
# endif
# ifndef __cpp_lib_containers_ranges
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index d9c78b73f7e23..aed1162977efc 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -6747,8 +6747,8 @@
# ifndef __cpp_lib_constexpr_string
# error "__cpp_lib_constexpr_string should be defined in c++26"
# endif
-# if __cpp_lib_constexpr_string != 201907L
-# error "__cpp_lib_constexpr_string should have the value 201907L in c++26"
+# if __cpp_lib_constexpr_string != 202511L
+# error "__cpp_lib_constexpr_string should have the value 202511L in c++26"
# endif
# ifndef __cpp_lib_constexpr_string_view
diff --git a/libcxx/test/std/strings/string.conversions/to_string.pass.cpp b/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
index 4731a072e8910..a36d21592b08d 100644
--- a/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
@@ -26,7 +26,7 @@
#include "test_macros.h"
template <class T>
-void test_signed() {
+TEST_CONSTEXPR_CXX26 void test_signed() {
{
std::string s = std::to_string(T(0));
assert(s.size() == 1);
@@ -59,7 +59,7 @@ void test_signed() {
}
template <class T>
-void test_unsigned() {
+TEST_CONSTEXPR_CXX26 void test_unsigned() {
{
std::string s = std::to_string(T(0));
assert(s.size() == 1);
@@ -102,16 +102,25 @@ void test_float() {
}
}
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test_integers() {
test_signed<int>();
test_signed<long>();
test_signed<long long>();
test_unsigned<unsigned>();
test_unsigned<unsigned long>();
test_unsigned<unsigned long long>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_integers();
test_float<float>();
test_float<double>();
test_float<long double>();
+#if TEST_STD_VER >= 26
+ static_assert(test_integers());
+#endif
return 0;
}
diff --git a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
index fff5ede848b57..22d06bffb84b8 100644
--- a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
@@ -28,7 +28,7 @@
#include "test_macros.h"
template <class T>
-void test_signed() {
+TEST_CONSTEXPR_CXX26 void test_signed() {
{
std::wstring s = std::to_wstring(T(0));
assert(s.size() == 1);
@@ -61,7 +61,7 @@ void test_signed() {
}
template <class T>
-void test_unsigned() {
+TEST_CONSTEXPR_CXX26 void test_unsigned() {
{
std::wstring s = std::to_wstring(T(0));
assert(s.size() == 1);
@@ -104,16 +104,25 @@ void test_float() {
}
}
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test_integers() {
test_signed<int>();
test_signed<long>();
test_signed<long long>();
test_unsigned<unsigned>();
test_unsigned<unsigned long>();
test_unsigned<unsigned long long>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_integers();
test_float<float>();
test_float<double>();
test_float<long double>();
+#if TEST_STD_VER >= 26
+ static_assert(test_integers());
+#endif
return 0;
}
diff --git a/libcxx/test/support/parse_integer.h b/libcxx/test/support/parse_integer.h
index 36f8f7b79881b..90f5541d222ef 100644
--- a/libcxx/test/support/parse_integer.h
+++ b/libcxx/test/support/parse_integer.h
@@ -9,7 +9,12 @@
#ifndef LIBCPP_TEST_SUPPORT_PARSE_INTEGER_H
#define LIBCPP_TEST_SUPPORT_PARSE_INTEGER_H
+#include <charconv>
+#include <cstdlib>
#include <string>
+#include <system_error>
+
+#include "test_macros.h"
namespace detail {
template <class T>
@@ -17,56 +22,110 @@ struct parse_integer_impl;
template <>
struct parse_integer_impl<int> {
- template <class CharT>
- int operator()(std::basic_string<CharT> const& str) const {
- return std::stoi(str);
+ template <class CharT>
+ TEST_CONSTEXPR_CXX23 int operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+ if consteval {
+ int n;
+ auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), n);
+ if (ec != std::errc{})
+ std::abort();
+ return n;
}
+#endif
+ return std::stoi(str);
+ }
};
template <>
struct parse_integer_impl<long> {
- template <class CharT>
- long operator()(std::basic_string<CharT> const& str) const {
- return std::stol(str);
+ template <class CharT>
+ TEST_CONSTEXPR_CXX23 long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+ if consteval {
+ long n;
+ auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), n);
+ if (ec != std::errc{})
+ std::abort();
+ return n;
}
+#endif
+ return std::stol(str);
+ }
};
template <>
struct parse_integer_impl<long long> {
- template <class CharT>
- long long operator()(std::basic_string<CharT> const& str) const {
- return std::stoll(str);
+ template <class CharT>
+ TEST_CONSTEXPR_CXX23 long long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+ if consteval {
+ long long n;
+ auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), n);
+ if (ec != std::errc{})
+ std::abort();
+ return n;
}
+#endif
+ return std::stoll(str);
+ }
};
template <>
struct parse_integer_impl<unsigned int> {
- template <class CharT>
- unsigned int operator()(std::basic_string<CharT> const& str) const {
- return std::stoul(str);
+ template <class CharT>
+ TEST_CONSTEXPR_CXX23 unsigned int operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+ if consteval {
+ unsigned int n;
+ auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), n);
+ if (ec != std::errc{})
+ std::abort();
+ return n;
}
+#endif
+ return std::stoul(str);
+ }
};
template <>
struct parse_integer_impl<unsigned long> {
- template <class CharT>
- unsigned long operator()(std::basic_string<CharT> const& str) const {
- return std::stoul(str);
+ template <class CharT>
+ TEST_CONSTEXPR_CXX23 unsigned long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+ if consteval {
+ unsigned long n;
+ auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), n);
+ if (ec != std::errc{})
+ std::abort();
+ return n;
}
+#endif
+ return std::stoul(str);
+ }
};
template <>
struct parse_integer_impl<unsigned long long> {
- template <class CharT>
- unsigned long long operator()(std::basic_string<CharT> const& str) const {
- return std::stoull(str);
+ template <class CharT>
+ TEST_CONSTEXPR_CXX23 unsigned long long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+ if consteval {
+ unsigned long long n;
+ auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), n);
+ if (ec != std::errc{})
+ std::abort();
+ return n;
}
+#endif
+ return std::stoull(str);
+ }
};
} // namespace detail
template <class T, class CharT>
-T parse_integer(std::basic_string<CharT> const& str) {
- return detail::parse_integer_impl<T>()(str);
+TEST_CONSTEXPR_CXX23 T parse_integer(std::basic_string<CharT> const& str) {
+ return detail::parse_integer_impl<T>()(str);
}
#endif // LIBCPP_TEST_SUPPORT_PARSE_INTEGER_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6..1f4961fda4d6f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -434,7 +434,7 @@ def add_version_header(tc):
},
{
"name": "__cpp_lib_constexpr_string",
- "values": {"c++20": 201907},
+ "values": {"c++20": 201907, "c++26": 202511},
"headers": ["string"],
},
{
More information about the libcxx-commits
mailing list