[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