[PATCH] libstdc++: Export explicit instantiations of __format::__do_vformat_to.
Tomasz Kaminski
tkaminsk@redhat.com
Fri Apr 3 15:31:07 GMT 2026
On Fri, Apr 3, 2026 at 5:21 PM Jonathan Wakely <jwakely.gcc@gmail.com>
wrote:
>
>
> On Fri, 3 Apr 2026, 12:50 Tomasz Kaminski, <tkaminsk@redhat.com> wrote:
>
>>
>>
>> On Fri, Apr 3, 2026 at 1:48 PM Tomasz Kamiński <tkaminsk@redhat.com>
>> wrote:
>>
>>> This patch exports instantiations of __format::__do_vformat_to
>>> for _Sink_iter and char/wchar_t. As every format function is
>>> implementing as delegating the one of these overloads, this
>>> significantly reduces the compliation time.
>>>
>>> Instantiating __format::__do_vformat_to triggers specializations
>>> of formatters for types stored in handle direclty (arithmetic
>>> types, strings), however we do not export any of their symbols.
>>> This keeps the interface boundary minimal. This file should
>>> be recompiled in later mode, when it is called stable.
>>>
>>> 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):
>>> Remove inline and declare extern instantiation
>>> of __format::__do_vformat_to _Sink_iter and char/wchar_t.
>>> * 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.
>>> ---
>>> From simple test on files from testsuite/std/format/functions/, the
>>> results looks very promising:
>>>
>>> Before After
>>> format.cc 24.203s 10.846s
>>> format_to.cc 18.475s 2.844s
>>> format_to_n.cc 18.677s 3.074s
>>> vformat_to.cc 17.281s 2.246s
>>>
>>> However, I am a bit concerned if the approach works, as
>>> libstdc++.so will always contains weak simbol for __do_vformat_to
>>> for _Sink_iter<char>, that supports only C++20 specifiers.
>>> Should we instead compile the format-inst in latest mode, and
>>> always provide extern defintion?
>>> Note that the same problem will also be present for the print function,
>>> but I haven not realized it then.
>>>
>> The scenarios like that are not only teoreticall, debug support (?) for
>> string and char was recently added.
>>
>
> Yes, I think we should instantiate the latest definition.
>
> Before C++26 there's no way to use those new format specifiers anyway,
> because the format string is always parsed as a consteval format_string and
> so only C++20 or C++23 formats will be recognized.
>
You can observe this by calling vformat_to, and we have a test checking if
vformat_to("{:?}", string) works or not,
so we will end up in unfortunae situation when ? is supported for dynamic
formatting, but not for static one.
I am experimenting with adding _M_ftm member to basic_format_context and
basic_format_parse_context,
and using it to decide if given format specifier should be rejected.
>
>
>
>>> I plan to do similar change for __formatter_chrono<char>::_M_format
>>> functions (entry point).
>>>
>>>
>>> libstdc++-v3/config/abi/pre/gnu.ver | 8 ++++-
>>> libstdc++-v3/include/std/format | 13 +++++++-
>>> libstdc++-v3/src/c++20/Makefile.am | 3 +-
>>> libstdc++-v3/src/c++20/Makefile.in | 6 ++--
>>> libstdc++-v3/src/c++20/format-inst.cc | 46 +++++++++++++++++++++++++++
>>> 5 files changed, 71 insertions(+), 5 deletions(-)
>>> create mode 100644 libstdc++-v3/src/c++20/format-inst.cc
>>>
>>> diff --git a/libstdc++-v3/config/abi/pre/gnu.ver
>>> b/libstdc++-v3/config/abi/pre/gnu.ver
>>> index fb968e122d8..2b391584c94 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]*;
>>> @@ -2587,6 +2589,10 @@ GLIBCXX_3.4.35 {
>>> _ZNSt12__cow_stringaSEOS_;
>>> _ZNKSt12__cow_string5c_strEv;
>>>
>>> + # __format::__do_vformat_to(_Sink_iter<char>, string_view, const
>>> format_args&, const locale*);
>>> + # __format::__do_vformat_to(_Sink_iter<wchar_t>, wstring_view,
>>> const wformat_args&, const locale*);
>>> +
>>> _ZNSt8__format15__do_vformat_toINS_10_Sink_iterI[wc]EE[wc]St20basic_format_contextIS2_[wc]EEET_S5_St17basic_string_viewIT0_St11char_traitsIS7_EERKSt17basic_format_argsIT1_EPKSt6locale;
>>> +
>>> #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 eca5bd213aa..2a8229a2eb5 100644
>>> --- a/libstdc++-v3/include/std/format
>>> +++ b/libstdc++-v3/include/std/format
>>> @@ -5220,7 +5220,7 @@ namespace __format
>>> };
>>>
>>> template<typename _Out, typename _CharT, typename _Context>
>>> - inline _Out
>>> + _Out
>>> __do_vformat_to(_Out __out, basic_string_view<_CharT> __fmt,
>>> const basic_format_args<_Context>& __args,
>>> const locale* __loc)
>>> @@ -5310,6 +5310,17 @@ namespace __format
>>> }
>>> }
>>>
>>> +#if __cplusplus <= 202002L && _GLIBCXX_EXTERN_TEMPLATE > 0
>>> + extern template _Sink_iter<char>
>>> + __do_vformat_to(_Sink_iter<char>, string_view,
>>> + const format_args&, const locale*);
>>> +# ifdef _GLIBCXX_USE_WCHAR_T
>>> + extern template _Sink_iter<wchar_t>
>>> + __do_vformat_to(_Sink_iter<wchar_t>, wstring_view,
>>> + const wformat_args&, const locale*);
>>> +# endif
>>> +#endif
>>> +
>>> template<typename _Out, typename _CharT>
>>> format_to_n_result<_Out>
>>> __do_vformat_to_n(_Out __out, iter_difference_t<_Out> __n,
>>> diff --git a/libstdc++-v3/src/c++20/Makefile.am
>>> b/libstdc++-v3/src/c++20/Makefile.am
>>> index 0061678dc0f..7744fa43a63 100644
>>> --- a/libstdc++-v3/src/c++20/Makefile.am
>>> +++ b/libstdc++-v3/src/c++20/Makefile.am
>>> @@ -30,7 +30,8 @@ headers =
>>> if ENABLE_EXTERN_TEMPLATE
>>> # XTEMPLATE_FLAGS = -fno-implicit-templates
>>> inst_sources = \
>>> - sstream-inst.cc
>>> + sstream-inst.cc \
>>> + format-inst.cc
>>> else
>>> # XTEMPLATE_FLAGS =
>>> inst_sources =
>>> diff --git a/libstdc++-v3/src/c++20/Makefile.in
>>> b/libstdc++-v3/src/c++20/Makefile.in
>>> index f481ad08edb..566d7292021 100644
>>> --- a/libstdc++-v3/src/c++20/Makefile.in
>>> +++ b/libstdc++-v3/src/c++20/Makefile.in
>>> @@ -124,7 +124,8 @@ CONFIG_CLEAN_VPATH_FILES =
>>> LTLIBRARIES = $(noinst_LTLIBRARIES)
>>> libc__20convenience_la_LIBADD =
>>> am__objects_1 = tzdb.lo format.lo atomic.lo clock.lo syncbuf.lo
>>> -@ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_2 = sstream-inst.lo
>>> +@ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_2 = sstream-inst.lo \
>>> +@ENABLE_EXTERN_TEMPLATE_TRUE@ format-inst.lo
>>> @GLIBCXX_HOSTED_TRUE@am_libc__20convenience_la_OBJECTS = \
>>> @GLIBCXX_HOSTED_TRUE@ $(am__objects_1) $(am__objects_2)
>>> libc__20convenience_la_OBJECTS = $(am_libc__20convenience_la_OBJECTS)
>>> @@ -433,7 +434,8 @@ headers =
>>>
>>> # XTEMPLATE_FLAGS = -fno-implicit-templates
>>> @ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = \
>>> -@ENABLE_EXTERN_TEMPLATE_TRUE@ sstream-inst.cc
>>> +@ENABLE_EXTERN_TEMPLATE_TRUE@ sstream-inst.cc \
>>> +@ENABLE_EXTERN_TEMPLATE_TRUE@ format-inst.cc
>>>
>>> sources = tzdb.cc format.cc atomic.cc clock.cc syncbuf.cc
>>> @GLIBCXX_HOSTED_FALSE@libc__20convenience_la_SOURCES =
>>> 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 00000000000..1904de35f7b
>>> --- /dev/null
>>> +++ b/libstdc++-v3/src/c++20/format-inst.cc
>>> @@ -0,0 +1,46 @@
>>> +// Definitions for <chrono> formatting -*- 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>
>>> +#include <chrono>
>>> +
>>> +namespace std
>>> +{
>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>> +namespace __format
>>> +{
>>> +
>>> + template _Sink_iter<char>
>>> + __do_vformat_to(_Sink_iter<char>, string_view,
>>> + const format_args&, const locale*);
>>> +
>>> +# ifdef _GLIBCXX_USE_WCHAR_T
>>> + template _Sink_iter<wchar_t>
>>> + __do_vformat_to(_Sink_iter<wchar_t>, wstring_view,
>>> + const wformat_args&, const locale*);
>>> +# endif
>>> +
>>> +} // namespace __format
>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>> +} // namespace std
>>> --
>>> 2.53.0
>>>
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260403/33f69c29/attachment-0001.htm>
More information about the Libstdc++
mailing list