[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
Tue Jun 23 19:54:34 PDT 2026


================
@@ -3798,17 +3804,121 @@ 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 || defined(_LIBCPP_PREFERRED_OVERLOAD) || defined(_LIBCPP_BUILDING_LIBRARY)
 [[__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 || defined(_LIBCPP_PREFERRED_OVERLOAD) || defined(_LIBCPP_BUILDING_LIBRARY)
+
 [[__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);
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
+#  if _LIBCPP_STD_VER >= 26
+
+template <class _String, class _Integer>
+_LIBCPP_HIDE_FROM_ABI 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 accommodate the value");
+  return _String(__buf, __ptr);
+}
+
+template <class _CharT, class _Integer>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT> __to_basic_string(_Integer __val) {
+#    ifdef _LIBCPP_PREFERRED_OVERLOAD
+  if !consteval {
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    if constexpr (is_same_v<_CharT, wchar_t>) {
+      return std::to_wstring(__val);
+    } else
+#      endif // _LIBCPP_HAS_WIDE_CHARACTERS
+    {
+      return std::to_string(__val);
+    }
+  } else
+#    endif // _LIBCPP_PREFERRED_OVERLOAD
+  {
+    return std::__integer_to_string<basic_string<_CharT>>(__val);
+  }
+}
+
+#    if !defined(_LIBCPP_BUILDING_LIBRARY)
+
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+#        define _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE _LIBCPP_PREFERRED_OVERLOAD
----------------
frederick-vs-ja wrote:

> > 2\. Drop the inline namespace. Declare `to_string` in namespace `std` as usual, but make it `__gnu_inline__`. Have it call a helper function that implements the actual logic, like this patch. Then, inside the dylib, keep providing `std::to_string` like we do today.
> 
> That sounds like an idea we should definitely explore. We'd probably want to mark the function as `[[gnu::noinline, gnu::gnu_inline]]` to avoid unnecessary CodeGen (and be explicit that we don't want to have this inlined). I completely forgot that that attribute exists.

I'm trying this approach now. I'm unsure whether we should use `[[gnu::noinline]]`, though, as when the argument is a small constant (e.g. 42), the codegen is arguably cleaner when inlined.

https://github.com/llvm/llvm-project/pull/205025


More information about the libcxx-commits mailing list