gcc/libstdc++-v3/include/std/format
Jonathan Wakely 74b5101cc9
libstdc++: Handle encodings in localized chrono formatting [PR109162]
This implements the C++23 paper P2419R2 (Clarify handling of encodings
in localized formatting of chrono types). The requirement is that when
the literal encoding is "a Unicode encoding form" and the formatting
locale uses a different encoding, any locale-specific strings such as
"août" for std::chrono::August should be converted to the literal
encoding.

Using the recently-added std::locale::encoding() function we can check
the locale's encoding and then use iconv if a conversion is needed.
Because nl_langinfo_l and iconv_open both allocate memory, a naive
implementation would perform multiple allocations and deallocations for
every snippet of locale-specific text that needs to be converted to
UTF-8. To avoid that, a new internal locale::facet is defined to store
the text_encoding and an iconv_t descriptor, which are then cached in
the formatting locale. This requires access to the internals of a
std::locale object in src/c++20/format.cc, so that new file needs to be
compiled with -fno-access-control, as well as -std=gnu++26 in order to
use std::text_encoding.

Because the new std::text_encoding and std::locale::encoding() symbols
are only in the libstdc++exp.a archive, we need to include
src/c++26/text_encoding.cc in the main library, but not export its
symbols yet. This means they can be used by the two new functions which
are exported from the main library.

The encoding conversions are done for C++20, treating it as a DR that
resolves LWG 3656.

With this change we can increase the value of the __cpp_lib_format macro
for C++23. The value should be 202207 for P2419R2, but we already
implement P2510R3 (Formatting pointers) so can use the value 202304.

libstdc++-v3/ChangeLog:

	PR libstdc++/109162
	* acinclude.m4 (libtool_VERSION): Update to 6:34:0.
	* config/abi/pre/gnu.ver: Disambiguate old patters. Add new
	GLIBCXX_3.4.34 symbol version and new exports.
	* configure: Regenerate.
	* include/bits/chrono_io.h (_ChronoSpec::_M_locale_specific):
	Add new accessor functions to use a reserved bit in _Spec.
	(__formatter_chrono::_M_parse): Use _M_locale_specific(true)
	when chrono-specs contains locale-dependent conversion
	specifiers.
	(__formatter_chrono::_M_format): Open iconv descriptor if
	conversion to UTF-8 will be needed.
	(__formatter_chrono::_M_write): New function to write a
	localized string with possible character conversion.
	(__formatter_chrono::_M_a_A, __formatter_chrono::_M_b_B)
	(__formatter_chrono::_M_p, __formatter_chrono::_M_r)
	(__formatter_chrono::_M_x, __formatter_chrono::_M_X)
	(__formatter_chrono::_M_locale_fmt): Use _M_write.
	* include/bits/version.def (format): Update value.
	* include/bits/version.h: Regenerate.
	* include/std/format (_GLIBCXX_P2518R3): Check feature test
	macro instead of __cplusplus.
	(basic_format_context): Declare __formatter_chrono as friend.
	* src/c++20/Makefile.am: Add new file.
	* src/c++20/Makefile.in: Regenerate.
	* src/c++20/format.cc: New file.
	* testsuite/std/time/format_localized.cc: New test.
	* testsuite/util/testsuite_abi.cc: Add new symbol version.
2024-07-31 17:07:10 +01:00

4596 lines
128 KiB
C++

