[gcc r16-8700] libstdc++: Export explicit instantiations of __format::__do_vformat_to.

Tomasz Kaminski tkaminsk@gcc.gnu.org
Thu Apr 16 10:09:29 GMT 2026


https://gcc.gnu.org/g:30c37984e87095b2f99bc6391c9d3da7585d6e32

commit r16-8700-g30c37984e87095b2f99bc6391c9d3da7585d6e32
Author: Tomasz Kamiński <tkaminsk@redhat.com>
Date:   Fri Apr 3 13:39:20 2026 +0200

    libstdc++: Export explicit instantiations of __format::__do_vformat_to.
    
    This patch extracts a __format::__do_vformat_to for the _Sink_iter
    (and matching format context) and exports explicit instantiations
    of it for char and wchar_t. As every format function is implementing
    as delegating to one of these overloads, this significantly reduces
    the compilation time.
    
    Instantiating __format::__do_vformat_to triggers specializations of
    formatters for types stored in basic_format_arg directly (arithmetic
    types, strings). In case when their behavior depends on the TU specific
    configuration, only one configuration can be exported from shared
    lib. In case of beforementioned formatters:
    * ? (debug mode) is accepted in for strings and characters
    * multibyte utf-8 encoded character is accepted as fill when the
      literal encoding is Unicode.
    
    The first issue is addressed by this patch, by declaring extern
    definition only for the C++20. We will need to reconsider how to
    handle the specifiers when C++23 becomes stable.
    
    The literal encoding is handled by adding a second template parameter
    to __do_vformat_to overload, that is initialized with 1 if literal
    encoding is Unicode (the parameter has unsigned type to allow more
    information to be encoded). This allows library to export implementation
    for Unicode literal encoding (format-inst.cc is compiled with appropriate
    flag), by declaring extern specialization only for value 1.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver (GLIBCXX_3.4): Exclude exports
            of std::basic_fo* (matching basic_format_context).
            (GLIBCXX_3.4.35): Export __format::__do_vformat_to
            specializations for _Sink_iter and char/wchar_t.
            * include/std/format: (__format::__do_vformat_to):
            Extract overload accepting _Sink_iter and provide extern
            explicit specialization for char/wchar_t in C++20 mode.
            * src/c++20/Makefile.am: Add format-inst.cc.
            * src/c++20/Makefile.in: Regenerate.
            * src/c++20/format-inst.cc: New file defining explicit
            instantiation.
    
    Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
    Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver   |   8 +-
 libstdc++-v3/include/std/format       | 154 ++++++++++++++++++++--------------
 libstdc++-v3/src/c++20/Makefile.am    |  10 ++-
 libstdc++-v3/src/c++20/Makefile.in    |  10 ++-
 libstdc++-v3/src/c++20/format-inst.cc |  45 ++++++++++
 5 files changed, 159 insertions(+), 68 deletions(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 624de951d415..71b3c175bbdf 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -34,7 +34,9 @@ GLIBCXX_3.4 {
       std::basic_[a-e]*;
       std::basic_f[a-h]*;
 #     std::basic_filebuf;
-      std::basic_f[j-r]*;
+      std::basic_f[j-n]*;
+#     std::basic_format_context;
+      std::basic_f[p-r]*;
 #     std::basic_fstream;
       std::basic_f[t-z]*;
       std::basic_[g-h]*;
@@ -2606,6 +2608,10 @@ GLIBCXX_3.4.35 {
     _ZNSbIwSt11char_traitsIwESaIwEEC[12]EvQ26is_default_constructible_vIT1_E;
     _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[1-2]EvQ26is_default_constructible_vIT1_E;
 
+    # __format::__do_vformat_to<char, 1>(_Sink_iter<char>, string_view, format_context&);
+    # __format::__do_vformat_to<wchar_t, 1>(_Sink_iter<wchar_t>, wstring_view, format_context&);
+    _ZNSt8__format15__do_vformat_toI[cw]Lj1EEENS_10_Sink_iterIT_EES3_St17basic_string_viewIS2_St11char_traitsIS2_EERSt20basic_format_contextIS3_S2_E;
+
 #if defined (_WIN32) && !defined (__CYGWIN__)
     _ZSt19__get_once_callablev;
     _ZSt15__get_once_callv;
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index eca5bd213aa0..92add1e0a01c 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -5219,82 +5219,106 @@ namespace __format
 #endif
     };
 
-  template<typename _Out, typename _CharT, typename _Context>
-    inline _Out
-    __do_vformat_to(_Out __out, basic_string_view<_CharT> __fmt,
-		    const basic_format_args<_Context>& __args,
-		    const locale* __loc)
+  template<typename _CharT, unsigned = __unicode::__literal_encoding_is_unicode<_CharT>()>
+    _Sink_iter<_CharT>
+    __do_vformat_to(_Sink_iter<_CharT> __out, basic_string_view<_CharT> __fmt,
+		    __format_context<_CharT>& __ctx)
     {
-      if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
-	{
-	  if constexpr (is_same_v<_CharT, char>)
-	    // Fast path for "{}" format strings and simple format arg types.
-	    if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
-	      {
-		bool __done = false;
-		__format::__visit_format_arg([&](auto& __arg) {
-		  using _Tp = remove_cvref_t<decltype(__arg)>;
-		  if constexpr (is_same_v<_Tp, bool>)
+      if constexpr (is_same_v<_CharT, char>)
+	// Fast path for "{}" format strings and simple format arg types.
+	if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
+	  {
+	    bool __done = false;
+	    __format::__visit_format_arg([&](auto& __arg) {
+	      using _Tp = remove_cvref_t<decltype(__arg)>;
+	      if constexpr (is_same_v<_Tp, bool>)
+		{
+		  size_t __len = 4 + !__arg;
+		  const char* __chars[] = { "false", "true" };
+		  if (auto __res = __out._M_reserve(__len))
 		    {
-		      size_t __len = 4 + !__arg;
-		      const char* __chars[] = { "false", "true" };
-		      if (auto __res = __out._M_reserve(__len))
-			{
-			  __builtin_memcpy(__res.get(), __chars[__arg], __len);
-			  __res._M_bump(__len);
-			  __done = true;
-			}
+		      __builtin_memcpy(__res.get(), __chars[__arg], __len);
+		      __res._M_bump(__len);
+		      __done = true;
 		    }
-		  else if constexpr (is_same_v<_Tp, char>)
+		}
+	      else if constexpr (is_same_v<_Tp, char>)
+		{
+		  if (auto __res = __out._M_reserve(1))
 		    {
-		      if (auto __res = __out._M_reserve(1))
-			{
-			  *__res.get() = __arg;
-			  __res._M_bump(1);
-			  __done = true;
-			}
+		      *__res.get() = __arg;
+		      __res._M_bump(1);
+		      __done = true;
 		    }
-		  else if constexpr (is_integral_v<_Tp>)
+		}
+	      else if constexpr (is_integral_v<_Tp>)
+		{
+		  make_unsigned_t<_Tp> __uval;
+		  const bool __neg = __arg < 0;
+		  if (__neg)
+		    __uval = make_unsigned_t<_Tp>(~__arg) + 1u;
+		  else
+		    __uval = __arg;
+		  const auto __n = __detail::__to_chars_len(__uval);
+		  if (auto __res = __out._M_reserve(__n + __neg))
 		    {
-		      make_unsigned_t<_Tp> __uval;
-		      const bool __neg = __arg < 0;
-		      if (__neg)
-			__uval = make_unsigned_t<_Tp>(~__arg) + 1u;
-		      else
-			__uval = __arg;
-		      const auto __n = __detail::__to_chars_len(__uval);
-		      if (auto __res = __out._M_reserve(__n + __neg))
-			{
-			  auto __ptr = __res.get();
-			  *__ptr = '-';
-			  __detail::__to_chars_10_impl(__ptr + (int)__neg, __n,
-						       __uval);
-			  __res._M_bump(__n + __neg);
-			  __done = true;
-			}
+		      auto __ptr = __res.get();
+		      *__ptr = '-';
+		      __detail::__to_chars_10_impl(__ptr + (int)__neg, __n,
+						   __uval);
+		      __res._M_bump(__n + __neg);
+		      __done = true;
 		    }
-		  else if constexpr (is_convertible_v<_Tp, string_view>)
+		}
+	      else if constexpr (is_convertible_v<_Tp, string_view>)
+		{
+		  string_view __sv = __arg;
+		  if (auto __res = __out._M_reserve(__sv.size()))
 		    {
-		      string_view __sv = __arg;
-		      if (auto __res = __out._M_reserve(__sv.size()))
-			{
-			  __builtin_memcpy(__res.get(), __sv.data(), __sv.size());
-			  __res._M_bump(__sv.size());
-			  __done = true;
-			}
+		      __builtin_memcpy(__res.get(), __sv.data(), __sv.size());
+		      __res._M_bump(__sv.size());
+		      __done = true;
 		    }
-		}, __args.get(0));
+		}
+	    }, __ctx.arg(0));
 
-		if (__done)
-		  return __out;
-	      }
+	    if (__done)
+	      return __out;
+	  }
+
+      _Formatting_scanner<_Sink_iter<_CharT>, _CharT> __scanner(__ctx, __fmt);
+      __scanner._M_scan();
+      return __out;
+    }
+
+// The behavior of the formatters (interpretation of fill character) depends
+// on the literal encoding. As explicit instantiation of __do_vformat_to
+// instantiates formatters for types stored in basic_format_arg, we can
+// support only single encoding, in this case unicode. This should cover
+// most common use cases.
+#if __cplusplus <= 202002L && _GLIBCXX_EXTERN_TEMPLATE > 0
+    extern template _Sink_iter<char>
+      __do_vformat_to<char, 1>(_Sink_iter<char>, string_view,
+			       format_context&);
+# ifdef _GLIBCXX_USE_WCHAR_T
+    extern template _Sink_iter<wchar_t>
+      __do_vformat_to<wchar_t, 1>(_Sink_iter<wchar_t>, wstring_view,
+				  wformat_context&);
+# endif
+#endif
 
+  template<typename _Out, typename _CharT, typename _Context>
+    inline _Out
+    __do_vformat_to(_Out __out, basic_string_view<_CharT> __fmt,
+		    const basic_format_args<_Context>& __args,
+		    const locale* __loc)
+    {
+      if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
+	{
 	  auto __ctx = __loc == nullptr
-			 ? _Context(__args, __out)
-			 : _Context(__args, __out, *__loc);
-	  _Formatting_scanner<_Sink_iter<_CharT>, _CharT> __scanner(__ctx, __fmt);
-	  __scanner._M_scan();
-	  return __out;
+		     ? _Context(__args, __out)
+		     : _Context(__args, __out, *__loc);
+	  return __format::__do_vformat_to(__out, __fmt, __ctx);
 	}
       else if constexpr (__contiguous_char_iter<_CharT, _Out>)
 	{
@@ -5311,7 +5335,7 @@ namespace __format
     }
 
   template<typename _Out, typename _CharT>
-    format_to_n_result<_Out>
+    inline format_to_n_result<_Out>
     __do_vformat_to_n(_Out __out, iter_difference_t<_Out> __n,
 		      basic_string_view<_CharT> __fmt,
 		      const type_identity_t<
diff --git a/libstdc++-v3/src/c++20/Makefile.am b/libstdc++-v3/src/c++20/Makefile.am
index af5d2fa4057b..5182d51b9faa 100644
--- a/libstdc++-v3/src/c++20/Makefile.am
+++ b/libstdc++-v3/src/c++20/Makefile.am
@@ -33,11 +33,11 @@ else
 extra_string_inst_sources =
 endif
 
-
 if ENABLE_EXTERN_TEMPLATE
 # XTEMPLATE_FLAGS = -fno-implicit-templates
 inst_sources = \
 	sstream-inst.cc \
+	format-inst.cc \
 	string-inst.cc \
 	$(extra_string_inst_sources)
 else
@@ -73,6 +73,14 @@ format.lo: format.cc
 format.o: format.cc
 	$(CXXCOMPILE) -std=gnu++26 -fno-access-control -c $<
 
+if ENABLE_EXTERN_TEMPLATE
+# The unicode literal version of formatters is exported, override the encoding accordingly
+format-inst.lo: format-inst.cc
+	$(LTCXXCOMPILE) -fexec-charset=UTF-8 -c $<
+format-inst.o: format-inst.cc
+	$(CXXCOMPILE) -fexec-charset=UTF-8 -c $<
+endif
+
 if GLIBCXX_HOSTED
 libc__20convenience_la_SOURCES = $(sources)  $(inst_sources)
 else
diff --git a/libstdc++-v3/src/c++20/Makefile.in b/libstdc++-v3/src/c++20/Makefile.in
index a2386ec7ee0c..33e3ba21b522 100644
--- a/libstdc++-v3/src/c++20/Makefile.in
+++ b/libstdc++-v3/src/c++20/Makefile.in
@@ -126,7 +126,8 @@ libc__20convenience_la_LIBADD =
 am__objects_1 = tzdb.lo format.lo atomic.lo clock.lo syncbuf.lo
 @ENABLE_DUAL_ABI_TRUE@am__objects_2 = cow-string-inst.lo
 @ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_3 = sstream-inst.lo \
-@ENABLE_EXTERN_TEMPLATE_TRUE@	string-inst.lo $(am__objects_2)
+@ENABLE_EXTERN_TEMPLATE_TRUE@	format-inst.lo string-inst.lo \
+@ENABLE_EXTERN_TEMPLATE_TRUE@	$(am__objects_2)
 @GLIBCXX_HOSTED_TRUE@am_libc__20convenience_la_OBJECTS =  \
 @GLIBCXX_HOSTED_TRUE@	$(am__objects_1) $(am__objects_3)
 libc__20convenience_la_OBJECTS = $(am_libc__20convenience_la_OBJECTS)
@@ -438,6 +439,7 @@ headers =
 # XTEMPLATE_FLAGS = -fno-implicit-templates
 @ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = \
 @ENABLE_EXTERN_TEMPLATE_TRUE@	sstream-inst.cc \
+@ENABLE_EXTERN_TEMPLATE_TRUE@	format-inst.cc \
 @ENABLE_EXTERN_TEMPLATE_TRUE@	string-inst.cc \
 @ENABLE_EXTERN_TEMPLATE_TRUE@	$(extra_string_inst_sources)
 
@@ -773,6 +775,12 @@ format.lo: format.cc
 format.o: format.cc
 	$(CXXCOMPILE) -std=gnu++26 -fno-access-control -c $<
 
+# The unicode literal version of formatters is exported, override the encoding accordingly
+@ENABLE_EXTERN_TEMPLATE_TRUE@format-inst.lo: format-inst.cc
+@ENABLE_EXTERN_TEMPLATE_TRUE@	$(LTCXXCOMPILE) -fexec-charset=UTF-8 -c $<
+@ENABLE_EXTERN_TEMPLATE_TRUE@format-inst.o: format-inst.cc
+@ENABLE_EXTERN_TEMPLATE_TRUE@	$(CXXCOMPILE) -fexec-charset=UTF-8 -c $<
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/libstdc++-v3/src/c++20/format-inst.cc b/libstdc++-v3/src/c++20/format-inst.cc
new file mode 100644
index 000000000000..65a3f2827327
--- /dev/null
+++ b/libstdc++-v3/src/c++20/format-inst.cc
@@ -0,0 +1,45 @@
+// Definitions for <format> -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <format>
+
+namespace std
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+namespace __format
+{
+
+ template _Sink_iter<char>
+   __do_vformat_to<char, 1>(_Sink_iter<char>, string_view,
+			    format_context&);
+
+# ifdef _GLIBCXX_USE_WCHAR_T
+ template _Sink_iter<wchar_t>
+   __do_vformat_to<wchar_t, 1>(_Sink_iter<wchar_t>, wstring_view,
+			       wformat_context&);
+# endif
+
+} // namespace __format
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std


More information about the Libstdc++-cvs mailing list