[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