// <format> 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/>.
/** @file include/format
* This is a Standard C++ Library header.
*/
#ifndef _GLIBCXX_FORMAT
#define _GLIBCXX_FORMAT 1
#pragma GCC system_header
#include <bits/requires_hosted.h> // for std::string
#define __glibcxx_want_format
#define __glibcxx_want_format_ranges
#define __glibcxx_want_format_uchar
#include <bits/version.h>
#ifdef __cpp_lib_format // C++ >= 20 && HOSTED
#include <array>
#include <charconv>
#include <concepts>
#include <limits>
#include <locale>
#include <optional>
#include <span>
#include <string_view>
#include <string>
#include <variant> // monostate (TODO: move to bits/utility.h?)
#include <bits/ranges_base.h> // input_range, range_reference_t
#include <bits/ranges_util.h> // subrange
#include <bits/ranges_algobase.h> // ranges::copy
#include <bits/stl_iterator.h> // back_insert_iterator
#include <bits/stl_pair.h> // __is_pair
#include <bits/unicode.h> // __is_scalar_value, _Utf_view, etc.
#include <bits/utility.h> // tuple_size_v
#include <ext/numeric_traits.h> // __int_traits
#if !__has_builtin(__builtin_toupper)
# include <cctype>
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
// [format.context], class template basic_format_context
template<typename _Out, typename _CharT> class basic_format_context;
// [format.fmt.string], class template basic_format_string
template<typename _CharT, typename... _Args> struct basic_format_string;
/// @cond undocumented
namespace __format
{
// Type-erased character sink.
template<typename _CharT> class _Sink;
// Output iterator that writes to a type-erase character sink.
template<typename _CharT>
class _Sink_iter;
template<typename _CharT>
using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>;
template<typename _CharT>
struct _Runtime_format_string
{
[[__gnu__::__always_inline__]]
_Runtime_format_string(basic_string_view<_CharT> __s) noexcept
: _M_str(__s) { }
_Runtime_format_string(const _Runtime_format_string&) = delete;
void operator=(const _Runtime_format_string&) = delete;
private:
basic_string_view<_CharT> _M_str;
template<typename, typename...> friend struct std::basic_format_string;
};
} // namespace __format
/// @endcond
using format_context = __format::__format_context<char>;
#ifdef _GLIBCXX_USE_WCHAR_T
using wformat_context = __format::__format_context<wchar_t>;
#endif
// [format.args], class template basic_format_args
template<typename _Context> class basic_format_args;
using format_args = basic_format_args<format_context>;
#ifdef _GLIBCXX_USE_WCHAR_T
using wformat_args = basic_format_args<wformat_context>;
#endif
// [format.arguments], arguments
// [format.arg], class template basic_format_arg
template<typename _Context>
class basic_format_arg;
/** A compile-time checked format string for the specified argument types.
*
* @since C++23 but available as an extension in C++20.
*/
template<typename _CharT, typename... _Args>
struct basic_format_string
{
template<typename _Tp>
requires convertible_to<const _Tp&, basic_string_view<_CharT>>
consteval
basic_format_string(const _Tp& __s);
[[__gnu__::__always_inline__]]
basic_format_string(__format::_Runtime_format_string<_CharT> __s) noexcept
: _M_str(__s._M_str)
{ }
[[__gnu__::__always_inline__]]
constexpr basic_string_view<_CharT>
get() const noexcept
{ return _M_str; }
private:
basic_string_view<_CharT> _M_str;
};
template<typename... _Args>
using format_string = basic_format_string<char, type_identity_t<_Args>...>;
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename... _Args>
using wformat_string
= basic_format_string<wchar_t, type_identity_t<_Args>...>;
#endif
#if __cplusplus > 202302L
[[__gnu__::__always_inline__]]
inline __format::_Runtime_format_string<char>
runtime_format(string_view __fmt) noexcept
{ return __fmt; }
#ifdef _GLIBCXX_USE_WCHAR_T
[[__gnu__::__always_inline__]]
inline __format::_Runtime_format_string<wchar_t>
runtime_format(wstring_view __fmt) noexcept
{ return __fmt; }
#endif
#endif // C++26
// [format.formatter], formatter
/// The primary template of std::formatter is disabled.
template<typename _Tp, typename _CharT = char>
struct formatter
{
formatter() = delete; // No std::formatter specialization for this type.
formatter(const formatter&) = delete;
formatter& operator=(const formatter&) = delete;
};
// [format.error], class format_error
class format_error : public runtime_error
{
public:
explicit format_error(const string& __what) : runtime_error(__what) { }
explicit format_error(const char* __what) : runtime_error(__what) { }
};
/// @cond undocumented
[[noreturn]]
inline void
__throw_format_error(const char* __what)
{ _GLIBCXX_THROW_OR_ABORT(format_error(__what)); }
namespace __format
{
// XXX use named functions for each constexpr error?
[[noreturn]]
inline void
__unmatched_left_brace_in_format_string()
{ __throw_format_error("format error: unmatched '{' in format string"); }
[[noreturn]]
inline void
__unmatched_right_brace_in_format_string()
{ __throw_format_error("format error: unmatched '}' in format string"); }
[[noreturn]]
inline void
__conflicting_indexing_in_format_string()
{ __throw_format_error("format error: conflicting indexing style in format string"); }
[[noreturn]]
inline void
__invalid_arg_id_in_format_string()
{ __throw_format_error("format error: invalid arg-id in format string"); }
[[noreturn]]
inline void
__failed_to_parse_format_spec()
{ __throw_format_error("format error: failed to parse format-spec"); }
} // namespace __format
/// @endcond
// [format.parse.ctx], class template basic_format_parse_context
template<typename _CharT> class basic_format_parse_context;
using format_parse_context = basic_format_parse_context<char>;
#ifdef _GLIBCXX_USE_WCHAR_T
using wformat_parse_context = basic_format_parse_context<wchar_t>;
#endif
template<typename _CharT>
class basic_format_parse_context
{
public:
using char_type = _CharT;
using const_iterator = typename basic_string_view<_CharT>::const_iterator;
using iterator = const_iterator;
constexpr explicit
basic_format_parse_context(basic_string_view<_CharT> __fmt,
size_t __num_args = 0) noexcept
: _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args)
{ }
basic_format_parse_context(const basic_format_parse_context&) = delete;
void operator=(const basic_format_parse_context&) = delete;
constexpr const_iterator begin() const noexcept { return _M_begin; }
constexpr const_iterator end() const noexcept { return _M_end; }
constexpr void
advance_to(const_iterator __it) noexcept
{ _M_begin = __it; }
constexpr size_t
next_arg_id()
{
if (_M_indexing == _Manual)
__format::__conflicting_indexing_in_format_string();
_M_indexing = _Auto;
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3825. Missing compile-time argument id check in next_arg_id
if (std::is_constant_evaluated())
if (_M_next_arg_id == _M_num_args)
__format::__invalid_arg_id_in_format_string();
return _M_next_arg_id++;
}
constexpr void
check_arg_id(size_t __id)
{
if (_M_indexing == _Auto)
__format::__conflicting_indexing_in_format_string();
_M_indexing = _Manual;
if (std::is_constant_evaluated())
if (__id >= _M_num_args)
__format::__invalid_arg_id_in_format_string();
}
private:
iterator _M_begin;
iterator _M_end;
enum _Indexing { _Unknown, _Manual, _Auto };
_Indexing _M_indexing = _Unknown;
size_t _M_next_arg_id = 0;
size_t _M_num_args;
};
/// @cond undocumented
template<typename _Tp, template<typename...> class _Class>
static constexpr bool __is_specialization_of = false;
template<template<typename...> class _Class, typename... _Args>
static constexpr bool __is_specialization_of<_Class<_Args...>, _Class>
= true;
namespace __format
{
// pre: first != last
template<typename _CharT>
constexpr pair<unsigned short, const _CharT*>
__parse_integer(const _CharT* __first, const _CharT* __last)
{
if (__first == __last)
__builtin_unreachable();
if constexpr (is_same_v<_CharT, char>)
{
const auto __start = __first;
unsigned short __val = 0;
// N.B. std::from_chars is not constexpr in C++20.
if (__detail::__from_chars_alnum<true>(__first, __last, __val, 10)
&& __first != __start) [[likely]]
return {__val, __first};
}
else
{
constexpr int __n = 32;
char __buf[__n]{};
for (int __i = 0; __i < __n && (__first + __i) != __last; ++__i)
__buf[__i] = __first[__i];
auto [__v, __ptr] = __format::__parse_integer(__buf, __buf + __n);
if (__ptr) [[likely]]
return {__v, __first + (__ptr - __buf)};
}
return {0, nullptr};
}
template<typename _CharT>
constexpr pair<unsigned short, const _CharT*>
__parse_arg_id(const _CharT* __first, const _CharT* __last)
{
if (__first == __last)
__builtin_unreachable();
if (*__first == '0')
return {0, __first + 1}; // No leading zeros allowed, so '0...' == 0
if ('1' <= *__first && *__first <= '9')
{
const unsigned short __id = *__first - '0';
const auto __next = __first + 1;
// Optimize for most likely case of single digit arg-id.
if (__next == __last || !('0' <= *__next && *__next <= '9'))
return {__id, __next};
else
return __format::__parse_integer(__first, __last);
}
return {0, nullptr};
}
enum _Pres_type {
_Pres_none = 0, // Default type (not valid for integer presentation types).
// Presentation types for integral types (including bool and charT).
_Pres_d = 1, _Pres_b, _Pres_B, _Pres_o, _Pres_x, _Pres_X, _Pres_c,
// Presentation types for floating-point types.
_Pres_a = 1, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F, _Pres_g, _Pres_G,
_Pres_p = 0, _Pres_P, // For pointers.
_Pres_s = 0, // For strings and bool.
_Pres_esc = 0xf, // For strings and charT.
};
enum _Align {
_Align_default,
_Align_left,
_Align_right,
_Align_centre,
};
enum _Sign {
_Sign_default,
_Sign_plus,
_Sign_minus, // XXX does this need to be distinct from _Sign_default?
_Sign_space,
};
enum _WidthPrec {
_WP_none, // No width/prec specified.
_WP_value, // Fixed width/prec specified.
_WP_from_arg // Use a formatting argument for width/prec.
};
template<typename _Context>
size_t
__int_from_arg(const basic_format_arg<_Context>& __arg);
constexpr bool __is_digit(char __c)
{ return std::__detail::__from_chars_alnum_to_val(__c) < 10; }
constexpr bool __is_xdigit(char __c)
{ return std::__detail::__from_chars_alnum_to_val(__c) < 16; }
template<typename _CharT>
struct _Spec
{
_Align _M_align : 2;
_Sign _M_sign : 2;
unsigned _M_alt : 1;
unsigned _M_localized : 1;
unsigned _M_zero_fill : 1;
_WidthPrec _M_width_kind : 2;
_WidthPrec _M_prec_kind : 2;
_Pres_type _M_type : 4;
unsigned _M_reserved : 1;
unsigned _M_reserved2 : 16;
unsigned short _M_width;
unsigned short _M_prec;
char32_t _M_fill = ' ';
using iterator = typename basic_string_view<_CharT>::iterator;
static constexpr _Align
_S_align(_CharT __c) noexcept
{
switch (__c)
{
case '<': return _Align_left;
case '>': return _Align_right;
case '^': return _Align_centre;
default: return _Align_default;
}
}
// pre: __first != __last
constexpr iterator
_M_parse_fill_and_align(iterator __first, iterator __last) noexcept
{
if (*__first != '{')
{
using namespace __unicode;
if constexpr (__literal_encoding_is_unicode<_CharT>())
{
// Accept any UCS scalar value as fill character.
_Utf32_view<ranges::subrange<iterator>> __uv({__first, __last});
if (!__uv.empty())
{
auto __beg = __uv.begin();
char32_t __c = *__beg++;
if (__is_scalar_value(__c))
if (auto __next = __beg.base(); __next != __last)
if (_Align __align = _S_align(*__next))
{
_M_fill = __c;
_M_align = __align;
return ++__next;
}
}
}
else if (__last - __first >= 2)
if (_Align __align = _S_align(__first[1]))
{
_M_fill = *__first;
_M_align = __align;
return __first + 2;
}
if (_Align __align = _S_align(__first[0]))
{
_M_fill = ' ';
_M_align = __align;
return __first + 1;
}
}
return __first;
}
static constexpr _Sign
_S_sign(_CharT __c) noexcept
{
switch (__c)
{
case '+': return _Sign_plus;
case '-': return _Sign_minus;
case ' ': return _Sign_space;
default: return _Sign_default;
}
}
// pre: __first != __last
constexpr iterator
_M_parse_sign(iterator __first, iterator) noexcept
{
if (_Sign __sign = _S_sign(*__first))
{
_M_sign = __sign;
return __first + 1;
}
return __first;
}
// pre: *__first is valid
constexpr iterator
_M_parse_alternate_form(iterator __first, iterator) noexcept
{
if (*__first == '#')
{
_M_alt = true;
++__first;
}
return __first;
}
// pre: __first != __last
constexpr iterator
_M_parse_zero_fill(iterator __first, iterator /* __last */) noexcept
{
if (*__first == '0')
{
_M_zero_fill = true;
++__first;
}
return __first;
}
// pre: __first != __last
static constexpr iterator
_S_parse_width_or_precision(iterator __first, iterator __last,
unsigned short& __val, bool& __arg_id,
basic_format_parse_context<_CharT>& __pc)
{
if (__format::__is_digit(*__first))
{
auto [__v, __ptr] = __format::__parse_integer(__first, __last);
if (!__ptr)
__throw_format_error("format error: invalid width or precision "
"in format-spec");
__first = __ptr;
__val = __v;
}
else if (*__first == '{')
{
__arg_id = true;
++__first;
if (__first == __last)
__format::__unmatched_left_brace_in_format_string();
if (*__first == '}')
__val = __pc.next_arg_id();
else
{
auto [__v, __ptr] = __format::__parse_arg_id(__first, __last);
if (__ptr == nullptr || __ptr == __last || *__ptr != '}')
__format::__invalid_arg_id_in_format_string();
__first = __ptr;
__pc.check_arg_id(__v);
__val = __v;
}
++__first; // past the '}'
}
return __first;
}
// pre: __first != __last
constexpr iterator
_M_parse_width(iterator __first, iterator __last,
basic_format_parse_context<_CharT>& __pc)
{
bool __arg_id = false;
if (*__first == '0')
__throw_format_error("format error: width must be non-zero in "
"format string");
auto __next = _S_parse_width_or_precision(__first, __last, _M_width,
__arg_id, __pc);
if (__next != __first)
_M_width_kind = __arg_id ? _WP_from_arg : _WP_value;
return __next;
}
// pre: __first != __last
constexpr iterator
_M_parse_precision(iterator __first, iterator __last,
basic_format_parse_context<_CharT>& __pc)
{
if (__first[0] != '.')
return __first;
iterator __next = ++__first;
bool __arg_id = false;
if (__next != __last)
__next = _S_parse_width_or_precision(__first, __last, _M_prec,
__arg_id, __pc);
if (__next == __first)
__throw_format_error("format error: missing precision after '.' in "
"format string");
_M_prec_kind = __arg_id ? _WP_from_arg : _WP_value;
return __next;
}
// pre: __first != __last
constexpr iterator
_M_parse_locale(iterator __first, iterator /* __last */) noexcept
{
if (*__first == 'L')
{
_M_localized = true;
++__first;
}
return __first;
}
template<typename _Context>
size_t
_M_get_width(_Context& __ctx) const
{
size_t __width = 0;
if (_M_width_kind == _WP_value)
__width = _M_width;
else if (_M_width_kind == _WP_from_arg)
__width = __format::__int_from_arg(__ctx.arg(_M_width));
return __width;
}
template<typename _Context>
size_t
_M_get_precision(_Context& __ctx) const
{
size_t __prec = -1;
if (_M_prec_kind == _WP_value)
__prec = _M_prec;
else if (_M_prec_kind == _WP_from_arg)
__prec = __format::__int_from_arg(__ctx.arg(_M_prec));
return __prec;
}
};
template<typename _Int>
inline char*
__put_sign(_Int __i, _Sign __sign, char* __dest) noexcept
{
if (__i < 0)
*__dest = '-';
else if (__sign == _Sign_plus)
*__dest = '+';
else if (__sign == _Sign_space)
*__dest = ' ';
else
++__dest;
return __dest;
}
// Write STR to OUT (and do so efficiently if OUT is a _Sink_iter).
template<typename _Out, typename _CharT>
requires output_iterator<_Out, const _CharT&>
inline _Out
__write(_Out __out, basic_string_view<_CharT> __str)
{
if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
{
if (__str.size())
__out = __str;
}
else
for (_CharT __c : __str)
*__out++ = __c;
return __out;
}
// Write STR to OUT with NFILL copies of FILL_CHAR specified by ALIGN.
// pre: __align != _Align_default
template<typename _Out, typename _CharT>
_Out
__write_padded(_Out __out, basic_string_view<_CharT> __str,
_Align __align, size_t __nfill, char32_t __fill_char)
{
const size_t __buflen = 0x20;
_CharT __padding_chars[__buflen];
__padding_chars[0] = _CharT();
basic_string_view<_CharT> __padding{__padding_chars, __buflen};
auto __pad = [&__padding] (size_t __n, _Out& __o) {
if (__n == 0)
return;
while (__n > __padding.size())
{
__o = __format::__write(std::move(__o), __padding);
__n -= __padding.size();
}
if (__n != 0)
__o = __format::__write(std::move(__o), __padding.substr(0, __n));
};
size_t __l, __r, __max;
if (__align == _Align_centre)
{
__l = __nfill / 2;
__r = __l + (__nfill & 1);
__max = __r;
}
else if (__align == _Align_right)
{
__l = __nfill;
__r = 0;
__max = __l;
}
else
{
__l = 0;
__r = __nfill;
__max = __r;
}
using namespace __unicode;
if constexpr (__literal_encoding_is_unicode<_CharT>())
if (!__is_single_code_unit<_CharT>(__fill_char)) [[unlikely]]
{
// Encode fill char as multiple code units of type _CharT.
const char32_t __arr[1]{ __fill_char };
_Utf_view<_CharT, const char32_t(&)[1]> __v(__arr);
basic_string<_CharT> __padstr(__v.begin(), __v.end());
__padding = __padstr;
while (__l-- > 0)
__out = __format::__write(std::move(__out), __padding);
__out = __format::__write(std::move(__out), __str);
while (__r-- > 0)
__out = __format::__write(std::move(__out), __padding);
return __out;
}
if (__max < __buflen)
__padding.remove_suffix(__buflen - __max);
else
__max = __buflen;
char_traits<_CharT>::assign(__padding_chars, __max, __fill_char);
__pad(__l, __out);
__out = __format::__write(std::move(__out), __str);
__pad(__r, __out);
return __out;
}
// Write STR to OUT, with alignment and padding as determined by SPEC.
// pre: __spec._M_align != _Align_default || __align != _Align_default
template<typename _CharT, typename _Out>
_Out
__write_padded_as_spec(basic_string_view<type_identity_t<_CharT>> __str,
size_t __estimated_width,
basic_format_context<_Out, _CharT>& __fc,
const _Spec<_CharT>& __spec,
_Align __align = _Align_left)
{
size_t __width = __spec._M_get_width(__fc);
if (__width <= __estimated_width)
return __format::__write(__fc.out(), __str);
const size_t __nfill = __width - __estimated_width;
if (__spec._M_align)
__align = __spec._M_align;
return __format::__write_padded(__fc.out(), __str, __align, __nfill,
__spec._M_fill);
}
// A lightweight optional<locale>.
struct _Optional_locale
{
[[__gnu__::__always_inline__]]
_Optional_locale() : _M_dummy(), _M_hasval(false) { }
_Optional_locale(const locale& __loc) noexcept
: _M_loc(__loc), _M_hasval(true)
{ }
_Optional_locale(const _Optional_locale& __l) noexcept
: _M_dummy(), _M_hasval(__l._M_hasval)
{
if (_M_hasval)
std::construct_at(&_M_loc, __l._M_loc);
}
_Optional_locale&
operator=(const _Optional_locale& __l) noexcept
{
if (_M_hasval)
{
if (__l._M_hasval)
_M_loc = __l._M_loc;
else
{
_M_loc.~locale();
_M_hasval = false;
}
}
else if (__l._M_hasval)
{
std::construct_at(&_M_loc, __l._M_loc);
_M_hasval = true;
}
return *this;
}
~_Optional_locale() { if (_M_hasval) _M_loc.~locale(); }
_Optional_locale&
operator=(locale&& __loc) noexcept
{
if (_M_hasval)
_M_loc = std::move(__loc);
else
{
std::construct_at(&_M_loc, std::move(__loc));
_M_hasval = true;
}
return *this;
}
const locale&
value() noexcept
{
if (!_M_hasval)
{
std::construct_at(&_M_loc);
_M_hasval = true;
}
return _M_loc;
}
bool has_value() const noexcept { return _M_hasval; }
union {
char _M_dummy = '\0';
std::locale _M_loc;
};
bool _M_hasval = false;
};
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _CharT>
concept __char = same_as<_CharT, char> || same_as<_CharT, wchar_t>;
#else
template<typename _CharT>
concept __char = same_as<_CharT, char>;
#endif
template<__char _CharT>
struct __formatter_str
{
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
auto __first = __pc.begin();
const auto __last = __pc.end();
_Spec<_CharT> __spec{};
auto __finalize = [this, &__spec] {
_M_spec = __spec;
};
auto __finished = [&] {
if (__first == __last || *__first == '}')
{
__finalize();
return true;
}
return false;
};
if (__finished())
return __first;
__first = __spec._M_parse_fill_and_align(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_width(__first, __last, __pc);
if (__finished())
return __first;
__first = __spec._M_parse_precision(__first, __last, __pc);
if (__finished())
return __first;
if (*__first == 's')
++__first;
#if __cpp_lib_format_ranges
else if (*__first == '?')
{
__spec._M_type = _Pres_esc;
++__first;
}
#endif
if (__finished())
return __first;
__format::__failed_to_parse_format_spec();
}
template<typename _Out>
_Out
format(basic_string_view<_CharT> __s,
basic_format_context<_Out, _CharT>& __fc) const
{
if (_M_spec._M_type == _Pres_esc)
{
// TODO: C++23 escaped string presentation
}
if (_M_spec._M_width_kind == _WP_none
&& _M_spec._M_prec_kind == _WP_none)
return __format::__write(__fc.out(), __s);
size_t __estimated_width;
if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
{
if (_M_spec._M_prec_kind != _WP_none)
{
size_t __prec = _M_spec._M_get_precision(__fc);
__estimated_width = __unicode::__truncate(__s, __prec);
}
else
__estimated_width = __unicode::__field_width(__s);
}
else
{
__s = __s.substr(0, _M_spec._M_get_precision(__fc));
__estimated_width = __s.size();
}
return __format::__write_padded_as_spec(__s, __estimated_width,
__fc, _M_spec);
}
#if __cpp_lib_format_ranges
constexpr void
set_debug_format() noexcept
{ _M_spec._M_type = _Pres_esc; }
#endif
private:
_Spec<_CharT> _M_spec{};
};
template<__char _CharT>
struct __formatter_int
{
// If no presentation type is specified, meaning of "none" depends
// whether we are formatting an integer or a char or a bool.
static constexpr _Pres_type _AsInteger = _Pres_d;
static constexpr _Pres_type _AsBool = _Pres_s;
static constexpr _Pres_type _AsChar = _Pres_c;
constexpr typename basic_format_parse_context<_CharT>::iterator
_M_do_parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type)
{
_Spec<_CharT> __spec{};
__spec._M_type = __type;
const auto __last = __pc.end();
auto __first = __pc.begin();
auto __finalize = [this, &__spec] {
_M_spec = __spec;
};
auto __finished = [&] {
if (__first == __last || *__first == '}')
{
__finalize();
return true;
}
return false;
};
if (__finished())
return __first;
__first = __spec._M_parse_fill_and_align(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_sign(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_alternate_form(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_zero_fill(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_width(__first, __last, __pc);
if (__finished())
return __first;
__first = __spec._M_parse_locale(__first, __last);
if (__finished())
return __first;
switch (*__first)
{
case 'b':
__spec._M_type = _Pres_b;
++__first;
break;
case 'B':
__spec._M_type = _Pres_B;
++__first;
break;
case 'c':
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3586. format should not print bool with 'c'
if (__type != _AsBool)
{
__spec._M_type = _Pres_c;
++__first;
}
break;
case 'd':
__spec._M_type = _Pres_d;
++__first;
break;
case 'o':
__spec._M_type = _Pres_o;
++__first;
break;
case 'x':
__spec._M_type = _Pres_x;
++__first;
break;
case 'X':
__spec._M_type = _Pres_X;
++__first;
break;
case 's':
if (__type == _AsBool)
{
__spec._M_type = _Pres_s; // same value (and meaning) as "none"
++__first;
}
break;
#if __cpp_lib_format_ranges
case '?':
if (__type == _AsChar)
{
__spec._M_type = _Pres_esc;
++__first;
}
#endif
break;
}
if (__finished())
return __first;
__format::__failed_to_parse_format_spec();
}
template<typename _Tp>
constexpr typename basic_format_parse_context<_CharT>::iterator
_M_parse(basic_format_parse_context<_CharT>& __pc)
{
if constexpr (is_same_v<_Tp, bool>)
{
auto __end = _M_do_parse(__pc, _AsBool);
if (_M_spec._M_type == _Pres_s)
if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill)
__throw_format_error("format error: format-spec contains "
"invalid formatting options for "
"'bool'");
return __end;
}
else if constexpr (__char<_Tp>)
{
auto __end = _M_do_parse(__pc, _AsChar);
if (_M_spec._M_type == _Pres_c || _M_spec._M_type == _Pres_esc)
if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill
/* XXX should be invalid? || _M_spec._M_localized */)
__throw_format_error("format error: format-spec contains "
"invalid formatting options for "
"'charT'");
return __end;
}
else
return _M_do_parse(__pc, _AsInteger);
}
template<typename _Int, typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Int __i, basic_format_context<_Out, _CharT>& __fc) const
{
if (_M_spec._M_type == _Pres_c)
return _M_format_character(_S_to_character(__i), __fc);
char __buf[sizeof(_Int) * __CHAR_BIT__ + 3];
to_chars_result __res{};
string_view __base_prefix;
make_unsigned_t<_Int> __u;
if (__i < 0)
__u = -static_cast<make_unsigned_t<_Int>>(__i);
else
__u = __i;
char* __start = __buf + 3;
char* const __end = __buf + sizeof(__buf);
char* const __start_digits = __start;
switch (_M_spec._M_type)
{
case _Pres_b:
case _Pres_B:
__base_prefix = _M_spec._M_type == _Pres_b ? "0b" : "0B";
__res = to_chars(__start, __end, __u, 2);
break;
#if 0
case _Pres_c:
return _M_format_character(_S_to_character(__i), __fc);
#endif
case _Pres_none:
// Should not reach here with _Pres_none for bool or charT, so:
[[fallthrough]];
case _Pres_d:
__res = to_chars(__start, __end, __u, 10);
break;
case _Pres_o:
if (__i != 0)
__base_prefix = "0";
__res = to_chars(__start, __end, __u, 8);
break;
case _Pres_x:
case _Pres_X:
__base_prefix = _M_spec._M_type == _Pres_x ? "0x" : "0X";
__res = to_chars(__start, __end, __u, 16);
if (_M_spec._M_type == _Pres_X)
for (auto __p = __start; __p != __res.ptr; ++__p)
#if __has_builtin(__builtin_toupper)
*__p = __builtin_toupper(*__p);
#else
*__p = std::toupper(*__p);
#endif
break;
default:
__builtin_unreachable();
}
if (_M_spec._M_alt && __base_prefix.size())
{
__start -= __base_prefix.size();
__builtin_memcpy(__start, __base_prefix.data(),
__base_prefix.size());
}
__start = __format::__put_sign(__i, _M_spec._M_sign, __start - 1);
return _M_format_int(string_view(__start, __res.ptr - __start),
__start_digits - __start, __fc);
}
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(bool __i, basic_format_context<_Out, _CharT>& __fc) const
{
if (_M_spec._M_type == _Pres_c)
return _M_format_character(static_cast<unsigned char>(__i), __fc);
if (_M_spec._M_type != _Pres_s)
return format(static_cast<unsigned char>(__i), __fc);
basic_string<_CharT> __s;
size_t __est_width;
if (_M_spec._M_localized) [[unlikely]]
{
auto& __np = std::use_facet<numpunct<_CharT>>(__fc.locale());
__s = __i ? __np.truename() : __np.falsename();
__est_width = __s.size(); // TODO Unicode-aware estimate
}
else
{
if constexpr (is_same_v<char, _CharT>)
__s = __i ? "true" : "false";
else
__s = __i ? L"true" : L"false";
__est_width = __s.size();
}
return __format::__write_padded_as_spec(__s, __est_width, __fc,
_M_spec);
}
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
_M_format_character(_CharT __c,
basic_format_context<_Out, _CharT>& __fc) const
{
return __format::__write_padded_as_spec({&__c, 1u}, 1, __fc, _M_spec);
}
template<typename _Int>
static _CharT
_S_to_character(_Int __i)
{
using _Traits = __gnu_cxx::__int_traits<_CharT>;
if constexpr (is_signed_v<_Int> == is_signed_v<_CharT>)
{
if (_Traits::__min <= __i && __i <= _Traits::__max)
return static_cast<_CharT>(__i);
}
else if constexpr (is_signed_v<_Int>)
{
if (__i >= 0 && make_unsigned_t<_Int>(__i) <= _Traits::__max)
return static_cast<_CharT>(__i);
}
else if (__i <= make_unsigned_t<_CharT>(_Traits::__max))
return static_cast<_CharT>(__i);
__throw_format_error("format error: integer not representable as "
"character");
}
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
_M_format_int(string_view __narrow_str, size_t __prefix_len,
basic_format_context<_Out, _CharT>& __fc) const
{
size_t __width = _M_spec._M_get_width(__fc);
basic_string_view<_CharT> __str;
if constexpr (is_same_v<char, _CharT>)
__str = __narrow_str;
#ifdef _GLIBCXX_USE_WCHAR_T
else
{
size_t __n = __narrow_str.size();
auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT));
std::__to_wstring_numeric(__narrow_str.data(), __n, __p);
__str = {__p, __n};
}
#endif
if (_M_spec._M_localized)
{
const auto& __l = __fc.locale();
if (__l.name() != "C")
{
auto& __np = use_facet<numpunct<_CharT>>(__l);
string __grp = __np.grouping();
if (!__grp.empty())
{
size_t __n = __str.size() - __prefix_len;
auto __p = (_CharT*)__builtin_alloca(2 * __n
* sizeof(_CharT)
+ __prefix_len);
auto __s = __str.data();
char_traits<_CharT>::copy(__p, __s, __prefix_len);
__s += __prefix_len;
auto __end = std::__add_grouping(__p + __prefix_len,
__np.thousands_sep(),
__grp.data(),
__grp.size(),
__s, __s + __n);
__str = {__p, size_t(__end - __p)};
}
}
}
if (__width <= __str.size())
return __format::__write(__fc.out(), __str);
char32_t __fill_char = _M_spec._M_fill;
_Align __align = _M_spec._M_align;
size_t __nfill = __width - __str.size();
auto __out = __fc.out();
if (__align == _Align_default)
{
__align = _Align_right;
if (_M_spec._M_zero_fill)
{
__fill_char = _CharT('0');
// Write sign and base prefix before zero filling.
if (__prefix_len != 0)
{
__out = __format::__write(std::move(__out),
__str.substr(0, __prefix_len));
__str.remove_prefix(__prefix_len);
}
}
else
__fill_char = _CharT(' ');
}
return __format::__write_padded(std::move(__out), __str,
__align, __nfill, __fill_char);
}
#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
template<typename _Tp>
using make_unsigned_t
= typename __conditional_t<(sizeof(_Tp) <= sizeof(long long)),
std::make_unsigned<_Tp>,
type_identity<unsigned __int128>>::type;
// std::to_chars is not overloaded for int128 in strict mode.
template<typename _Int>
static to_chars_result
to_chars(char* __first, char* __last, _Int __value, int __base)
{ return std::__to_chars_i<_Int>(__first, __last, __value, __base); }
#endif
_Spec<_CharT> _M_spec{};
};
// Decide how 128-bit floating-point types should be formatted (or not).
// When supported, the typedef __format::__float128_t is the type that
// format arguments should be converted to for storage in basic_format_arg.
// Define the macro _GLIBCXX_FORMAT_F128 to say they're supported.
// _GLIBCXX_FORMAT_F128=1 means __float128, _Float128 etc. will be formatted
// by converting them to long double (or __ieee128 for powerpc64le).
// _GLIBCXX_FORMAT_F128=2 means basic_format_arg needs to enable explicit
// support for _Float128, rather than formatting it as another type.
#undef _GLIBCXX_FORMAT_F128
#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
// Format 128-bit floating-point types using __ieee128.
using __float128_t = __ieee128;
# define _GLIBCXX_FORMAT_F128 1
#ifdef __LONG_DOUBLE_IEEE128__
// These overloads exist in the library, but are not declared.
// Make them available as std::__format::to_chars.
to_chars_result
to_chars(char*, char*, __ibm128) noexcept
__asm("_ZSt8to_charsPcS_e");
to_chars_result
to_chars(char*, char*, __ibm128, chars_format) noexcept
__asm("_ZSt8to_charsPcS_eSt12chars_format");
to_chars_result
to_chars(char*, char*, __ibm128, chars_format, int) noexcept
__asm("_ZSt8to_charsPcS_eSt12chars_formati");
#elif __cplusplus == 202002L
to_chars_result
to_chars(char*, char*, __ieee128) noexcept
__asm("_ZSt8to_charsPcS_u9__ieee128");
to_chars_result
to_chars(char*, char*, __ieee128, chars_format) noexcept
__asm("_ZSt8to_charsPcS_u9__ieee128St12chars_format");
to_chars_result
to_chars(char*, char*, __ieee128, chars_format, int) noexcept
__asm("_ZSt8to_charsPcS_u9__ieee128St12chars_formati");
#endif
#elif defined _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128
// Format 128-bit floating-point types using long double.
using __float128_t = long double;
# define _GLIBCXX_FORMAT_F128 1
#elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH)
// Format 128-bit floating-point types using _Float128.
using __float128_t = _Float128;
# define _GLIBCXX_FORMAT_F128 2
# if __cplusplus == 202002L
// These overloads exist in the library, but are not declared for C++20.
// Make them available as std::__format::to_chars.
to_chars_result
to_chars(char*, char*, _Float128) noexcept
# if _GLIBCXX_INLINE_VERSION
__asm("_ZNSt3__88to_charsEPcS0_DF128_");
# else
__asm("_ZSt8to_charsPcS_DF128_");
# endif
to_chars_result
to_chars(char*, char*, _Float128, chars_format) noexcept
# if _GLIBCXX_INLINE_VERSION
__asm("_ZNSt3__88to_charsEPcS0_DF128_NS_12chars_formatE");
# else
__asm("_ZSt8to_charsPcS_DF128_St12chars_format");
# endif
to_chars_result
to_chars(char*, char*, _Float128, chars_format, int) noexcept
# if _GLIBCXX_INLINE_VERSION
__asm("_ZNSt3__88to_charsEPcS0_DF128_NS_12chars_formatEi");
# else
__asm("_ZSt8to_charsPcS_DF128_St12chars_formati");
# endif
# endif
#endif
using std::to_chars;
// We can format a floating-point type iff it is usable with to_chars.
template<typename _Tp>
concept __formattable_float = requires (_Tp __t, char* __p)
{ __format::to_chars(__p, __p, __t, chars_format::scientific, 6); };
template<__char _CharT>
struct __formatter_fp
{
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
_Spec<_CharT> __spec{};
const auto __last = __pc.end();
auto __first = __pc.begin();
auto __finalize = [this, &__spec] {
_M_spec = __spec;
};
auto __finished = [&] {
if (__first == __last || *__first == '}')
{
__finalize();
return true;
}
return false;
};
if (__finished())
return __first;
__first = __spec._M_parse_fill_and_align(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_sign(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_alternate_form(__first, __last);
if (__finished())
return __first;
__first = __spec._M_parse_zero_fill(__first, __last);
if (__finished())
return __first;
if (__first[0] != '.')
{
__first = __spec._M_parse_width(__first, __last, __pc);
if (__finished())
return __first;
}
__first = __spec._M_parse_precision(__first, __last, __pc);
if (__finished())
return __first;
__first = __spec._M_parse_locale(__first, __last);
if (__finished())
return __first;
switch (*__first)
{
case 'a':
__spec._M_type = _Pres_a;
++__first;
break;
case 'A':
__spec._M_type = _Pres_A;
++__first;
break;
case 'e':
__spec._M_type = _Pres_e;
++__first;
break;
case 'E':
__spec._M_type = _Pres_E;
++__first;
break;
case 'f':
__spec._M_type = _Pres_f;
++__first;
break;
case 'F':
__spec._M_type = _Pres_F;
++__first;
break;
case 'g':
__spec._M_type = _Pres_g;
++__first;
break;
case 'G':
__spec._M_type = _Pres_G;
++__first;
break;
}
if (__finished())
return __first;
__format::__failed_to_parse_format_spec();
}
template<typename _Fp, typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Fp __v, basic_format_context<_Out, _CharT>& __fc) const
{
std::string __dynbuf;
char __buf[128];
to_chars_result __res{};
size_t __prec = 6;
bool __use_prec = _M_spec._M_prec_kind != _WP_none;
if (__use_prec)
__prec = _M_spec._M_get_precision(__fc);
char* __start = __buf + 1; // reserve space for sign
char* __end = __buf + sizeof(__buf);
chars_format __fmt{};
bool __upper = false;
bool __trailing_zeros = false;
char __expc = 'e';
switch (_M_spec._M_type)
{
case _Pres_A:
__upper = true;
__expc = 'P';
[[fallthrough]];
case _Pres_a:
if (_M_spec._M_type != _Pres_A)
__expc = 'p';
__fmt = chars_format::hex;
break;
case _Pres_E:
__upper = true;
__expc = 'E';
[[fallthrough]];
case _Pres_e:
__use_prec = true;
__fmt = chars_format::scientific;
break;
case _Pres_F:
__upper = true;
[[fallthrough]];
case _Pres_f:
__use_prec = true;
__fmt = chars_format::fixed;
break;
case _Pres_G:
__upper = true;
__expc = 'E';
[[fallthrough]];
case _Pres_g:
__trailing_zeros = true;
__use_prec = true;
__fmt = chars_format::general;
break;
case _Pres_none:
if (__use_prec)
__fmt = chars_format::general;
break;
default:
__builtin_unreachable();
}
// Write value into buffer using std::to_chars.
auto __to_chars = [&](char* __b, char* __e) {
if (__use_prec)
return __format::to_chars(__b, __e, __v, __fmt, __prec);
else if (__fmt != chars_format{})
return __format::to_chars(__b, __e, __v, __fmt);
else
return __format::to_chars(__b, __e, __v);
};
// First try using stack buffer.
__res = __to_chars(__start, __end);
if (__builtin_expect(__res.ec == errc::value_too_large, 0))
{
// If the buffer is too small it's probably because of a large
// precision, or a very large value in fixed format.
size_t __guess = 8 + __prec;
if (__fmt == chars_format::fixed) // +ddd.prec
{
if constexpr (is_same_v<_Fp, float> || is_same_v<_Fp, double>
|| is_same_v<_Fp, long double>)
{
// The number of digits to the left of the decimal point
// is floor(log10(max(abs(__v),1)))+1
int __exp{};
if constexpr (is_same_v<_Fp, float>)
__builtin_frexpf(__v, &__exp);
else if constexpr (is_same_v<_Fp, double>)
__builtin_frexp(__v, &__exp);
else if constexpr (is_same_v<_Fp, long double>)
__builtin_frexpl(__v, &__exp);
if (__exp > 0)
__guess += 1U + __exp * 4004U / 13301U; // log10(2) approx.
}
else
__guess += numeric_limits<_Fp>::max_exponent10;
}
if (__guess <= sizeof(__buf)) [[unlikely]]
__guess = sizeof(__buf) * 2;
__dynbuf.reserve(__guess);
do
{
auto __overwrite = [&__to_chars, &__res] (char* __p, size_t __n)
{
__res = __to_chars(__p + 1, __p + __n - 1);
return __res.ec == errc{} ? __res.ptr - __p : 0;
};
__dynbuf.__resize_and_overwrite(__dynbuf.capacity() * 2,
__overwrite);
__start = __dynbuf.data() + 1; // reserve space for sign
__end = __dynbuf.data() + __dynbuf.size();
}
while (__builtin_expect(__res.ec == errc::value_too_large, 0));
}
// Use uppercase for 'A', 'E', and 'G' formats.
if (__upper)
{
for (char* __p = __start; __p != __res.ptr; ++__p)
*__p = std::toupper(*__p);
}
bool __have_sign = true;
// Add sign for non-negative values.
if (!__builtin_signbit(__v))
{
if (_M_spec._M_sign == _Sign_plus)
*--__start = '+';
else if (_M_spec._M_sign == _Sign_space)
*--__start = ' ';
else
__have_sign = false;
}
string_view __narrow_str(__start, __res.ptr - __start);
// Use alternate form. Ensure decimal point is always present,
// and add trailing zeros (up to precision) for g and G forms.
if (_M_spec._M_alt && __builtin_isfinite(__v))
{
string_view __s = __narrow_str;
size_t __sigfigs; // Number of significant figures.
size_t __z = 0; // Number of trailing zeros to add.
size_t __p; // Position of the exponent character (if any).
size_t __d = __s.find('.'); // Position of decimal point.
if (__d != __s.npos) // Found decimal point.
{
__p = __s.find(__expc, __d + 1);
if (__p == __s.npos)
__p = __s.size();
// If presentation type is g or G we might need to add zeros.
if (__trailing_zeros)
{
// Find number of digits after first significant figure.
if (__s[__have_sign] != '0')
// A string like "D.D" or "-D.DDD"
__sigfigs = __p - __have_sign - 1;
else
// A string like "0.D" or "-0.0DD".
// Safe to assume there is a non-zero digit, because
// otherwise there would be no decimal point.
__sigfigs = __p - __s.find_first_not_of('0', __d + 1);
}
}
else // No decimal point, we need to insert one.
{
__p = __s.find(__expc); // Find the exponent, if present.
if (__p == __s.npos)
__p = __s.size();
__d = __p; // Position where '.' should be inserted.
__sigfigs = __d - __have_sign;
}
if (__trailing_zeros && __prec != 0)
{
// For g and G presentation types std::to_chars produces
// no more than prec significant figures. Insert this many
// zeros so the result has exactly prec significant figures.
__z = __prec - __sigfigs;
}
if (size_t __extras = int(__d == __p) + __z) // How many to add.
{
if (__dynbuf.empty() && __extras <= size_t(__end - __res.ptr))
{
// The stack buffer is large enough for the result.
// Move exponent to make space for extra chars.
__builtin_memmove(__start + __p + __extras,
__start + __p,
__s.size() - __p);
if (__d == __p)
__start[__p++] = '.';
__builtin_memset(__start + __p, '0', __z);
__narrow_str = {__s.data(), __s.size() + __extras};
}
else // Need to switch to the dynamic buffer.
{
__dynbuf.reserve(__s.size() + __extras);
if (__dynbuf.empty())
{
__dynbuf = __s.substr(0, __p);
if (__d == __p)
__dynbuf += '.';
if (__z)
__dynbuf.append(__z, '0');
__dynbuf.append(__s.substr(__p));
}
else
{
__dynbuf.insert(__p, __extras, '0');
if (__d == __p)
__dynbuf[__p] = '.';
}
__narrow_str = __dynbuf;
}
}
}
basic_string<_CharT> __wstr;
basic_string_view<_CharT> __str;
if constexpr (is_same_v<_CharT, char>)
__str = __narrow_str;
#ifdef _GLIBCXX_USE_WCHAR_T
else
{
__wstr = std::__to_wstring_numeric(__narrow_str);
__str = __wstr;
}
#endif
if (_M_spec._M_localized && __builtin_isfinite(__v))
{
__wstr = _M_localize(__str, __expc, __fc.locale());
if (!__wstr.empty())
__str = __wstr;
}
size_t __width = _M_spec._M_get_width(__fc);
if (__width <= __str.size())
return __format::__write(__fc.out(), __str);
char32_t __fill_char = _M_spec._M_fill;
_Align __align = _M_spec._M_align;
size_t __nfill = __width - __str.size();
auto __out = __fc.out();
if (__align == _Align_default)
{
__align = _Align_right;
if (_M_spec._M_zero_fill && __builtin_isfinite(__v))
{
__fill_char = _CharT('0');
// Write sign before zero filling.
if (!__format::__is_xdigit(__narrow_str[0]))
{
*__out++ = __str[0];
__str.remove_prefix(1);
}
}
else
__fill_char = _CharT(' ');
}
return __format::__write_padded(std::move(__out), __str,
__align, __nfill, __fill_char);
}
// Locale-specific format.
basic_string<_CharT>
_M_localize(basic_string_view<_CharT> __str, char __expc,
const locale& __loc) const
{
basic_string<_CharT> __lstr;
if (__loc == locale::classic())
return __lstr; // Nothing to do.
const auto& __np = use_facet<numpunct<_CharT>>(__loc);
const _CharT __point = __np.decimal_point();
const string __grp = __np.grouping();
_CharT __dot, __exp;
if constexpr (is_same_v<_CharT, char>)
{
__dot = '.';
__exp = __expc;
}
else
{
__dot = L'.';
switch (__expc)
{
case 'e':
__exp = L'e';
break;
case 'E':
__exp = L'E';
break;
case 'p':
__exp = L'p';
break;
case 'P':
__exp = L'P';
break;
default:
__builtin_unreachable();
}
}
if (__grp.empty() && __point == __dot)
return __lstr; // Locale uses '.' and no grouping.
size_t __d = __str.find(__dot);
size_t __e = min(__d, __str.find(__exp));
if (__e == __str.npos)
__e = __str.size();
const size_t __r = __str.size() - __e;
auto __overwrite = [&](_CharT* __p, size_t) {
auto __end = std::__add_grouping(__p, __np.thousands_sep(),
__grp.data(), __grp.size(),
__str.data(), __str.data() + __e);
if (__r)
{
if (__d != __str.npos)
{
*__end = __point;
++__end;
++__e;
}
if (__r > 1)
__end += __str.copy(__end, __str.npos, __e);
}
return (__end - __p);
};
__lstr.__resize_and_overwrite(__e * 2 + __r, __overwrite);
return __lstr;
}
_Spec<_CharT> _M_spec{};
};
} // namespace __format
/// @endcond
/// Format a character.
template<__format::__char _CharT>
struct formatter<_CharT, _CharT>
{
formatter() = default;
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
return _M_f.template _M_parse<_CharT>(__pc);
}
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_CharT __u, basic_format_context<_Out, _CharT>& __fc) const
{
if (_M_f._M_spec._M_type == __format::_Pres_none
|| _M_f._M_spec._M_type == __format::_Pres_c)
return _M_f._M_format_character(__u, __fc);
else if (_M_f._M_spec._M_type == __format::_Pres_esc)
{
// TODO
return __fc.out();
}
else
return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc);
}
#if __cpp_lib_format_ranges
constexpr void
set_debug_format() noexcept
{ _M_f._M_spec._M_type = __format::_Pres_esc; }
#endif
private:
__format::__formatter_int<_CharT> _M_f;
};
#ifdef _GLIBCXX_USE_WCHAR_T
/// Format a char value for wide character output.
template<>
struct formatter<char, wchar_t>
{
formatter() = default;
constexpr typename basic_format_parse_context<wchar_t>::iterator
parse(basic_format_parse_context<wchar_t>& __pc)
{
return _M_f._M_parse<char>(__pc);
}
template<typename _Out>
typename basic_format_context<_Out, wchar_t>::iterator
format(char __u, basic_format_context<_Out, wchar_t>& __fc) const
{
if (_M_f._M_spec._M_type == __format::_Pres_none
|| _M_f._M_spec._M_type == __format::_Pres_c)
return _M_f._M_format_character(__u, __fc);
else if (_M_f._M_spec._M_type == __format::_Pres_esc)
{
// TODO
return __fc.out();
}
else
return _M_f.format(static_cast<unsigned char>(__u), __fc);
}
#if __cpp_lib_format_ranges
constexpr void
set_debug_format() noexcept
{ _M_f._M_spec._M_type = __format::_Pres_esc; }
#endif
private:
__format::__formatter_int<wchar_t> _M_f;
};
#endif // USE_WCHAR_T
/** Format a string.
* @{
*/
template<__format::__char _CharT>
struct formatter<_CharT*, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
[[__gnu__::__nonnull__]]
typename basic_format_context<_Out, _CharT>::iterator
format(_CharT* __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(__u, __fc); }
#if __cpp_lib_format_ranges
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<_CharT> _M_f;
};
template<__format::__char _CharT>
struct formatter<const _CharT*, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
[[__gnu__::__nonnull__]]
typename basic_format_context<_Out, _CharT>::iterator
format(const _CharT* __u,
basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(__u, __fc); }
#if __cpp_lib_format_ranges
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<_CharT> _M_f;
};
template<__format::__char _CharT, size_t _Nm>
struct formatter<_CharT[_Nm], _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(const _CharT (&__u)[_Nm],
basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format({__u, _Nm}, __fc); }
#if __cpp_lib_format_ranges
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<_CharT> _M_f;
};
template<typename _Traits, typename _Alloc>
struct formatter<basic_string<char, _Traits, _Alloc>, char>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<char>::iterator
parse(basic_format_parse_context<char>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, char>::iterator
format(const basic_string<char, _Traits, _Alloc>& __u,
basic_format_context<_Out, char>& __fc) const
{ return _M_f.format(__u, __fc); }
#if __cpp_lib_format_ranges
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<char> _M_f;
};
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Traits, typename _Alloc>
struct formatter<basic_string<wchar_t, _Traits, _Alloc>, wchar_t>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<wchar_t>::iterator
parse(basic_format_parse_context<wchar_t>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, wchar_t>::iterator
format(const basic_string<wchar_t, _Traits, _Alloc>& __u,
basic_format_context<_Out, wchar_t>& __fc) const
{ return _M_f.format(__u, __fc); }
#if __cpp_lib_format_ranges
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<wchar_t> _M_f;
};
#endif // USE_WCHAR_T
template<typename _Traits>
struct formatter<basic_string_view<char, _Traits>, char>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<char>::iterator
parse(basic_format_parse_context<char>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, char>::iterator
format(basic_string_view<char, _Traits> __u,
basic_format_context<_Out, char>& __fc) const
{ return _M_f.format(__u, __fc); }
#if __cpp_lib_format_ranges
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<char> _M_f;
};
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Traits>
struct formatter<basic_string_view<wchar_t, _Traits>, wchar_t>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<wchar_t>::iterator
parse(basic_format_parse_context<wchar_t>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, wchar_t>::iterator
format(basic_string_view<wchar_t, _Traits> __u,
basic_format_context<_Out, wchar_t>& __fc) const
{ return _M_f.format(__u, __fc); }
#if __cpp_lib_format_ranges
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<wchar_t> _M_f;
};
#endif // USE_WCHAR_T
/// @}
/// Format an integer.
template<integral _Tp, __format::__char _CharT>
requires (!__is_one_of<_Tp, char, wchar_t, char16_t, char32_t>::value)
struct formatter<_Tp, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
return _M_f.template _M_parse<_Tp>(__pc);
}
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(__u, __fc); }
private:
__format::__formatter_int<_CharT> _M_f;
};
#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
template<typename _Tp, __format::__char _CharT>
requires (__is_one_of<_Tp, __int128, unsigned __int128>::value)
struct formatter<_Tp, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
return _M_f.template _M_parse<_Tp>(__pc);
}
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(__u, __fc); }
private:
__format::__formatter_int<_CharT> _M_f;
};
#endif
#if defined __glibcxx_to_chars
/// Format a floating-point value.
template<__format::__formattable_float _Tp, __format::__char _CharT>
struct formatter<_Tp, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
// Reuse __formatter_fp<C>::format<double, Out> for long double.
template<__format::__char _CharT>
struct formatter<long double, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(long double __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format((double)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#ifdef __STDCPP_FLOAT16_T__
// Reuse __formatter_fp<C>::format<float, Out> for _Float16.
template<__format::__char _CharT>
struct formatter<_Float16, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Float16 __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format((float)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#if defined(__FLT32_DIG__)
// Reuse __formatter_fp<C>::format<float, Out> for _Float32.
template<__format::__char _CharT>
struct formatter<_Float32, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Float32 __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format((float)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#if defined(__FLT64_DIG__)
// Reuse __formatter_fp<C>::format<double, Out> for _Float64.
template<__format::__char _CharT>
struct formatter<_Float64, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Float64 __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format((double)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 == 1
// Reuse __formatter_fp<C>::format<__float128_t, Out> for _Float128.
template<__format::__char _CharT>
struct formatter<_Float128, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format((__format::__float128_t)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#ifdef __STDCPP_BFLOAT16_T__
// Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t.
template<__format::__char _CharT>
struct formatter<__gnu_cxx::__bfloat16_t, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(__gnu_cxx::__bfloat16_t __u,
basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format((float)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#endif // __cpp_lib_to_chars
/** Format a pointer.
* @{
*/
template<__format::__char _CharT>
struct formatter<const void*, _CharT>
{
formatter() = default;
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
__format::_Spec<_CharT> __spec{};
const auto __last = __pc.end();
auto __first = __pc.begin();
auto __finalize = [this, &__spec] {
_M_spec = __spec;
};
auto __finished = [&] {
if (__first == __last || *__first == '}')
{
__finalize();
return true;
}
return false;
};
if (__finished())
return __first;
__first = __spec._M_parse_fill_and_align(__first, __last);
if (__finished())
return __first;
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// P2510R3 Formatting pointers
#if __glibcxx_format >= 202304L || ! defined __STRICT_ANSI__
# define _GLIBCXX_P2518R3 1
#else
# define _GLIBCXX_P2518R3 0
#endif
#if _GLIBCXX_P2518R3
__first = __spec._M_parse_zero_fill(__first, __last);
if (__finished())
return __first;
#endif
__first = __spec._M_parse_width(__first, __last, __pc);
if (__first != __last)
{
if (*__first == 'p')
++__first;
#if _GLIBCXX_P2518R3
else if (*__first == 'P')
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// P2510R3 Formatting pointers
__spec._M_type = __format::_Pres_P;
++__first;
}
#endif
}
if (__finished())
return __first;
__format::__failed_to_parse_format_spec();
}
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const
{
auto __u = reinterpret_cast<__UINTPTR_TYPE__>(__v);
char __buf[2 + sizeof(__v) * 2];
auto [__ptr, __ec] = std::to_chars(__buf + 2, std::end(__buf),
__u, 16);
int __n = __ptr - __buf;
__buf[0] = '0';
__buf[1] = 'x';
#if _GLIBCXX_P2518R3
if (_M_spec._M_type == __format::_Pres_P)
{
__buf[1] = 'X';
for (auto __p = __buf + 2; __p != __ptr; ++__p)
#if __has_builtin(__builtin_toupper)
*__p = __builtin_toupper(*__p);
#else
*__p = std::toupper(*__p);
#endif
}
#endif
basic_string_view<_CharT> __str;
if constexpr (is_same_v<_CharT, char>)
__str = string_view(__buf, __n);
#ifdef _GLIBCXX_USE_WCHAR_T
else
{
auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT));
std::__to_wstring_numeric(__buf, __n, __p);
__str = wstring_view(__p, __n);
}
#endif
#if _GLIBCXX_P2518R3
if (_M_spec._M_zero_fill)
{
size_t __width = _M_spec._M_get_width(__fc);
if (__width <= __str.size())
return __format::__write(__fc.out(), __str);
auto __out = __fc.out();
// Write "0x" or "0X" prefix before zero-filling.
__out = __format::__write(std::move(__out), __str.substr(0, 2));
__str.remove_prefix(2);
size_t __nfill = __width - __n;
return __format::__write_padded(std::move(__out), __str,
__format::_Align_right,
__nfill, _CharT('0'));
}
#endif
return __format::__write_padded_as_spec(__str, __n, __fc, _M_spec,
__format::_Align_right);
}
private:
__format::_Spec<_CharT> _M_spec{};
};
template<__format::__char _CharT>
struct formatter<void*, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(void* __v, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(__v, __fc); }
private:
formatter<const void*, _CharT> _M_f;
};
template<__format::__char _CharT>
struct formatter<nullptr_t, _CharT>
{
formatter() = default;
[[__gnu__::__always_inline__]]
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(nullptr_t, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(nullptr, __fc); }
private:
formatter<const void*, _CharT> _M_f;
};
/// @}
#if defined _GLIBCXX_USE_WCHAR_T && __cpp_lib_format_ranges
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3944. Formatters converting sequences of char to sequences of wchar_t
namespace __format { struct __disabled; }
// std::formatter<__disabled, C> uses the primary template, which is disabled.
template<>
struct formatter<char*, wchar_t>
: private formatter<__format::__disabled, wchar_t> { };
template<>
struct formatter<const char*, wchar_t>
: private formatter<__format::__disabled, wchar_t> { };
template<size_t _Nm>
struct formatter<char[_Nm], wchar_t>
: private formatter<__format::__disabled, wchar_t> { };
template<class _Traits, class _Allocator>
struct formatter<basic_string<char, _Traits, _Allocator>, wchar_t>
: private formatter<__format::__disabled, wchar_t> { };
template<class _Traits>
struct formatter<basic_string_view<char, _Traits>, wchar_t>
: private formatter<__format::__disabled, wchar_t> { };
#endif
/// @cond undocumented
namespace __format
{
template<typename _Tp, typename _Context,
typename _Formatter
= typename _Context::template formatter_type<remove_const_t<_Tp>>,
typename _ParseContext
= basic_format_parse_context<typename _Context::char_type>>
concept __parsable_with
= semiregular<_Formatter>
&& requires (_Formatter __f, _ParseContext __pc)
{
{ __f.parse(__pc) } -> same_as<typename _ParseContext::iterator>;
};
template<typename _Tp, typename _Context,
typename _Formatter
= typename _Context::template formatter_type<remove_const_t<_Tp>>,
typename _ParseContext
= basic_format_parse_context<typename _Context::char_type>>
concept __formattable_with
= semiregular<_Formatter>
&& requires (const _Formatter __cf, _Tp&& __t, _Context __fc)
{
{ __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
};
// An unspecified output iterator type used in the `formattable` concept.
template<typename _CharT>
using _Iter_for = back_insert_iterator<basic_string<_CharT>>;
template<typename _Tp, typename _CharT,
typename _Context = basic_format_context<_Iter_for<_CharT>, _CharT>>
concept __formattable_impl
= __parsable_with<_Tp, _Context> && __formattable_with<_Tp, _Context>;
} // namespace __format
/// @endcond
#if __cplusplus > 202002L
// [format.formattable], concept formattable
template<typename _Tp, typename _CharT>
concept formattable
= __format::__formattable_impl<remove_reference_t<_Tp>, _CharT>;
#endif
#if __cpp_lib_format_ranges
/// @cond undocumented
namespace __format
{
template<typename _Rg, typename _CharT>
concept __const_formattable_range
= ranges::input_range<const _Rg>
&& formattable<ranges::range_reference_t<const _Rg>, _CharT>;
template<typename _Rg, typename _CharT>
using __maybe_const_range
= conditional_t<__const_formattable_range<_Rg, _CharT>, const _Rg, _Rg>;
} // namespace __format
/// @endcond
#endif // format_ranges
/// An iterator after the last character written, and the number of
/// characters that would have been written.
template<typename _Out>
struct format_to_n_result
{
_Out out;
iter_difference_t<_Out> size;
};
_GLIBCXX_BEGIN_NAMESPACE_CONTAINER
template<typename, typename> class vector;
_GLIBCXX_END_NAMESPACE_CONTAINER
/// @cond undocumented
namespace __format
{
template<typename _CharT>
class _Sink_iter
{
_Sink<_CharT>* _M_sink = nullptr;
public:
using iterator_category = output_iterator_tag;
using value_type = void;
using difference_type = ptrdiff_t;
using pointer = void;
using reference = void;
_Sink_iter() = default;
_Sink_iter(const _Sink_iter&) = default;
_Sink_iter& operator=(const _Sink_iter&) = default;
[[__gnu__::__always_inline__]]
explicit constexpr
_Sink_iter(_Sink<_CharT>& __sink) : _M_sink(std::addressof(__sink)) { }
[[__gnu__::__always_inline__]]
constexpr _Sink_iter&
operator=(_CharT __c)
{
_M_sink->_M_write(__c);
return *this;
}
[[__gnu__::__always_inline__]]
constexpr _Sink_iter&
operator=(basic_string_view<_CharT> __s)
{
_M_sink->_M_write(__s);
return *this;
}
[[__gnu__::__always_inline__]]
constexpr _Sink_iter&
operator*() { return *this; }
[[__gnu__::__always_inline__]]
constexpr _Sink_iter&
operator++() { return *this; }
[[__gnu__::__always_inline__]]
constexpr _Sink_iter
operator++(int) { return *this; }
auto
_M_reserve(size_t __n) const
{ return _M_sink->_M_reserve(__n); }
};
// Abstract base class for type-erased character sinks.
// All formatting and output is done via this type's iterator,
// to reduce the number of different template instantiations.
template<typename _CharT>
class _Sink
{
friend class _Sink_iter<_CharT>;
span<_CharT> _M_span;
typename span<_CharT>::iterator _M_next;
// Called when the span is full, to make more space available.
// Precondition: _M_next != _M_span.begin()
// Postcondition: _M_next != _M_span.end()
// TODO: remove the precondition? could make overflow handle it.
virtual void _M_overflow() = 0;
protected:
// Precondition: __span.size() != 0
[[__gnu__::__always_inline__]]
explicit constexpr
_Sink(span<_CharT> __span) noexcept
: _M_span(__span), _M_next(__span.begin())
{ }
// The portion of the span that has been written to.
[[__gnu__::__always_inline__]]
span<_CharT>
_M_used() const noexcept
{ return _M_span.first(_M_next - _M_span.begin()); }
// The portion of the span that has not been written to.
[[__gnu__::__always_inline__]]
constexpr span<_CharT>
_M_unused() const noexcept
{ return _M_span.subspan(_M_next - _M_span.begin()); }
// Use the start of the span as the next write position.
[[__gnu__::__always_inline__]]
constexpr void
_M_rewind() noexcept
{ _M_next = _M_span.begin(); }
// Replace the current output range.
void
_M_reset(span<_CharT> __s, size_t __pos = 0) noexcept
{
_M_span = __s;
_M_next = __s.begin() + __pos;
}
// Called by the iterator for *it++ = c
constexpr void
_M_write(_CharT __c)
{
*_M_next++ = __c;
if (_M_next - _M_span.begin() == std::ssize(_M_span)) [[unlikely]]
_M_overflow();
}
constexpr void
_M_write(basic_string_view<_CharT> __s)
{
span __to = _M_unused();
while (__to.size() <= __s.size())
{
__s.copy(__to.data(), __to.size());
_M_next += __to.size();
__s.remove_prefix(__to.size());
_M_overflow();
__to = _M_unused();
}
if (__s.size())
{
__s.copy(__to.data(), __s.size());
_M_next += __s.size();
}
}
// A successful _Reservation can be used to directly write
// up to N characters to the sink to avoid unwanted buffering.
struct _Reservation
{
// True if the reservation was successful, false otherwise.
explicit operator bool() const noexcept { return _M_sink; }
// A pointer to write directly to the sink.
_CharT* get() const noexcept { return _M_sink->_M_next.operator->(); }
// Add n to the _M_next iterator for the sink.
void _M_bump(size_t __n) { _M_sink->_M_bump(__n); }
_Sink* _M_sink;
};
// Attempt to reserve space to write n characters to the sink.
// If anything is written to the reservation then there must be a call
// to _M_bump(N2) before any call to another member function of *this,
// where N2 is the number of characters written.
virtual _Reservation
_M_reserve(size_t __n)
{
if (__n <= _M_unused().size())
return { this };
if (__n <= _M_span.size()) // Cannot meet the request.
{
_M_overflow(); // Make more space available.
if (__n <= _M_unused().size())
return { this };
}
return { nullptr };
}
// Update the next output position after writing directly to the sink.
// pre: no calls to _M_write or _M_overflow since _M_reserve.
virtual void
_M_bump(size_t __n)
{ _M_next += __n; }
public:
_Sink(const _Sink&) = delete;
_Sink& operator=(const _Sink&) = delete;
[[__gnu__::__always_inline__]]
constexpr _Sink_iter<_CharT>
out() noexcept
{ return _Sink_iter<_CharT>(*this); }
};
// A sink with an internal buffer. This is used to implement concrete sinks.
template<typename _CharT>
class _Buf_sink : public _Sink<_CharT>
{
protected:
_CharT _M_buf[32 * sizeof(void*) / sizeof(_CharT)];
[[__gnu__::__always_inline__]]
constexpr
_Buf_sink() noexcept
: _Sink<_CharT>(_M_buf)
{ }
};
using _GLIBCXX_STD_C::vector;
// A sink that fills a sequence (e.g. std::string, std::vector, std::deque).
// Writes to a buffer then appends that to the sequence when it fills up.
template<typename _Seq>
class _Seq_sink final : public _Buf_sink<typename _Seq::value_type>
{
using _CharT = typename _Seq::value_type;
_Seq _M_seq;
// Transfer buffer contents to the sequence, so buffer can be refilled.
void
_M_overflow() override
{
auto __s = this->_M_used();
if (__s.empty()) [[unlikely]]
return; // Nothing in the buffer to transfer to _M_seq.
// If _M_reserve was called then _M_bump must have been called too.
_GLIBCXX_DEBUG_ASSERT(__s.data() != _M_seq.data());
if constexpr (__is_specialization_of<_Seq, basic_string>)
_M_seq.append(__s.data(), __s.size());
else
_M_seq.insert(_M_seq.end(), __s.begin(), __s.end());
// Make the whole of _M_buf available for the next write:
this->_M_rewind();
}
typename _Sink<_CharT>::_Reservation
_M_reserve(size_t __n) override
{
// We might already have n characters available in this->_M_unused(),
// but the whole point of this function is to be an optimization for
// the std::format("{}", x) case. We want to avoid writing to _M_buf
// and then copying that into a basic_string if possible, so this
// function prefers to create space directly in _M_seq rather than
// using _M_buf.
if constexpr (__is_specialization_of<_Seq, basic_string>
|| __is_specialization_of<_Seq, vector>)
{
// Flush the buffer to _M_seq first (should not be needed).
if (this->_M_used().size()) [[unlikely]]
_Seq_sink::_M_overflow();
// Expand _M_seq to make __n new characters available:
const auto __sz = _M_seq.size();
if constexpr (is_same_v<string, _Seq> || is_same_v<wstring, _Seq>)
_M_seq.__resize_and_overwrite(__sz + __n,
[](auto, auto __n2) {
return __n2;
});
else
_M_seq.resize(__sz + __n);
// Set _M_used() to be a span over the original part of _M_seq
// and _M_unused() to be the extra capacity we just created:
this->_M_reset(_M_seq, __sz);
return { this };
}
else // Try to use the base class' buffer.
return _Sink<_CharT>::_M_reserve(__n);
}
void
_M_bump(size_t __n) override
{
if constexpr (__is_specialization_of<_Seq, basic_string>
|| __is_specialization_of<_Seq, vector>)
{
auto __s = this->_M_used();
_GLIBCXX_DEBUG_ASSERT(__s.data() == _M_seq.data());
// Truncate the sequence to the part that was actually written to:
_M_seq.resize(__s.size() + __n);
// Switch back to using buffer:
this->_M_reset(this->_M_buf);
}
}
public:
// TODO: for SSO string, use SSO buffer as initial span, then switch
// to _M_buf if it overflows? Or even do that for all unused capacity?
[[__gnu__::__always_inline__]]
_Seq_sink() noexcept(is_nothrow_default_constructible_v<_Seq>)
{ }
_Seq_sink(_Seq&& __s) noexcept(is_nothrow_move_constructible_v<_Seq>)
: _M_seq(std::move(__s))
{ }
using _Sink<_CharT>::out;
_Seq
get() &&
{
if (this->_M_used().size() != 0)
_Seq_sink::_M_overflow();
return std::move(_M_seq);
}
// A writable span that views everything written to the sink.
// Will be either a view over _M_seq or the used part of _M_buf.
span<_CharT>
view()
{
auto __s = this->_M_used();
if (_M_seq.size())
{
if (__s.size() != 0)
_Seq_sink::_M_overflow();
return _M_seq;
}
return __s;
}
};
template<typename _CharT, typename _Alloc = allocator<_CharT>>
using _Str_sink
= _Seq_sink<basic_string<_CharT, char_traits<_CharT>, _Alloc>>;
// template<typename _CharT, typename _Alloc = allocator<_CharT>>
// using _Vec_sink = _Seq_sink<vector<_CharT, _Alloc>>;
// A sink that writes to an output iterator.
// Writes to a fixed-size buffer and then flushes to the output iterator
// when the buffer fills up.
template<typename _CharT, typename _OutIter>
class _Iter_sink : public _Buf_sink<_CharT>
{
_OutIter _M_out;
iter_difference_t<_OutIter> _M_max;
protected:
size_t _M_count = 0;
void
_M_overflow() override
{
auto __s = this->_M_used();
if (_M_max < 0) // No maximum.
_M_out = ranges::copy(__s, std::move(_M_out)).out;
else if (_M_count < static_cast<size_t>(_M_max))
{
auto __max = _M_max - _M_count;
span<_CharT> __first;
if (__max < __s.size())
__first = __s.first(static_cast<size_t>(__max));
else
__first = __s;
_M_out = ranges::copy(__first, std::move(_M_out)).out;
}
this->_M_rewind();
_M_count += __s.size();
}
public:
[[__gnu__::__always_inline__]]
explicit
_Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __max = -1)
: _M_out(std::move(__out)), _M_max(__max)
{ }
using _Sink<_CharT>::out;
format_to_n_result<_OutIter>
_M_finish() &&
{
if (this->_M_used().size() != 0)
_Iter_sink::_M_overflow();
iter_difference_t<_OutIter> __count(_M_count);
return { std::move(_M_out), __count };
}
};
// Partial specialization for contiguous iterators.
// No buffer is used, characters are written straight to the iterator.
// We do not know the size of the output range, so the span size just grows
// as needed. The end of the span might be an invalid pointer outside the
// valid range, but we never actually call _M_span.end(). This class does
// not introduce any invalid pointer arithmetic or overflows that would not
// have happened anyway.
template<typename _CharT, contiguous_iterator _OutIter>
requires same_as<iter_value_t<_OutIter>, _CharT>
class _Iter_sink<_CharT, _OutIter> : public _Sink<_CharT>
{
_OutIter _M_first;
iter_difference_t<_OutIter> _M_max = -1;
protected:
size_t _M_count = 0;
private:
_CharT _M_buf[64]; // Write here after outputting _M_max characters.
protected:
void
_M_overflow() override
{
if (this->_M_unused().size() != 0)
return; // No need to switch to internal buffer yet.
auto __s = this->_M_used();
if (_M_max >= 0)
{
_M_count += __s.size();
// Span was already sized for the maximum character count,
// if it overflows then any further output must go to the
// internal buffer, to be discarded.
this->_M_reset(this->_M_buf);
}
else
{
// No maximum character count. Just extend the span to allow
// writing more characters to it.
this->_M_reset({__s.data(), __s.size() + 1024}, __s.size());
}
}
typename _Sink<_CharT>::_Reservation
_M_reserve(size_t __n) final
{
auto __avail = this->_M_unused();
if (__n > __avail.size())
{
if (_M_max >= 0)
return {}; // cannot grow
auto __s = this->_M_used();
this->_M_reset({__s.data(), __s.size() + __n}, __s.size());
}
return { this };
}
private:
static span<_CharT>
_S_make_span(_CharT* __ptr, iter_difference_t<_OutIter> __n,
span<_CharT> __buf) noexcept
{
if (__n == 0)
return __buf; // Only write to the internal buffer.
if (__n > 0)
{
if constexpr (!is_integral_v<iter_difference_t<_OutIter>>
|| sizeof(__n) > sizeof(size_t))
{
// __int128 or __detail::__max_diff_type
auto __m = iter_difference_t<_OutIter>((size_t)-1);
if (__n > __m)
__n = __m;
}
return {__ptr, (size_t)__n};
}
#if __has_builtin(__builtin_dynamic_object_size)
if (size_t __bytes = __builtin_dynamic_object_size(__ptr, 2))
return {__ptr, __bytes / sizeof(_CharT)};
#endif
// Avoid forming a pointer to a different memory page.
const auto __off = reinterpret_cast<__UINTPTR_TYPE__>(__ptr) % 1024;
__n = (1024 - __off) / sizeof(_CharT);
if (__n > 0) [[likely]]
return {__ptr, static_cast<size_t>(__n)};
else // Misaligned/packed buffer of wchar_t?
return {__ptr, 1};
}
public:
explicit
_Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __n = -1) noexcept
: _Sink<_CharT>(_S_make_span(std::to_address(__out), __n, _M_buf)),
_M_first(__out), _M_max(__n)
{ }
format_to_n_result<_OutIter>
_M_finish() &&
{
auto __s = this->_M_used();
if (__s.data() == _M_buf)
{
// Switched to internal buffer, so must have written _M_max.
iter_difference_t<_OutIter> __count(_M_count + __s.size());
return { _M_first + _M_max, __count };
}
else // Not using internal buffer yet
{
iter_difference_t<_OutIter> __count(__s.size());
return { _M_first + __count, __count };
}
}
};
enum _Arg_t : unsigned char {
_Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull,
_Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle,
_Arg_i128, _Arg_u128,
_Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, // These are unused.
#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
_Arg_next_value_,
_Arg_f128 = _Arg_ldbl,
_Arg_ibm128 = _Arg_next_value_,
#else
_Arg_f128,
#endif
_Arg_max_
};
template<typename _Context>
struct _Arg_value
{
using _CharT = typename _Context::char_type;
struct _HandleBase
{
const void* _M_ptr;
void (*_M_func)();
};
union
{
monostate _M_none;
bool _M_bool;
_CharT _M_c;
int _M_i;
unsigned _M_u;
long long _M_ll;
unsigned long long _M_ull;
float _M_flt;
double _M_dbl;
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // No long double if it's ambiguous.
long double _M_ldbl;
#endif
const _CharT* _M_str;
basic_string_view<_CharT> _M_sv;
const void* _M_ptr;
_HandleBase _M_handle;
#ifdef __SIZEOF_INT128__
__int128 _M_i128;
unsigned __int128 _M_u128;
#endif
#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
__ieee128 _M_f128;
__ibm128 _M_ibm128;
#elif _GLIBCXX_FORMAT_F128 == 2
__float128_t _M_f128;
#endif
};
[[__gnu__::__always_inline__]]
_Arg_value() : _M_none() { }
#if 0
template<typename _Tp>
_Arg_value(in_place_type_t<_Tp>, _Tp __val)
{ _S_get<_Tp>() = __val; }
#endif
template<typename _Tp, typename _Self>
[[__gnu__::__always_inline__]]
static auto&
_S_get(_Self& __u) noexcept
{
if constexpr (is_same_v<_Tp, bool>)
return __u._M_bool;
else if constexpr (is_same_v<_Tp, _CharT>)
return __u._M_c;
else if constexpr (is_same_v<_Tp, int>)
return __u._M_i;
else if constexpr (is_same_v<_Tp, unsigned>)
return __u._M_u;
else if constexpr (is_same_v<_Tp, long long>)
return __u._M_ll;
else if constexpr (is_same_v<_Tp, unsigned long long>)
return __u._M_ull;
else if constexpr (is_same_v<_Tp, float>)
return __u._M_flt;
else if constexpr (is_same_v<_Tp, double>)
return __u._M_dbl;
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
else if constexpr (is_same_v<_Tp, long double>)
return __u._M_ldbl;
#else
else if constexpr (is_same_v<_Tp, __ieee128>)
return __u._M_f128;
else if constexpr (is_same_v<_Tp, __ibm128>)
return __u._M_ibm128;
#endif
else if constexpr (is_same_v<_Tp, const _CharT*>)
return __u._M_str;
else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>)
return __u._M_sv;
else if constexpr (is_same_v<_Tp, const void*>)
return __u._M_ptr;
#ifdef __SIZEOF_INT128__
else if constexpr (is_same_v<_Tp, __int128>)
return __u._M_i128;
else if constexpr (is_same_v<_Tp, unsigned __int128>)
return __u._M_u128;
#endif
#if _GLIBCXX_FORMAT_F128 == 2
else if constexpr (is_same_v<_Tp, __float128_t>)
return __u._M_f128;
#endif
else if constexpr (derived_from<_Tp, _HandleBase>)
return static_cast<_Tp&>(__u._M_handle);
// Otherwise, ill-formed.
}
template<typename _Tp>
[[__gnu__::__always_inline__]]
auto&
_M_get() noexcept
{ return _S_get<_Tp>(*this); }
template<typename _Tp>
[[__gnu__::__always_inline__]]
const auto&
_M_get() const noexcept
{ return _S_get<_Tp>(*this); }
template<typename _Tp>
[[__gnu__::__always_inline__]]
void
_M_set(_Tp __v) noexcept
{
if constexpr (derived_from<_Tp, _HandleBase>)
std::construct_at(&_M_handle, __v);
else
_S_get<_Tp>(*this) = __v;
}
};
// [format.arg.store], class template format-arg-store
template<typename _Context, typename... _Args>
class _Arg_store;
} // namespace __format
/// @endcond
template<typename _Context>
class basic_format_arg
{
using _CharT = typename _Context::char_type;
template<typename _Tp>
static constexpr bool __formattable
= __format::__formattable_with<_Tp, _Context>;
public:
class handle : public __format::_Arg_value<_Context>::_HandleBase
{
using _Base = typename __format::_Arg_value<_Context>::_HandleBase;
// Format as const if possible, to reduce instantiations.
template<typename _Tp>
using __maybe_const_t
= __conditional_t<__formattable<const _Tp>, const _Tp, _Tp>;
template<typename _Tq>
static void
_S_format(basic_format_parse_context<_CharT>& __parse_ctx,
_Context& __format_ctx, const void* __ptr)
{
using _Td = remove_const_t<_Tq>;
typename _Context::template formatter_type<_Td> __f;
__parse_ctx.advance_to(__f.parse(__parse_ctx));
_Tq& __val = *const_cast<_Tq*>(static_cast<const _Td*>(__ptr));
__format_ctx.advance_to(__f.format(__val, __format_ctx));
}
template<typename _Tp>
explicit
handle(_Tp& __val) noexcept
{
this->_M_ptr = __builtin_addressof(__val);
auto __func = _S_format<__maybe_const_t<_Tp>>;
this->_M_func = reinterpret_cast<void(*)()>(__func);
}
friend class basic_format_arg<_Context>;
public:
handle(const handle&) = default;
handle& operator=(const handle&) = default;
[[__gnu__::__always_inline__]]
void
format(basic_format_parse_context<_CharT>& __pc, _Context& __fc) const
{
using _Func = void(*)(basic_format_parse_context<_CharT>&,
_Context&, const void*);
auto __f = reinterpret_cast<_Func>(this->_M_func);
__f(__pc, __fc, this->_M_ptr);
}
};
[[__gnu__::__always_inline__]]
basic_format_arg() noexcept : _M_type(__format::_Arg_none) { }
[[nodiscard,__gnu__::__always_inline__]]
explicit operator bool() const noexcept
{ return _M_type != __format::_Arg_none; }
private:
template<typename _Ctx>
friend class basic_format_args;
template<typename _Ctx, typename... _Args>
friend class __format::_Arg_store;
static_assert(is_trivially_copyable_v<__format::_Arg_value<_Context>>);
__format::_Arg_value<_Context> _M_val;
__format::_Arg_t _M_type;
// Transform incoming argument type to the type stored in _Arg_value.
// e.g. short -> int, std::string -> std::string_view,
// char[3] -> const char*.
template<typename _Tp>
static consteval auto
_S_to_arg_type()
{
using _Td = remove_const_t<_Tp>;
if constexpr (is_same_v<_Td, bool>)
return type_identity<bool>();
else if constexpr (is_same_v<_Td, _CharT>)
return type_identity<_CharT>();
else if constexpr (is_same_v<_Td, char> && is_same_v<_CharT, wchar_t>)
return type_identity<_CharT>();
#ifdef __SIZEOF_INT128__ // Check before signed/unsigned integer
else if constexpr (is_same_v<_Td, __int128>)
return type_identity<__int128>();
else if constexpr (is_same_v<_Td, unsigned __int128>)
return type_identity<unsigned __int128>();
#endif
else if constexpr (__is_signed_integer<_Td>::value)
{
if constexpr (sizeof(_Td) <= sizeof(int))
return type_identity<int>();
else if constexpr (sizeof(_Td) <= sizeof(long long))
return type_identity<long long>();
}
else if constexpr (__is_unsigned_integer<_Td>::value)
{
if constexpr (sizeof(_Td) <= sizeof(unsigned))
return type_identity<unsigned>();
else if constexpr (sizeof(_Td) <= sizeof(unsigned long long))
return type_identity<unsigned long long>();
}
else if constexpr (is_same_v<_Td, float>)
return type_identity<float>();
else if constexpr (is_same_v<_Td, double>)
return type_identity<double>();
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
else if constexpr (is_same_v<_Td, long double>)
return type_identity<long double>();
#else
else if constexpr (is_same_v<_Td, __ibm128>)
return type_identity<__ibm128>();
else if constexpr (is_same_v<_Td, __ieee128>)
return type_identity<__ieee128>();
#endif
#if defined(__FLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Td, _Float16>)
return type_identity<float>();
#endif
#if defined(__BFLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Td, decltype(0.0bf16)>)
return type_identity<float>();
#endif
#ifdef __FLT32_DIG__
else if constexpr (is_same_v<_Td, _Float32>)
# ifdef _GLIBCXX_FLOAT_IS_IEEE_BINARY32
return type_identity<float>();
# else
return type_identity<_Float32>();
# endif
#endif
#ifdef __FLT64_DIG__
else if constexpr (is_same_v<_Td, _Float64>)
# ifdef _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
return type_identity<double>();
# else
return type_identity<_Float64>();
# endif
#endif
#if _GLIBCXX_FORMAT_F128
# if __FLT128_DIG__
else if constexpr (is_same_v<_Td, _Float128>)
return type_identity<__format::__float128_t>();
# endif
# if __SIZEOF_FLOAT128__
else if constexpr (is_same_v<_Td, __float128>)
return type_identity<__format::__float128_t>();
# endif
#endif
else if constexpr (__is_specialization_of<_Td, basic_string_view>
|| __is_specialization_of<_Td, basic_string>)
{
if constexpr (is_same_v<typename _Td::value_type, _CharT>)
return type_identity<basic_string_view<_CharT>>();
else
return type_identity<handle>();
}
else if constexpr (is_same_v<decay_t<_Td>, const _CharT*>)
return type_identity<const _CharT*>();
else if constexpr (is_same_v<decay_t<_Td>, _CharT*>)
return type_identity<const _CharT*>();
else if constexpr (is_void_v<remove_pointer_t<_Td>>)
return type_identity<const void*>();
else if constexpr (is_same_v<_Td, nullptr_t>)
return type_identity<const void*>();
else
return type_identity<handle>();
}
// Transform a formattable type to the appropriate storage type.
template<typename _Tp>
using _Normalize = typename decltype(_S_to_arg_type<_Tp>())::type;
// Get the _Arg_t value corresponding to a normalized type.
template<typename _Tp>
static consteval __format::_Arg_t
_S_to_enum()
{
using namespace __format;
if constexpr (is_same_v<_Tp, bool>)
return _Arg_bool;
else if constexpr (is_same_v<_Tp, _CharT>)
return _Arg_c;
else if constexpr (is_same_v<_Tp, int>)
return _Arg_i;
else if constexpr (is_same_v<_Tp, unsigned>)
return _Arg_u;
else if constexpr (is_same_v<_Tp, long long>)
return _Arg_ll;
else if constexpr (is_same_v<_Tp, unsigned long long>)
return _Arg_ull;
else if constexpr (is_same_v<_Tp, float>)
return _Arg_flt;
else if constexpr (is_same_v<_Tp, double>)
return _Arg_dbl;
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
else if constexpr (is_same_v<_Tp, long double>)
return _Arg_ldbl;
#else
// Don't use _Arg_ldbl for this target, it's ambiguous.
else if constexpr (is_same_v<_Tp, __ibm128>)
return _Arg_ibm128;
else if constexpr (is_same_v<_Tp, __ieee128>)
return _Arg_f128;
#endif
else if constexpr (is_same_v<_Tp, const _CharT*>)
return _Arg_str;
else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>)
return _Arg_sv;
else if constexpr (is_same_v<_Tp, const void*>)
return _Arg_ptr;
#ifdef __SIZEOF_INT128__
else if constexpr (is_same_v<_Tp, __int128>)
return _Arg_i128;
else if constexpr (is_same_v<_Tp, unsigned __int128>)
return _Arg_u128;
#endif
// N.B. some of these types will never actually be used here,
// because they get normalized to a standard floating-point type.
#if defined __FLT32_DIG__ && ! _GLIBCXX_FLOAT_IS_IEEE_BINARY32
else if constexpr (is_same_v<_Tp, _Float32>)
return _Arg_f32;
#endif
#if defined __FLT64_DIG__ && ! _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
else if constexpr (is_same_v<_Tp, _Float64>)
return _Arg_f64;
#endif
#if _GLIBCXX_FORMAT_F128 == 2
else if constexpr (is_same_v<_Tp, __format::__float128_t>)
return _Arg_f128;
#endif
else if constexpr (is_same_v<_Tp, handle>)
return _Arg_handle;
}
template<typename _Tp>
void
_M_set(_Tp __v) noexcept
{
_M_type = _S_to_enum<_Tp>();
_M_val._M_set(__v);
}
template<typename _Tp>
requires __format::__formattable_with<_Tp, _Context>
explicit
basic_format_arg(_Tp& __v) noexcept
{
using _Td = _Normalize<_Tp>;
if constexpr (is_same_v<_Td, basic_string_view<_CharT>>)
_M_set(_Td{__v.data(), __v.size()});
else if constexpr (is_same_v<remove_const_t<_Tp>, char>
&& is_same_v<_CharT, wchar_t>)
_M_set(static_cast<_Td>(static_cast<unsigned char>(__v)));
else
_M_set(static_cast<_Td>(__v));
}
template<typename _Ctx, typename... _Argz>
friend auto
make_format_args(_Argz&...) noexcept;
template<typename _Visitor, typename _Ctx>
friend decltype(auto)
visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>);
template<typename _Visitor>
decltype(auto)
_M_visit(_Visitor&& __vis, __format::_Arg_t __type)
{
using namespace __format;
switch (__type)
{
case _Arg_none:
return std::forward<_Visitor>(__vis)(_M_val._M_none);
case _Arg_bool:
return std::forward<_Visitor>(__vis)(_M_val._M_bool);
case _Arg_c:
return std::forward<_Visitor>(__vis)(_M_val._M_c);
case _Arg_i:
return std::forward<_Visitor>(__vis)(_M_val._M_i);
case _Arg_u:
return std::forward<_Visitor>(__vis)(_M_val._M_u);
case _Arg_ll:
return std::forward<_Visitor>(__vis)(_M_val._M_ll);
case _Arg_ull:
return std::forward<_Visitor>(__vis)(_M_val._M_ull);
#if __glibcxx_to_chars // FIXME: need to be able to format these types!
case _Arg_flt:
return std::forward<_Visitor>(__vis)(_M_val._M_flt);
case _Arg_dbl:
return std::forward<_Visitor>(__vis)(_M_val._M_dbl);
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
case _Arg_ldbl:
return std::forward<_Visitor>(__vis)(_M_val._M_ldbl);
#else
case _Arg_f128:
return std::forward<_Visitor>(__vis)(_M_val._M_f128);
case _Arg_ibm128:
return std::forward<_Visitor>(__vis)(_M_val._M_ibm128);
#endif
#endif
case _Arg_str:
return std::forward<_Visitor>(__vis)(_M_val._M_str);
case _Arg_sv:
return std::forward<_Visitor>(__vis)(_M_val._M_sv);
case _Arg_ptr:
return std::forward<_Visitor>(__vis)(_M_val._M_ptr);
case _Arg_handle:
{
auto& __h = static_cast<handle&>(_M_val._M_handle);
return std::forward<_Visitor>(__vis)(__h);
}
#ifdef __SIZEOF_INT128__
case _Arg_i128:
return std::forward<_Visitor>(__vis)(_M_val._M_i128);
case _Arg_u128:
return std::forward<_Visitor>(__vis)(_M_val._M_u128);
#endif
#if _GLIBCXX_FORMAT_F128 == 2
case _Arg_f128:
return std::forward<_Visitor>(__vis)(_M_val._M_f128);
#endif
default:
// _Arg_f16 etc.
__builtin_unreachable();
}
}
};
template<typename _Visitor, typename _Context>
inline decltype(auto)
visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg)
{
return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
}
/// @cond undocumented
namespace __format
{
struct _WidthPrecVisitor
{
template<typename _Tp>
size_t
operator()(_Tp& __arg) const
{
if constexpr (is_same_v<_Tp, monostate>)
__format::__invalid_arg_id_in_format_string();
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3720. Restrict the valid types of arg-id for width and precision
// 3721. Allow an arg-id with a value of zero for width
else if constexpr (sizeof(_Tp) <= sizeof(long long))
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3720. Restrict the valid types of arg-id for width and precision
if constexpr (__is_unsigned_integer<_Tp>::value)
return __arg;
else if constexpr (__is_signed_integer<_Tp>::value)
if (__arg >= 0)
return __arg;
}
__throw_format_error("format error: argument used for width or "
"precision must be a non-negative integer");
}
};
template<typename _Context>
inline size_t
__int_from_arg(const basic_format_arg<_Context>& __arg)
{ return std::visit_format_arg(_WidthPrecVisitor(), __arg); }
// Pack _Arg_t enum values into a single 60-bit integer.
template<int _Bits, size_t _Nm>
constexpr auto
__pack_arg_types(const array<_Arg_t, _Nm>& __types)
{
__UINT64_TYPE__ __packed_types = 0;
for (auto __i = __types.rbegin(); __i != __types.rend(); ++__i)
__packed_types = (__packed_types << _Bits) | *__i;
return __packed_types;
}
} // namespace __format
/// @endcond
template<typename _Context>
class basic_format_args
{
static constexpr int _S_packed_type_bits = 5; // _Arg_t values [0,20]
static constexpr int _S_packed_type_mask = 0b11111;
static constexpr int _S_max_packed_args = 12;
static_assert( __format::_Arg_max_ <= (1 << _S_packed_type_bits) );
template<typename... _Args>
using _Store = __format::_Arg_store<_Context, _Args...>;
template<typename _Ctx, typename... _Args>
friend class __format::_Arg_store;
using uint64_t = __UINT64_TYPE__;
using _Format_arg = basic_format_arg<_Context>;
using _Format_arg_val = __format::_Arg_value<_Context>;
// If args are packed then the number of args is in _M_packed_size and
// the packed types are in _M_unpacked_size, accessed via _M_type(i).
// If args are not packed then the number of args is in _M_unpacked_size
// and _M_packed_size is zero.
uint64_t _M_packed_size : 4;
uint64_t _M_unpacked_size : 60;
union {
const _Format_arg_val* _M_values; // Active when _M_packed_size != 0
const _Format_arg* _M_args; // Active when _M_packed_size == 0
};
size_t
_M_size() const noexcept
{ return _M_packed_size ? _M_packed_size : _M_unpacked_size; }
typename __format::_Arg_t
_M_type(size_t __i) const noexcept
{
uint64_t __t = _M_unpacked_size >> (__i * _S_packed_type_bits);
return static_cast<__format::_Arg_t>(__t & _S_packed_type_mask);
}
template<typename _Ctx, typename... _Args>
friend auto
make_format_args(_Args&...) noexcept;
// An array of _Arg_t enums corresponding to _Args...
template<typename... _Args>
static consteval array<__format::_Arg_t, sizeof...(_Args)>
_S_types_to_pack()
{ return {_Format_arg::template _S_to_enum<_Args>()...}; }
public:
template<typename... _Args>
basic_format_args(const _Store<_Args...>& __store) noexcept;
[[nodiscard,__gnu__::__always_inline__]]
basic_format_arg<_Context>
get(size_t __i) const noexcept
{
basic_format_arg<_Context> __arg;
if (__i < _M_packed_size)
{
__arg._M_type = _M_type(__i);
__arg._M_val = _M_values[__i];
}
else if (_M_packed_size == 0 && __i < _M_unpacked_size)
__arg = _M_args[__i];
return __arg;
}
};
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3810. CTAD for std::basic_format_args
template<typename _Context, typename... _Args>
basic_format_args(__format::_Arg_store<_Context, _Args...>)
-> basic_format_args<_Context>;
template<typename _Context, typename... _Args>
auto
make_format_args(_Args&... __fmt_args) noexcept;
// An array of type-erased formatting arguments.
template<typename _Context, typename... _Args>
class __format::_Arg_store
{
friend std::basic_format_args<_Context>;
template<typename _Ctx, typename... _Argz>
friend auto std::
#if _GLIBCXX_INLINE_VERSION
__8:: // Needed for PR c++/59256
#endif
make_format_args(_Argz&...) noexcept;
// For a sufficiently small number of arguments we only store values.
// basic_format_args can get the types from the _Args pack.
static constexpr bool _S_values_only
= sizeof...(_Args) <= basic_format_args<_Context>::_S_max_packed_args;
using _Element_t
= __conditional_t<_S_values_only,
__format::_Arg_value<_Context>,
basic_format_arg<_Context>>;
_Element_t _M_args[sizeof...(_Args)];
template<typename _Tp>
static _Element_t
_S_make_elt(_Tp& __v)
{
using _Tq = remove_const_t<_Tp>;
using _CharT = typename _Context::char_type;
static_assert(is_default_constructible_v<formatter<_Tq, _CharT>>,
"std::formatter must be specialized for the type "
"of each format arg");
using __format::__formattable_with;
if constexpr (is_const_v<_Tp>)
if constexpr (!__formattable_with<_Tp, _Context>)
if constexpr (__formattable_with<_Tq, _Context>)
static_assert(__formattable_with<_Tp, _Context>,
"format arg must be non-const because its "
"std::formatter specialization has a "
"non-const reference parameter");
basic_format_arg<_Context> __arg(__v);
if constexpr (_S_values_only)
return __arg._M_val;
else
return __arg;
}
template<typename... _Tp>
requires (sizeof...(_Tp) == sizeof...(_Args))
[[__gnu__::__always_inline__]]
_Arg_store(_Tp&... __a) noexcept
: _M_args{_S_make_elt(__a)...}
{ }
};
template<typename _Context>
class __format::_Arg_store<_Context>
{ };
template<typename _Context>
template<typename... _Args>
inline
basic_format_args<_Context>::
basic_format_args(const _Store<_Args...>& __store) noexcept
{
if constexpr (sizeof...(_Args) == 0)
{
_M_packed_size = 0;
_M_unpacked_size = 0;
_M_args = nullptr;
}
else if constexpr (sizeof...(_Args) <= _S_max_packed_args)
{
// The number of packed arguments:
_M_packed_size = sizeof...(_Args);
// The packed type enums:
_M_unpacked_size
= __format::__pack_arg_types<_S_packed_type_bits>(_S_types_to_pack<_Args...>());
// The _Arg_value objects.
_M_values = __store._M_args;
}
else
{
// No packed arguments:
_M_packed_size = 0;
// The number of unpacked arguments:
_M_unpacked_size = sizeof...(_Args);
// The basic_format_arg objects:
_M_args = __store._M_args;
}
}
/// Capture formatting arguments for use by `std::vformat`.
template<typename _Context = format_context, typename... _Args>
[[nodiscard,__gnu__::__always_inline__]]
inline auto
make_format_args(_Args&... __fmt_args) noexcept
{
using _Fmt_arg = basic_format_arg<_Context>;
using _Store = __format::_Arg_store<_Context, typename _Fmt_arg::template
_Normalize<_Args>...>;
return _Store(__fmt_args...);
}
#ifdef _GLIBCXX_USE_WCHAR_T
/// Capture formatting arguments for use by `std::vformat` (for wide output).
template<typename... _Args>
[[nodiscard,__gnu__::__always_inline__]]
inline auto
make_wformat_args(_Args&... __args) noexcept
{ return std::make_format_args<wformat_context>(__args...); }
#endif
/// @cond undocumented
namespace __format
{
template<typename _Out, typename _CharT, typename _Context>
_Out
__do_vformat_to(_Out, basic_string_view<_CharT>,
const basic_format_args<_Context>&,
const locale* = nullptr);
template<typename _CharT> struct __formatter_chrono;
} // namespace __format
/// @endcond
/** Context for std::format and similar functions.
*
* A formatting context contains an output iterator and locale to use
* for the formatting operations. Most programs will never need to use
* this class template explicitly. For typical uses of `std::format` the
* library will use the specializations `std::format_context` (for `char`)
* and `std::wformat_context` (for `wchar_t`).
*
* You are not allowed to define partial or explicit specializations of
* this class template.
*
* @since C++20
*/
template<typename _Out, typename _CharT>
class basic_format_context
{
static_assert( output_iterator<_Out, const _CharT&> );
basic_format_args<basic_format_context> _M_args;
_Out _M_out;
__format::_Optional_locale _M_loc;
basic_format_context(basic_format_args<basic_format_context> __args,
_Out __out)
: _M_args(__args), _M_out(std::move(__out))
{ }
basic_format_context(basic_format_args<basic_format_context> __args,
_Out __out, const std::locale& __loc)
: _M_args(__args), _M_out(std::move(__out)), _M_loc(__loc)
{ }
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4061. Should std::basic_format_context be
// default-constructible/copyable/movable?
basic_format_context(const basic_format_context&) = delete;
basic_format_context& operator=(const basic_format_context&) = delete;
template<typename _Out2, typename _CharT2, typename _Context2>
friend _Out2
__format::__do_vformat_to(_Out2, basic_string_view<_CharT2>,
const basic_format_args<_Context2>&,
const locale*);
friend __format::__formatter_chrono<_CharT>;
public:
~basic_format_context() = default;
using iterator = _Out;
using char_type = _CharT;
template<typename _Tp>
using formatter_type = formatter<_Tp, _CharT>;
[[nodiscard]]
basic_format_arg<basic_format_context>
arg(size_t __id) const noexcept
{ return _M_args.get(__id); }
[[nodiscard]]
std::locale locale() { return _M_loc.value(); }
[[nodiscard]]
iterator out() { return std::move(_M_out); }
void advance_to(iterator __it) { _M_out = std::move(__it); }
};
/// @cond undocumented
namespace __format
{
// Abstract base class defining an interface for scanning format strings.
// Scan the characters in a format string, dividing it up into strings of
// ordinary characters, escape sequences, and replacement fields.
// Call virtual functions for derived classes to parse format-specifiers
// or write formatted output.
template<typename _CharT>
struct _Scanner
{
using iterator = typename basic_format_parse_context<_CharT>::iterator;
basic_format_parse_context<_CharT> _M_pc;
constexpr explicit
_Scanner(basic_string_view<_CharT> __str, size_t __nargs = -1)
: _M_pc(__str, __nargs)
{ }
constexpr iterator begin() const noexcept { return _M_pc.begin(); }
constexpr iterator end() const noexcept { return _M_pc.end(); }
constexpr void
_M_scan()
{
basic_string_view<_CharT> __fmt = _M_fmt_str();
if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
{
_M_pc.advance_to(begin() + 1);
_M_format_arg(_M_pc.next_arg_id());
return;
}
size_t __lbr = __fmt.find('{');
size_t __rbr = __fmt.find('}');
while (__fmt.size())
{
auto __cmp = __lbr <=> __rbr;
if (__cmp == 0)
{
_M_on_chars(end());
_M_pc.advance_to(end());
return;
}
else if (__cmp < 0)
{
if (__lbr + 1 == __fmt.size()
|| (__rbr == __fmt.npos && __fmt[__lbr + 1] != '{'))
__format::__unmatched_left_brace_in_format_string();
const bool __is_escape = __fmt[__lbr + 1] == '{';
iterator __last = begin() + __lbr + int(__is_escape);
_M_on_chars(__last);
_M_pc.advance_to(__last + 1);
__fmt = _M_fmt_str();
if (__is_escape)
{
if (__rbr != __fmt.npos)
__rbr -= __lbr + 2;
__lbr = __fmt.find('{');
}
else
{
_M_on_replacement_field();
__fmt = _M_fmt_str();
__lbr = __fmt.find('{');
__rbr = __fmt.find('}');
}
}
else
{
if (++__rbr == __fmt.size() || __fmt[__rbr] != '}')
__format::__unmatched_right_brace_in_format_string();
iterator __last = begin() + __rbr;
_M_on_chars(__last);
_M_pc.advance_to(__last + 1);
__fmt = _M_fmt_str();
if (__lbr != __fmt.npos)
__lbr -= __rbr + 1;
__rbr = __fmt.find('}');
}
}
}
constexpr basic_string_view<_CharT>
_M_fmt_str() const noexcept
{ return {begin(), end()}; }
constexpr virtual void _M_on_chars(iterator) { }
constexpr void _M_on_replacement_field()
{
auto __next = begin();
size_t __id;
if (*__next == '}')
__id = _M_pc.next_arg_id();
else if (*__next == ':')
{
__id = _M_pc.next_arg_id();
_M_pc.advance_to(++__next);
}
else
{
auto [__i, __ptr] = __format::__parse_arg_id(begin(), end());
if (!__ptr || !(*__ptr == '}' || *__ptr == ':'))
__format::__invalid_arg_id_in_format_string();
_M_pc.check_arg_id(__id = __i);
if (*__ptr == ':')
{
_M_pc.advance_to(++__ptr);
}
else
_M_pc.advance_to(__ptr);
}
_M_format_arg(__id);
if (begin() == end() || *begin() != '}')
__format::__unmatched_left_brace_in_format_string();
_M_pc.advance_to(begin() + 1); // Move past '}'
}
constexpr virtual void _M_format_arg(size_t __id) = 0;
};
// Process a format string and format the arguments in the context.
template<typename _Out, typename _CharT>
class _Formatting_scanner : public _Scanner<_CharT>
{
public:
_Formatting_scanner(basic_format_context<_Out, _CharT>& __fc,
basic_string_view<_CharT> __str)
: _Scanner<_CharT>(__str), _M_fc(__fc)
{ }
private:
basic_format_context<_Out, _CharT>& _M_fc;
using iterator = typename _Scanner<_CharT>::iterator;
constexpr void
_M_on_chars(iterator __last) override
{
basic_string_view<_CharT> __str(this->begin(), __last);
_M_fc.advance_to(__format::__write(_M_fc.out(), __str));
}
constexpr void
_M_format_arg(size_t __id) override
{
using _Context = basic_format_context<_Out, _CharT>;
using handle = typename basic_format_arg<_Context>::handle;
std::visit_format_arg([this](auto& __arg) {
using _Type = remove_reference_t<decltype(__arg)>;
using _Formatter = typename _Context::template formatter_type<_Type>;
if constexpr (is_same_v<_Type, monostate>)
__format::__invalid_arg_id_in_format_string();
else if constexpr (is_same_v<_Type, handle>)
__arg.format(this->_M_pc, this->_M_fc);
else if constexpr (is_default_constructible_v<_Formatter>)
{
_Formatter __f;
this->_M_pc.advance_to(__f.parse(this->_M_pc));
this->_M_fc.advance_to(__f.format(__arg, this->_M_fc));
}
else
static_assert(__format::__formattable_with<_Type, _Context>);
}, _M_fc.arg(__id));
}
};
// Validate a format string for Args.
template<typename _CharT, typename... _Args>
class _Checking_scanner : public _Scanner<_CharT>
{
static_assert(
(is_default_constructible_v<formatter<_Args, _CharT>> && ...),
"std::formatter must be specialized for each type being formatted");
public:
constexpr
_Checking_scanner(basic_string_view<_CharT> __str)
: _Scanner<_CharT>(__str, sizeof...(_Args))
{ }
private:
constexpr void
_M_format_arg(size_t __id) override
{
if constexpr (sizeof...(_Args) != 0)
{
if (__id < sizeof...(_Args))
{
_M_parse_format_spec<_Args...>(__id);
return;
}
}
__builtin_unreachable();
}
template<typename _Tp, typename... _OtherArgs>
constexpr void
_M_parse_format_spec(size_t __id)
{
if (__id == 0)
{
formatter<_Tp, _CharT> __f;
this->_M_pc.advance_to(__f.parse(this->_M_pc));
}
else if constexpr (sizeof...(_OtherArgs) != 0)
_M_parse_format_spec<_OtherArgs...>(__id - 1);
else
__builtin_unreachable();
}
};
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)
{
_Iter_sink<_CharT, _Out> __sink(std::move(__out));
_Sink_iter<_CharT> __sink_out;
if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
__sink_out = __out; // Already a sink iterator, safe to use post-move.
else
__sink_out = __sink.out();
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;
std::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 = __sink_out._M_reserve(__len))
{
__builtin_memcpy(__res.get(), __chars[__arg], __len);
__res._M_bump(__len);
__done = true;
}
}
else if constexpr (is_same_v<_Tp, char>)
{
if (auto __res = __sink_out._M_reserve(1))
{
*__res.get() = __arg;
__res._M_bump(1);
__done = true;
}
}
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 = __sink_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;
}
}
else if constexpr (is_convertible_v<_Tp, string_view>)
{
string_view __sv = __arg;
if (auto __res = __sink_out._M_reserve(__sv.size()))
{
__builtin_memcpy(__res.get(), __sv.data(), __sv.size());
__res._M_bump(__sv.size());
__done = true;
}
}
}, __args.get(0));
if (__done)
{
if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
return __sink_out;
else
return std::move(__sink)._M_finish().out;
}
}
auto __ctx = __loc == nullptr
? _Context(__args, __sink_out)
: _Context(__args, __sink_out, *__loc);
_Formatting_scanner<_Sink_iter<_CharT>, _CharT> __scanner(__ctx, __fmt);
__scanner._M_scan();
if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
return __ctx.out();
else
return std::move(__sink)._M_finish().out;
}
} // namespace __format
/// @endcond
template<typename _CharT, typename... _Args>
template<typename _Tp>
requires convertible_to<const _Tp&, basic_string_view<_CharT>>
consteval
basic_format_string<_CharT, _Args...>::
basic_format_string(const _Tp& __s)
: _M_str(__s)
{
__format::_Checking_scanner<_CharT, remove_cvref_t<_Args>...>
__scanner(_M_str);
__scanner._M_scan();
}
// [format.functions], formatting functions
template<typename _Out> requires output_iterator<_Out, const char&>
[[__gnu__::__always_inline__]]
inline _Out
vformat_to(_Out __out, string_view __fmt, format_args __args)
{ return __format::__do_vformat_to(std::move(__out), __fmt, __args); }
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Out> requires output_iterator<_Out, const wchar_t&>
[[__gnu__::__always_inline__]]
inline _Out
vformat_to(_Out __out, wstring_view __fmt, wformat_args __args)
{ return __format::__do_vformat_to(std::move(__out), __fmt, __args); }
#endif
template<typename _Out> requires output_iterator<_Out, const char&>
[[__gnu__::__always_inline__]]
inline _Out
vformat_to(_Out __out, const locale& __loc, string_view __fmt,
format_args __args)
{
return __format::__do_vformat_to(std::move(__out), __fmt, __args, &__loc);
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Out> requires output_iterator<_Out, const wchar_t&>
[[__gnu__::__always_inline__]]
inline _Out
vformat_to(_Out __out, const locale& __loc, wstring_view __fmt,
wformat_args __args)
{
return __format::__do_vformat_to(std::move(__out), __fmt, __args, &__loc);
}
#endif
[[nodiscard]]
inline string
vformat(string_view __fmt, format_args __args)
{
__format::_Str_sink<char> __buf;
std::vformat_to(__buf.out(), __fmt, __args);
return std::move(__buf).get();
}
#ifdef _GLIBCXX_USE_WCHAR_T
[[nodiscard]]
inline wstring
vformat(wstring_view __fmt, wformat_args __args)
{
__format::_Str_sink<wchar_t> __buf;
std::vformat_to(__buf.out(), __fmt, __args);
return std::move(__buf).get();
}
#endif
[[nodiscard]]
inline string
vformat(const locale& __loc, string_view __fmt, format_args __args)
{
__format::_Str_sink<char> __buf;
std::vformat_to(__buf.out(), __loc, __fmt, __args);
return std::move(__buf).get();
}
#ifdef _GLIBCXX_USE_WCHAR_T
[[nodiscard]]
inline wstring
vformat(const locale& __loc, wstring_view __fmt, wformat_args __args)
{
__format::_Str_sink<wchar_t> __buf;
std::vformat_to(__buf.out(), __loc, __fmt, __args);
return std::move(__buf).get();
}
#endif
template<typename... _Args>
[[nodiscard]]
inline string
format(format_string<_Args...> __fmt, _Args&&... __args)
{ return std::vformat(__fmt.get(), std::make_format_args(__args...)); }
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename... _Args>
[[nodiscard]]
inline wstring
format(wformat_string<_Args...> __fmt, _Args&&... __args)
{ return std::vformat(__fmt.get(), std::make_wformat_args(__args...)); }
#endif
template<typename... _Args>
[[nodiscard]]
inline string
format(const locale& __loc, format_string<_Args...> __fmt,
_Args&&... __args)
{
return std::vformat(__loc, __fmt.get(),
std::make_format_args(__args...));
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename... _Args>
[[nodiscard]]
inline wstring
format(const locale& __loc, wformat_string<_Args...> __fmt,
_Args&&... __args)
{
return std::vformat(__loc, __fmt.get(),
std::make_wformat_args(__args...));
}
#endif
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const char&>
inline _Out
format_to(_Out __out, format_string<_Args...> __fmt, _Args&&... __args)
{
return std::vformat_to(std::move(__out), __fmt.get(),
std::make_format_args(__args...));
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const wchar_t&>
inline _Out
format_to(_Out __out, wformat_string<_Args...> __fmt, _Args&&... __args)
{
return std::vformat_to(std::move(__out), __fmt.get(),
std::make_wformat_args(__args...));
}
#endif
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const char&>
inline _Out
format_to(_Out __out, const locale& __loc, format_string<_Args...> __fmt,
_Args&&... __args)
{
return std::vformat_to(std::move(__out), __loc, __fmt.get(),
std::make_format_args(__args...));
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const wchar_t&>
inline _Out
format_to(_Out __out, const locale& __loc, wformat_string<_Args...> __fmt,
_Args&&... __args)
{
return std::vformat_to(std::move(__out), __loc, __fmt.get(),
std::make_wformat_args(__args...));
}
#endif
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const char&>
inline format_to_n_result<_Out>
format_to_n(_Out __out, iter_difference_t<_Out> __n,
format_string<_Args...> __fmt, _Args&&... __args)
{
__format::_Iter_sink<char, _Out> __sink(std::move(__out), __n);
std::vformat_to(__sink.out(), __fmt.get(),
std::make_format_args(__args...));
return std::move(__sink)._M_finish();
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const wchar_t&>
inline format_to_n_result<_Out>
format_to_n(_Out __out, iter_difference_t<_Out> __n,
wformat_string<_Args...> __fmt, _Args&&... __args)
{
__format::_Iter_sink<wchar_t, _Out> __sink(std::move(__out), __n);
std::vformat_to(__sink.out(), __fmt.get(),
std::make_wformat_args(__args...));
return std::move(__sink)._M_finish();
}
#endif
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const char&>
inline format_to_n_result<_Out>
format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc,
format_string<_Args...> __fmt, _Args&&... __args)
{
__format::_Iter_sink<char, _Out> __sink(std::move(__out), __n);
std::vformat_to(__sink.out(), __loc, __fmt.get(),
std::make_format_args(__args...));
return std::move(__sink)._M_finish();
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename _Out, typename... _Args>
requires output_iterator<_Out, const wchar_t&>
inline format_to_n_result<_Out>
format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc,
wformat_string<_Args...> __fmt, _Args&&... __args)
{
__format::_Iter_sink<wchar_t, _Out> __sink(std::move(__out), __n);
std::vformat_to(__sink.out(), __loc, __fmt.get(),
std::make_wformat_args(__args...));
return std::move(__sink)._M_finish();
}
#endif
/// @cond undocumented
namespace __format
{
#if 1
template<typename _CharT>
class _Counting_sink final : public _Iter_sink<_CharT, _CharT*>
{
public:
_Counting_sink() : _Iter_sink<_CharT, _CharT*>(nullptr, 0) { }
[[__gnu__::__always_inline__]]
size_t
count() const
{ return this->_M_count + this->_M_used().size(); }
};
#else
template<typename _CharT>
class _Counting_sink : public _Buf_sink<_CharT>
{
size_t _M_count = 0;
void
_M_overflow() override
{
if (!std::is_constant_evaluated())
_M_count += this->_M_used().size();
this->_M_rewind();
}
public:
_Counting_sink() = default;
[[__gnu__::__always_inline__]]
size_t
count() noexcept
{
_Counting_sink::_M_overflow();
return _M_count;
}
};
#endif
} // namespace __format
/// @endcond
template<typename... _Args>
[[nodiscard]]
inline size_t
formatted_size(format_string<_Args...> __fmt, _Args&&... __args)
{
__format::_Counting_sink<char> __buf;
std::vformat_to(__buf.out(), __fmt.get(),
std::make_format_args(__args...));
return __buf.count();
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename... _Args>
[[nodiscard]]
inline size_t
formatted_size(wformat_string<_Args...> __fmt, _Args&&... __args)
{
__format::_Counting_sink<wchar_t> __buf;
std::vformat_to(__buf.out(), __fmt.get(),
std::make_wformat_args(__args...));
return __buf.count();
}
#endif
template<typename... _Args>
[[nodiscard]]
inline size_t
formatted_size(const locale& __loc, format_string<_Args...> __fmt,
_Args&&... __args)
{
__format::_Counting_sink<char> __buf;
std::vformat_to(__buf.out(), __loc, __fmt.get(),
std::make_format_args(__args...));
return __buf.count();
}
#ifdef _GLIBCXX_USE_WCHAR_T
template<typename... _Args>
[[nodiscard]]
inline size_t
formatted_size(const locale& __loc, wformat_string<_Args...> __fmt,
_Args&&... __args)
{
__format::_Counting_sink<wchar_t> __buf;
std::vformat_to(__buf.out(), __loc, __fmt.get(),
std::make_wformat_args(__args...));
return __buf.count();
}
#endif
#if __cpp_lib_format_ranges
// [format.range], formatting of ranges
// [format.range.fmtkind], variable template format_kind
enum class range_format {
disabled,
map,
set,
sequence,
string,
debug_string
};
/// @cond undocumented
template<typename _Rg>
constexpr auto format_kind = not defined(format_kind<_Rg>);
template<typename _Tp>
consteval range_format
__fmt_kind()
{
using _Ref = ranges::range_reference_t<_Tp>;
if constexpr (is_same_v<remove_cvref_t<_Ref>, _Tp>)
return range_format::disabled;
else if constexpr (requires { typename _Tp::key_type; })
{
if constexpr (requires { typename _Tp::mapped_type; })
{
using _Up = remove_cvref_t<_Ref>;
if constexpr (__is_pair<_Up>)
return range_format::map;
else if constexpr (__is_specialization_of<_Up, tuple>)
if constexpr (tuple_size_v<_Up> == 2)
return range_format::map;
}
return range_format::set;
}
else
return range_format::sequence;
}
/// @endcond
/// A constant determining how a range should be formatted.
template<ranges::input_range _Rg> requires same_as<_Rg, remove_cvref_t<_Rg>>
constexpr range_format format_kind<_Rg> = __fmt_kind<_Rg>();
// [format.range.formatter], class template range_formatter
template<typename _Tp, typename _CharT = char>
requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT>
class range_formatter; // TODO
/// @cond undocumented
namespace __format
{
// [format.range.fmtdef], class template range-default-formatter
template<range_format _Kind, ranges::input_range _Rg, typename _CharT>
struct __range_default_formatter; // TODO
} // namespace __format
/// @endcond
// [format.range.fmtmap], [format.range.fmtset], [format.range.fmtstr],
// specializations for maps, sets, and strings
template<ranges::input_range _Rg, typename _CharT>
requires (format_kind<_Rg> != range_format::disabled)
&& formattable<ranges::range_reference_t<_Rg>, _CharT>
struct formatter<_Rg, _CharT>
: __format::__range_default_formatter<format_kind<_Rg>, _Rg, _CharT>
{ };
#endif // C++23 formatting ranges
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // __cpp_lib_format
#endif // _GLIBCXX_FORMAT