Files
gcc/libstdc++-v3/include/std/format
Jakub Jelinek 62c126db6b libstdc++: Implement C++26 P3378R2 - constexpr exception types
The following patch attempts to implement the C++26 P3378R2 - constexpr
exception types paper.

This is quite complicated, because most of these classes which should
be constexpr-ized use solely or mostly out of line definitions in
libstdc++, both for historical, code size and dual ABI reasons, so that
one can throw these as exceptions between TUs with old vs. new (or vice
versa) ABIs.
For this reason, logic_error/runtime_error and classes derived from it
have the old ABI std::string object inside of them and the exported
APIs from libstdc++.so.6 ensure the right thing.

Now, because new invoked during constant evaluation needs to be deleted
during the same constant evaluation and can't leak into the constant
expressions, I think we don't have to use COW strings under the hood
(which aren't constexpr I guess because of reference counting/COW) and
we can use something else, the patch uses heap allocated std::string
object (where __cow_constexpr_string class has just a pointer to that).
As I think we still want to hide the ugly details if !consteval in the
library, the patch exports 8 __cow_string class symbols (6 existing which
were previously just not exported and 2 new ones) and if !consteval
calls those through extern "C" _Zmangled_name symbols.  The functions
are always_inline.

And then logic_error etc. have for C++26 (precisely for
__cpp_lib_constexpr_exceptions >= 202502L) constexpr definitions of
cdtors/methods.  This results in slightly larger code (a few insns at most)
at runtime for C++26, e.g. instead of calling say some logic error
cdtor/method with 2 arguments it calls some __cow_string one with 2
arguments but + 8 bytes pointer additions on both.

The patch also removes the __throw_format_error forward declaration
which apparently wasn't needed for anything as all __throw_format_error
users were either in <format> or included <format> before the uses,
reverts the
https://gcc.gnu.org/pipermail/libstdc++/2025-July/062598.html
patch and makes sure __throw_* functions (only those for exception types
which the P3378R2 or P3068R5 papers made constexpr usable and there are
actually constexpr/consteval uses of those) are constexpr for C++26
constexpr exceptions.

The patch does that by splitting the bits/functexcept.h header:
1) bits/functexcept.h stays for the __throw_* functions which are (at
least for now) never constexpr (the <ios>, <system_error>, <future>
and <functional> std::exception derived classes) or are never used
or never used in constexpr/consteval contexts (<exception>, <typeinfo>
std::exception derived classes and std::range_error).
2) bits/new_{throw,except}.h for __throw_bad_alloc/__throw_bad_array_new_length
and std::bad_alloc/std::bad_array_new_length (where <new> includes
<bits/new_except.h> and <bits/new_throw.h> as well for the C++26 constexpr
exceptions case)
3) for the most complicated <stdexcept> stuff, one header
addition to bits/stdexcept.h one header for the __throw_logic_error etc.
forward declarations, one header for the __throw_logic_error etc.
definitions and one header without header guards which will
depending on __glibcxx_exc_in_string include one or the other because
<string> vs. <string_view> vs. <stdexcept> have heavy interdependencies

2025-12-11  Jakub Jelinek  <jakub@redhat.com>

	PR libstdc++/121114
libstdc++-v3/
	* include/bits/version.def: Implement C++26 P3378R2 - constexpr
	exception types.
	(constexpr_exceptions): Change value from 1 to 202502, remove
	no_stdname and TODO comments.
	* include/bits/version.h: Regenerate.
	* src/c++11/cow-stdexcept.cc (__cow_string(const char*)): New
	ctor.
	(__cow_string::c_str()): New method.
	* config/abi/pre/gnu.ver (GLIBCXX_3.4.35): Export 8 __cow_string
	symbols.
	* include/bits/new_except.h: New file.
	* include/bits/new_throw.h: New file.
	* include/bits/stdexcept_throw.h: New file.
	* include/bits/stdexcept_throwdef.h: New file.
	* include/bits/stdexcept_throwfwd.h: New file.
	* include/std/stdexcept: Include bits/stdexcept_except.h and move
	everything after <string> include except for std::range_error into
	include/bits/stdexcept_except.h.
	(std::range_error): If __cpp_lib_constexpr_exceptions >= 202502L
	make all cdtors and methods constexpr.
	* include/bits/stdexcept_except.h: New file.
	* include/std/optional (__glibcxx_want_constexpr_exceptions): Define
	before including bits/version.h.
	(bad_optional_access::what): Make constexpr for
	__cpp_lib_constexpr_exceptions >= 202502L.
	(__throw_bad_optional_access): Likewise.
	* include/std/expected (__glibcxx_want_constexpr_exceptions): Define
	before including bits/version.h.
	(bad_expected_access): Make cdtors and all methods constexpr for
	__cpp_lib_constexpr_exceptions >= 202502L.
	* include/std/format (__glibcxx_want_constexpr_exceptions): Define
	before including bits/version.h.
	(_GLIBCXX_CONSTEXPR_FORMAT_ERROR): Define and undef later.
	(format_error): Use _GLIBCXX_CONSTEXPR_FORMAT_ERROR on ctors.
	* include/std/variant (__glibcxx_want_constexpr_exceptions): Define
	before including bits/version.h.
	(_GLIBCXX_CONSTEXPR_BAD_VARIANT_ACCESS): Define and undef later.
	(bad_variant_access): Use it on ctors and what() method.
	(__throw_bad_variant_access): Use it here too.
	* testsuite/18_support/exception/version.cc: Adjust expected
	__cpp_lib_constexpr_exceptions value.
	* testsuite/19_diagnostics/runtime_error/constexpr.cc: New test.
	* testsuite/19_diagnostics/headers/stdexcept/version.cc: New test.
	* testsuite/19_diagnostics/logic_error/constexpr.cc: New test.
	* testsuite/20_util/expected/observers.cc (test_value_throw): Change
	return type to bool from void, return true at the end, add test
	to dereference what() first character.  Make it constexpr for
	__cpp_lib_constexpr_exceptions >= 202502L and add static_assert.
	* testsuite/20_util/expected/version.cc: Add tests for
	__cpp_lib_constexpr_exceptions value.
	* testsuite/20_util/variant/constexpr.cc: For
	__cpp_lib_constexpr_exceptions >= 202502L include <string>.
	(test_get): New function if __cpp_lib_constexpr_exceptions >= 202502L,
	assert calling it is true.
	* testsuite/20_util/variant/version.cc: Add tests for
	__cpp_lib_constexpr_exceptions value.
	* testsuite/20_util/optional/constexpr/observers/3.cc: Include
	testsuite_hooks.h.
	(eat, test01): New functions.  Assert test01() is true.
	* testsuite/20_util/optional/version.cc: Add tests for
	__cpp_lib_constexpr_exceptions value.
	* include/std/future: Add #include <bits/functexcept.h>.
	* include/std/shared_mutex: Include <bits/new_throw.h>.
	* include/std/flat_map: Include <bits/stdexcept_throw.h> instead of
	<bits/functexcept.h>.
	* include/std/syncstream: Remove <bits/functexcept.h> include.
	* include/std/flat_set: Likewise.
	* include/std/bitset: Include <bits/stdexcept_throw.h> instead of
	<bits/functexcept.h>.
	* include/std/string_view: Don't include <bits/functexcept.h>, include
	<bits/stdexcept_throw.h> early if __glibcxx_exc_in_string is not
	defined and include <bits/stdexcept_throw.h> at the end of
	the header again if __glibcxx_exc_in_string is 2 and C++26 constexpr
	exceptions are enabled.
	(__glibcxx_exc_in_string): Define if __glibcxx_exc_in_string wasn't
	defined before including <bits/stdexcept_throw.h>.
	* include/std/array: Include <bits/stdexcept_throw.h> instead of
	<bits/functexcept.h>.
	* include/std/inplace_vector: Likewise.
	* include/std/string: Include <bits/stdexcept_except.h> and
	<bits/stdexcept_throw.h> after bits/basic_string.tcc include if
	C++26 constexpr exceptions are enabled and include
	<bits/stdexcept_throw.h> instead of <bits/functexcept.h> early.
	(__glibcxx_exc_in_string): Define early to 1, undefine at the end.
	* include/std/deque: Include <bits/stdexcept_throw.h>.
	* include/bits/new_allocator.h: Include <bits/new_throw.h> instead
	of <bits/functexcept.h>.
	* include/bits/stl_algobase.h: Remove <bits/functexcept.h> include.
	* include/bits/stl_vector.h: Include <bits/stdexcept_throw.h> instead
	of <bits/functexcept.h>.
	* include/bits/memory_resource.h: Include <bits/new_throw.h> instead
	of <bits/functexcept.h>.
	* include/bits/functexcept.h: Guard everything after includes with
	#if _GLIBCXX_HOSTED.
	(__throw_bad_alloc, __throw_bad_array_new_length,  __throw_logic_error,
	__throw_domain_error, __throw_invalid_argument, __throw_length_error,
	__throw_out_of_range, __throw_out_of_range_fmt, __throw_runtime_error,
	__throw_overflow_error, __throw_underflow_error): Move declarations to
	other headers - <bits/new_throw.h> and <bits/stdexcept_throwfwd.h>.
	* include/bits/stl_map.h: Include <bits/stdexcept_throw.h> instead
	of <bits/functexcept.h>.
	* include/bits/hashtable_policy.h: Include <bits/stdexcept_throw.h>
	instead of <bits/functexcept.h>.
	* include/bits/formatfwd.h (std::__throw_format_error): Remove
	declaration.
	* include/bits/specfun.h: Include <bits/stdexcept_throw.h> instead of
	<bits/functexcept.h>.
	* include/bits/basic_ios.h: Include <bits/functexcept.h>.
	* include/bits/locale_classes.h: Likewise.
	* include/tr1/cmath: Include <bits/stdexcept_throw.h> instead of
	<bits/functexcept.h>.
	* include/tr1/memory: Remove <bits/functexcept.h> include.
	* include/tr1/array: Include <bits/stdexcept_throw.h>.
	* include/ext/vstring_util.h: Include <bits/stdexcept_throw.h> instead
	of <bits/functexcept.h>.
	* include/ext/bitmap_allocator.h: Include <bits/new_throw.h> instead
	of <bits/functexcept.h>.
	* include/ext/mt_allocator.h: Likewise.
	* include/ext/malloc_allocator.h: Likewise.
	* include/ext/debug_allocator.h: Include <bits/stdexcept_throw.h>
	instead of <bits/functexcept.h>.
	* include/ext/concurrence.h: Include <bits/exception_defines.h>
	instead of <bits/functexcept.h>.
	* include/ext/throw_allocator.h: Include <bits/new_throw.h> and
	<bits/stdexcept_throw.h> instead of <bits/functexcept.h>.
	* include/ext/string_conversions.h: Include <bits/stdexcept_throw.h>
	instead of <bits/functexcept.h>.
	* include/ext/pool_allocator.h: Include <bits/new_throw.h> instead
	of <bits/functexcept.h>.
	* include/ext/ropeimpl.h: Include <bits/stdexcept_throw.h> instead of
	<bits/functexcept.h>.
	* include/tr2/dynamic_bitset: Likewise.
	* include/experimental/optional: Include <bits/exception_defines.h>
	instead of <bits/functexcept.h>.
	* include/Makefile.am (bits_freestanding): Add
	${bits_srcdir}/{new,stdexcept}_{except,throw}.h
	and ${bits_srcdir}/stdexcept_throw{fwd,def}.h.
	* include/Makefile.in: Regenerate.
	* src/c++17/floating_from_chars.cc: Remove <bits/functexcept.h>
	include.
	* src/c++11/regex.cc: Likewise.
	* src/c++11/functexcept.cc: Likewise.
	* src/c++11/snprintf_lite.cc: Include <bits/stdexcept_throw.h> instead
	of <bits/functexcept.h>.
	* src/c++11/thread.cc: Include <bits/functexcept.h>.
	* testsuite/util/testsuite_hooks.h: Include <bits/stdexcept_throw.h>
	instead of <bits/functexcept.h>.
	* testsuite/util/io/verified_cmd_line_input.cc: Include
	<bits/exception_defines.h> instead of <bits/functexcept.h>.
	* testsuite/20_util/allocator/105975.cc: Expect different diagnostics
	for C++26.
	* testsuite/23_containers/inplace_vector/access/capacity.cc: Remove
	#error, guard if consteval { return; } with
	#ifndef __cpp_lib_constexpr_exceptions.
	* testsuite/23_containers/inplace_vector/access/elem.cc: Likewise.
	* testsuite/23_containers/inplace_vector/cons/1.cc: Likewise.
	* testsuite/23_containers/inplace_vector/cons/from_range.cc: Likewise.
	* testsuite/23_containers/inplace_vector/modifiers/single_insert.cc:
	Likewise.
	* testsuite/23_containers/inplace_vector/modifiers/assign.cc:
	Likewise.
	* testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc:
	Likewise.
	* libsupc++/new: Include <bits/new_except.h>.
	(std::bad_alloc, std::bad_array_new_length): Move defintion to
	<bits/new_except.h>.
libgomp/
	* omp.h.in: Include <bits/new_throw.h> instead of
	<bits/functexcept.h>.
gcc/testsuite/
	* g++.dg/tree-ssa/pr110819.C: Guard scan-tree-dump-not delete on
	c++23_down and add comment explaining why C++26 fails that.
	* g++.dg/tree-ssa/pr96945.C: Likewise.
	* g++.dg/tree-ssa/pr109442.C: Likewise.
	* g++.dg/tree-ssa/pr116868.C: Likewise.
	* g++.dg/tree-ssa/pr58483.C: Likewise.
2025-12-11 19:54:44 +01:00

6316 lines
178 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
#ifdef _GLIBCXX_SYSHDR
#pragma GCC system_header
#endif
#include <bits/requires_hosted.h> // for std::string
#define __glibcxx_want_format
#define __glibcxx_want_format_ranges
#define __glibcxx_want_format_uchar
#define __glibcxx_want_constexpr_exceptions
#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 <bits/monostate.h>
#include <bits/formatfwd.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> // counted_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
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" // __int128
#pragma GCC diagnostic ignored "-Wc++23-extensions" // bf16
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
// [format.fmt.string], class template basic_format_string
template<typename _CharT, typename... _Args> struct basic_format_string;
/// @cond undocumented
namespace __format
{
// STATICALLY-WIDEN, see C++20 [time.general]
// It doesn't matter for format strings (which can only be char or wchar_t)
// but this returns the narrow string for anything that isn't wchar_t. This
// is done because const char* can be inserted into any ostream type, and
// will be widened at runtime if necessary.
template<typename _CharT>
consteval auto
_Widen(const char* __narrow, const wchar_t* __wide)
{
if constexpr (is_same_v<_CharT, wchar_t>)
return __wide;
else
return __narrow;
}
#define _GLIBCXX_WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
#define _GLIBCXX_WIDEN(S) _GLIBCXX_WIDEN_(_CharT, S)
// Size for stack located buffer
template<typename _CharT>
constexpr size_t __stackbuf_size = 32 * sizeof(void*) / sizeof(_CharT);
// Type-erased character sinks.
template<typename _CharT> class _Sink;
template<typename _CharT> class _Fixedbuf_sink;
template<typename _Out, typename _CharT> class _Padding_sink;
template<typename _Out, typename _CharT> class _Escaping_sink;
// Output iterator that writes to a type-erase character sink.
template<typename _CharT>
class _Sink_iter;
// Output iterator that ignores the characters
template<typename _CharT>
class _Drop_iter;
// An unspecified output iterator type used in the `formattable` concept.
template<typename _CharT>
struct _Iter_for
{ using type = _Drop_iter<_CharT>; };
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 __cpp_lib_format >= 202311L // >= C++26
[[__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>
struct formatter
{
formatter() = delete; // No std::formatter specialization for this type.
formatter(const formatter&) = delete;
formatter& operator=(const formatter&) = delete;
};
#if __cpp_lib_constexpr_exceptions >= 202502L
#define _GLIBCXX_CONSTEXPR_FORMAT_ERROR constexpr
#else
#define _GLIBCXX_CONSTEXPR_FORMAT_ERROR
#endif
// [format.error], class format_error
class format_error : public runtime_error
{
public:
_GLIBCXX_CONSTEXPR_FORMAT_ERROR explicit format_error(const string& __what)
: runtime_error(__what) { }
_GLIBCXX_CONSTEXPR_FORMAT_ERROR 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)); }
#undef _GLIBCXX_CONSTEXPR_FORMAT_ERROR
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"); }
template<typename _CharT> class _Scanner;
} // 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) noexcept
: _M_begin(__fmt.begin()), _M_end(__fmt.end())
{ }
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();
}
#if __cpp_lib_format >= 202305L
template<typename... _Ts>
constexpr void
check_dynamic_spec(size_t __id) noexcept
{
static_assert(__valid_types_for_check_dynamic_spec<_Ts...>(),
"template arguments for check_dynamic_spec<Ts...>(id) "
"must be unique and must be one of the allowed types");
if consteval {
__check_dynamic_spec<_Ts...>(__id);
}
}
constexpr void
check_dynamic_spec_integral(size_t __id) noexcept
{
if consteval {
__check_dynamic_spec<int, unsigned, long long,
unsigned long long>(__id);
}
}
constexpr void
check_dynamic_spec_string(size_t __id) noexcept
{
if consteval {
__check_dynamic_spec<const _CharT*, basic_string_view<_CharT>>(__id);
}
}
private:
// True if _Tp occurs exactly once in _Ts.
template<typename _Tp, typename... _Ts>
static constexpr bool __once = (is_same_v<_Tp, _Ts> + ...) == 1;
template<typename... _Ts>
consteval bool
__valid_types_for_check_dynamic_spec()
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4142. check_dynamic_spec should require at least one type
if constexpr (sizeof...(_Ts) == 0)
return false;
else
{
// The types in Ts... are unique. Each type in Ts... is one of
// bool, char_type, int, unsigned int, long long int,
// unsigned long long int, float, double, long double,
// const char_type*, basic_string_view<char_type>, or const void*.
unsigned __sum
= __once<bool, _Ts...>
+ __once<char_type, _Ts...>
+ __once<int, _Ts...>
+ __once<unsigned int, _Ts...>
+ __once<long long int, _Ts...>
+ __once<unsigned long long int, _Ts...>
+ __once<float, _Ts...>
+ __once<double, _Ts...>
+ __once<long double, _Ts...>
+ __once<const char_type*, _Ts...>
+ __once<basic_string_view<char_type>, _Ts...>
+ __once<const void*, _Ts...>;
return __sum == sizeof...(_Ts);
}
}
template<typename... _Ts>
consteval void
__check_dynamic_spec(size_t __id) noexcept;
// This must not be constexpr.
static void __invalid_dynamic_spec(const char*);
friend __format::_Scanner<_CharT>;
#endif
// This constructor should only be used by the implementation.
constexpr explicit
basic_format_parse_context(basic_string_view<_CharT> __fmt,
size_t __num_args) noexcept
: _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args)
{ }
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 = 0;
};
/// @cond undocumented
template<typename _Tp, template<typename...> class _Class>
constexpr bool __is_specialization_of = false;
template<template<typename...> class _Class, typename... _Args>
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 class _Pres_type : unsigned char {
_Pres_none = 0, // Default type (not valid for integer presentation types).
_Pres_s = 1, // For strings, bool, ranges
// Presentation types for integral types (including bool and charT).
_Pres_c = 2, _Pres_x, _Pres_X, _Pres_d, _Pres_o, _Pres_b, _Pres_B,
// Presentation types for floating-point types
_Pres_g = 1, _Pres_G, _Pres_a, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F,
// For pointers, the value are same as hexadecimal presentations for integers
_Pres_p = _Pres_x, _Pres_P = _Pres_X,
_Pres_max = 0xf,
};
using enum _Pres_type;
enum class _Sign : unsigned char {
_Sign_default,
_Sign_plus,
_Sign_minus, // XXX does this need to be distinct from _Sign_default?
_Sign_space,
};
using enum _Sign;
enum _WidthPrec : unsigned char {
_WP_none, // No width/prec specified.
_WP_value, // Fixed width/prec specified.
_WP_from_arg // Use a formatting argument for width/prec.
};
using enum _WidthPrec;
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; }
// Used to make _Spec a non-C++98 POD, so the tail-padding is used.
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#pod
struct _SpecBase
{ };
template<typename _CharT>
struct _Spec : _SpecBase
{
unsigned short _M_width;
unsigned short _M_prec;
char32_t _M_fill = ' ';
_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;
unsigned _M_debug : 1;
_Pres_type _M_type : 4;
unsigned _M_reserved : 8;
// This class has 8 bits of tail padding, that can be used by
// derived classes.
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
{ return _M_parse_fill_and_align(__first, __last, "{"); }
// pre: __first != __last
constexpr iterator
_M_parse_fill_and_align(iterator __first, iterator __last, string_view __not_fill) noexcept
{
for (char __c : __not_fill)
if (*__first == static_cast<_CharT>(__c))
return __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); __align != _Align_default)
{
_M_fill = __c;
_M_align = __align;
return ++__next;
}
}
}
else if (__last - __first >= 2)
if (_Align __align = _S_align(__first[1]); __align != _Align_default)
{
_M_fill = *__first;
_M_align = __align;
return __first + 2;
}
if (_Align __align = _S_align(__first[0]); __align != _Align_default)
{
_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); __sign != _Sign_default)
{
_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;
}
#if __cpp_lib_format >= 202305L
__pc.check_dynamic_spec_integral(__val);
#endif
++__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, span<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_default)
__align = __spec._M_align;
return __format::__write_padded(__fc.out(), __str, __align, __nfill,
__spec._M_fill);
}
template<typename _CharT>
size_t
__truncate(basic_string_view<_CharT>& __s, size_t __prec)
{
if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
{
if (__prec != (size_t)-1)
return __unicode::__truncate(__s, __prec);
else
return __unicode::__field_width(__s);
}
else
{
__s = __s.substr(0, __prec);
return __s.size();
}
}
enum class _Term_char : unsigned char {
_Term_none,
_Term_quote,
_Term_apos,
};
using enum _Term_char;
template<typename _CharT>
struct _Escapes
{
using _Str_view = basic_string_view<_CharT>;
static consteval
_Str_view _S_all()
{ return _GLIBCXX_WIDEN("\t\\t\n\\n\r\\r\\\\\\\"\\\"'\\'\\u\\x"); }
static consteval
_Str_view _S_tab()
{ return _S_all().substr(0, 3); }
static consteval
_Str_view _S_newline()
{ return _S_all().substr(3, 3); }
static consteval
_Str_view _S_return()
{ return _S_all().substr(6, 3); }
static consteval
_Str_view _S_bslash()
{ return _S_all().substr(9, 3); }
static consteval
_Str_view _S_quote()
{ return _S_all().substr(12, 3); }
static consteval
_Str_view _S_apos()
{ return _S_all().substr(15, 3); }
static consteval
_Str_view _S_u()
{ return _S_all().substr(18, 2); }
static consteval
_Str_view _S_x()
{ return _S_all().substr(20, 2); }
static constexpr
_Str_view _S_term(_Term_char __term)
{
switch (__term)
{
case _Term_none:
return _Str_view();
case _Term_quote:
return _S_quote().substr(0, 1);
case _Term_apos:
return _S_apos().substr(0, 1);
}
__builtin_unreachable();
}
};
template<typename _CharT>
struct _Separators
{
using _Str_view = basic_string_view<_CharT>;
static consteval
_Str_view _S_all()
{ return _GLIBCXX_WIDEN("[]{}(), : "); }
static consteval
_Str_view _S_squares()
{ return _S_all().substr(0, 2); }
static consteval
_Str_view _S_braces()
{ return _S_all().substr(2, 2); }
static consteval
_Str_view _S_parens()
{ return _S_all().substr(4, 2); }
static consteval
_Str_view _S_comma()
{ return _S_all().substr(6, 2); }
static consteval
_Str_view _S_colon()
{ return _S_all().substr(8, 2); }
};
template<typename _CharT>
constexpr bool __should_escape_ascii(_CharT __c, _Term_char __term)
{
using _Esc = _Escapes<_CharT>;
switch (__c)
{
case _Esc::_S_tab()[0]:
case _Esc::_S_newline()[0]:
case _Esc::_S_return()[0]:
case _Esc::_S_bslash()[0]:
return true;
case _Esc::_S_quote()[0]:
return __term == _Term_quote;
case _Esc::_S_apos()[0]:
return __term == _Term_apos;
default:
return (__c >= 0 && __c < 0x20) || __c == 0x7f;
};
}
// @pre __c <= 0x10FFFF
constexpr bool __should_escape_unicode(char32_t __c, bool __prev_esc)
{
if (__unicode::__should_escape_category(__c))
return __c != U' ';
if (!__prev_esc)
return false;
return __unicode::__grapheme_cluster_break_property(__c)
== __unicode::_Gcb_property::_Gcb_Extend;
}
using uint_least32_t = __UINT_LEAST32_TYPE__;
template<typename _Out, typename _CharT>
_Out
__write_escape_seq(_Out __out, uint_least32_t __val,
basic_string_view<_CharT> __prefix)
{
using _Str_view = basic_string_view<_CharT>;
constexpr size_t __max = 8;
char __buf[__max];
const string_view __narrow(
__buf,
std::__to_chars_i<uint_least32_t>(__buf, __buf + __max, __val, 16).ptr);
__out = __format::__write(__out, __prefix);
*__out = _Separators<_CharT>::_S_braces()[0];
++__out;
if constexpr (is_same_v<char, _CharT>)
__out = __format::__write(__out, __narrow);
#ifdef _GLIBCXX_USE_WCHAR_T
else
{
_CharT __wbuf[__max];
const size_t __n = __narrow.size();
std::__to_wstring_numeric(__narrow.data(), __n, __wbuf);
__out = __format::__write(__out, _Str_view(__wbuf, __n));
}
#endif
*__out = _Separators<_CharT>::_S_braces()[1];
return ++__out;
}
template<typename _Out, typename _CharT>
_Out
__write_escape_seqs(_Out __out, basic_string_view<_CharT> __units)
{
using _UChar = make_unsigned_t<_CharT>;
for (_CharT __c : __units)
__out = __format::__write_escape_seq(
__out, static_cast<_UChar>(__c), _Escapes<_CharT>::_S_x());
return __out;
}
template<typename _Out, typename _CharT>
_Out
__write_escaped_char(_Out __out, _CharT __c)
{
using _UChar = make_unsigned_t<_CharT>;
using _Esc = _Escapes<_CharT>;
switch (__c)
{
case _Esc::_S_tab()[0]:
return __format::__write(__out, _Esc::_S_tab().substr(1, 2));
case _Esc::_S_newline()[0]:
return __format::__write(__out, _Esc::_S_newline().substr(1, 2));
case _Esc::_S_return()[0]:
return __format::__write(__out, _Esc::_S_return().substr(1, 2));
case _Esc::_S_bslash()[0]:
return __format::__write(__out, _Esc::_S_bslash().substr(1, 2));
case _Esc::_S_quote()[0]:
return __format::__write(__out, _Esc::_S_quote().substr(1, 2));
case _Esc::_S_apos()[0]:
return __format::__write(__out, _Esc::_S_apos().substr(1, 2));
default:
return __format::__write_escape_seq(
__out, static_cast<_UChar>(__c), _Esc::_S_u());
}
}
template<typename _CharT, typename _Out>
_Out
__write_escaped_ascii(_Out __out,
basic_string_view<_CharT> __str,
_Term_char __term)
{
using _Str_view = basic_string_view<_CharT>;
auto __first = __str.begin();
auto const __last = __str.end();
while (__first != __last)
{
auto __print = __first;
// assume anything outside ASCII is printable
while (__print != __last
&& !__format::__should_escape_ascii(*__print, __term))
++__print;
if (__print != __first)
__out = __format::__write(__out, _Str_view(__first, __print));
if (__print == __last)
return __out;
__first = __print;
__out = __format::__write_escaped_char(__out, *__first);
++__first;
}
return __out;
}
template<typename _CharT, typename _Out>
_Out
__write_escaped_unicode_part(_Out __out, basic_string_view<_CharT>& __str,
bool& __prev_esc, _Term_char __term)
{
using _Str_view = basic_string_view<_CharT>;
using _Esc = _Escapes<_CharT>;
static constexpr char32_t __replace = U'\uFFFD';
static constexpr _Str_view __replace_rep = []
{
// N.B. "\uFFFD" is ill-formed if encoding is not unicode.
if constexpr (is_same_v<char, _CharT>)
return "\xEF\xBF\xBD";
else
return L"\xFFFD";
}();
__unicode::_Utf_view<char32_t, _Str_view> __v(std::move(__str));
__str = {};
auto __first = __v.begin();
auto const __last = __v.end();
while (__first != __last)
{
bool __esc_ascii = false;
bool __esc_unicode = false;
bool __esc_replace = false;
auto __should_escape = [&](auto const& __it)
{
if (*__it <= 0x7f)
return __esc_ascii
= __format::__should_escape_ascii(*__it.base(), __term);
if (__format::__should_escape_unicode(*__it, __prev_esc))
return __esc_unicode = true;
if (*__it == __replace)
{
_Str_view __units(__it.base(), __it._M_units());
return __esc_replace = (__units != __replace_rep);
}
return false;
};
auto __print = __first;
while (__print != __last && !__should_escape(__print))
{
__prev_esc = false;
++__print;
}
if (__print != __first)
__out = __format::__write(__out, _Str_view(__first.base(), __print.base()));
if (__print == __last)
return __out;
__first = __print;
if (__esc_ascii)
__out = __format::__write_escaped_char(__out, *__first.base());
else if (__esc_unicode)
__out = __format::__write_escape_seq(__out, *__first, _Esc::_S_u());
// __esc_replace
else if (_Str_view __units(__first.base(), __first._M_units());
__units.end() != __last.base())
__out = __format::__write_escape_seqs(__out, __units);
else
{
__str = __units;
return __out;
}
__prev_esc = true;
++__first;
}
return __out;
}
template<typename _CharT, typename _Out>
_Out
__write_escaped_unicode(_Out __out, basic_string_view<_CharT> __str,
_Term_char __term)
{
bool __prev_escape = true;
__out = __format::__write_escaped_unicode_part(__out, __str,
__prev_escape, __term);
__out = __format::__write_escape_seqs(__out, __str);
return __out;
}
template<typename _CharT, typename _Out>
_Out
__write_escaped(_Out __out, basic_string_view<_CharT> __str, _Term_char __term)
{
__out = __format::__write(__out, _Escapes<_CharT>::_S_term(__term));
if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
__out = __format::__write_escaped_unicode(__out, __str, __term);
else if constexpr (is_same_v<char, _CharT>
&& __unicode::__literal_encoding_is_extended_ascii())
__out = __format::__write_escaped_ascii(__out, __str, __term);
else
// TODO Handle non-ascii extended encoding
__out = __format::__write_escaped_ascii(__out, __str, __term);
return __format::__write(__out, _Escapes<_CharT>::_S_term(__term));
}
// 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;
};
template<__char _CharT>
struct __formatter_str
{
__formatter_str() = default;
constexpr
__formatter_str(_Spec<_CharT> __spec) noexcept
: _M_spec(__spec)
{ }
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')
{
__spec._M_type = _Pres_s;
++__first;
}
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
else if (*__first == '?')
{
__spec._M_debug = true;
++__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_debug)
return _M_format_escaped(__s, __fc);
if (_M_spec._M_width_kind == _WP_none
&& _M_spec._M_prec_kind == _WP_none)
return __format::__write(__fc.out(), __s);
const size_t __maxwidth = _M_spec._M_get_precision(__fc);
const size_t __width = __format::__truncate(__s, __maxwidth);
return __format::__write_padded_as_spec(__s, __width, __fc, _M_spec);
}
template<typename _Out>
_Out
_M_format_escaped(basic_string_view<_CharT> __s,
basic_format_context<_Out, _CharT>& __fc) const
{
const size_t __padwidth = _M_spec._M_get_width(__fc);
if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none)
return __format::__write_escaped(__fc.out(), __s, _Term_quote);
const size_t __maxwidth = _M_spec._M_get_precision(__fc);
const size_t __width = __truncate(__s, __maxwidth);
// N.B. Escaping only increases width
if (__padwidth <= __width && _M_spec._M_prec_kind == _WP_none)
return __format::__write_escaped(__fc.out(), __s, _Term_quote);
// N.B. [tab:format.type.string] defines '?' as
// Copies the escaped string ([format.string.escaped]) to the output,
// so precision seem to appy to escaped string.
_Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth, __maxwidth);
__format::__write_escaped(__sink.out(), __s, _Term_quote);
return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill);
}
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
template<ranges::input_range _Rg, typename _Out>
requires same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>, _CharT>
_Out
_M_format_range(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const
{
using _Range = remove_reference_t<_Rg>;
using _String_view = basic_string_view<_CharT>;
if constexpr (!is_lvalue_reference_v<_Rg>)
return _M_format_range<_Range&>(__rg, __fc);
else if constexpr (!is_const_v<_Range>
&& __simply_formattable_range<_Range, _CharT>)
return _M_format_range<const _Range&>(__rg, __fc);
else if constexpr (ranges::contiguous_range<_Rg>)
{
_String_view __str(ranges::data(__rg),
size_t(ranges::distance(__rg)));
return format(__str, __fc);
}
else
{
auto __handle_debug = [this, &__rg]<typename _NOut>(_NOut __nout)
{
if (!_M_spec._M_debug)
return ranges::copy(__rg, std::move(__nout)).out;
_Escaping_sink<_NOut, _CharT>
__sink(std::move(__nout), _Term_quote);
ranges::copy(__rg, __sink.out());
return __sink._M_finish();
};
const size_t __padwidth = _M_spec._M_get_width(__fc);
if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none)
return __handle_debug(__fc.out());
_Padding_sink<_Out, _CharT>
__sink(__fc.out(), __padwidth, _M_spec._M_get_precision(__fc));
__handle_debug(__sink.out());
return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill);
}
}
constexpr void
set_debug_format() noexcept
{ _M_spec._M_debug = true; }
#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;
__formatter_int() = default;
constexpr
__formatter_int(_Spec<_CharT> __spec) noexcept
: _M_spec(__spec)
{
if (_M_spec._M_type == _Pres_none)
_M_spec._M_type = _Pres_d;
}
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 meaning as "none" for bool
++__first;
}
break;
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
case '?':
if (__type == _AsChar)
{
__spec._M_debug = true;
++__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 != _Sign_default || _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)
if (_M_spec._M_sign != _Sign_default || _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
{
basic_string_view<_CharT> __in(&__c, 1u);
size_t __width = 1u;
// N.B. single byte cannot encode character of width greater than 1
if constexpr (sizeof(_CharT) > 1u &&
__unicode::__literal_encoding_is_unicode<_CharT>())
__width = __unicode::__field_width(__c);
if (!_M_spec._M_debug)
return __format::__write_padded_as_spec(__in, __width,
__fc, _M_spec);
__width += 2;
if (_M_spec._M_get_width(__fc) <= __width)
return __format::__write_escaped(__fc.out(), __in, _Term_apos);
_CharT __buf[12];
_Fixedbuf_sink<_CharT> __sink(__buf);
__format::__write_escaped(__sink.out(), __in, _Term_apos);
__in = __sink.view();
if (__in[1] == _Escapes<_CharT>::_S_bslash()[0]) // escape sequence
__width = __in.size();
return __format::__write_padded_as_spec(__in, __width,
__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);
}
_Spec<_CharT> _M_spec{};
};
#ifdef __BFLT16_DIG__
using __bflt16_t = decltype(0.0bf16);
#endif
// Decide how 128-bit floating-point types should be formatted (or not).
// When supported, the typedef __format::__flt128_t is the type that format
// arguments should be converted to before passing them to __formatter_fp.
// Define the macro _GLIBCXX_FORMAT_F128 to say they're supported.
// The __float128, _Float128 will be formatted by converting them to:
// __ieee128 (same as __float128) when _GLIBCXX_FORMAT_F128=1,
// long double when _GLIBCXX_FORMAT_F128=2,
// _Float128 when _GLIBCXX_FORMAT_F128=3.
#undef _GLIBCXX_FORMAT_F128
#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
// Format 128-bit floating-point types using __ieee128.
using __flt128_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 __flt128_t = long double;
# define _GLIBCXX_FORMAT_F128 2
#elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH)
// Format 128-bit floating-point types using _Float128.
using __flt128_t = _Float128;
# define _GLIBCXX_FORMAT_F128 3
# 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
= is_same_v<remove_cv_t<_Tp>, _Tp> && 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
{
// Mangling of this lambda, and thus resize_and_overwrite
// instantiated with it, was fixed in ABI 18 (G++ 13). Since
// <format> was new in G++ 13, and is experimental, that
// isn't a problem.
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))
{
auto __s = _M_localize(__str, __expc, __fc.locale());
if (!__s.empty())
__str = __wstr = std::move(__s);
}
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); // Index of radix character (if any).
size_t __e = min(__d, __str.find(__exp)); // First of radix or exponent
if (__e == __str.npos)
__e = __str.size();
const size_t __r = __str.size() - __e; // Length of remainder.
auto __overwrite = [&](_CharT* __p, size_t) {
// Apply grouping to the digits before the radix or exponent.
int __off = 0;
if (auto __c = __str.front(); __c == '-' || __c == '+' || __c == ' ')
{
*__p = __c;
__off = 1;
}
auto __end = std::__add_grouping(__p + __off, __np.thousands_sep(),
__grp.data(), __grp.size(),
__str.data() + __off,
__str.data() + __e);
if (__r) // If there's a fractional part or exponent
{
if (__d != __str.npos)
{
*__end = __point; // Add the locale's radix character.
++__end;
++__e;
}
const size_t __rlen = __str.size() - __e;
// Append fractional digits and/or exponent:
char_traits<_CharT>::copy(__end, __str.data() + __e, __rlen);
__end += __rlen;
}
return (__end - __p);
};
__lstr.__resize_and_overwrite(__e * 2 + __r, __overwrite);
return __lstr;
}
_Spec<_CharT> _M_spec{};
};
template<__format::__char _CharT>
struct __formatter_ptr
{
__formatter_ptr() = default;
constexpr
__formatter_ptr(_Spec<_CharT> __spec) noexcept
: _M_spec(__spec)
{ _M_set_default(_Pres_p); }
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type = _Pres_p)
{
__format::_Spec<_CharT> __spec{};
const auto __last = __pc.end();
auto __first = __pc.begin();
auto __finalize = [this, &__spec, __type] {
_M_spec = __spec;
_M_set_default(__type);
};
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
__first = __spec._M_parse_zero_fill(__first, __last);
if (__finished())
return __first;
#endif
__first = __spec._M_parse_width(__first, __last, __pc);
if (__finished())
return __first;
if (*__first == 'p')
{
__spec._M_type = _Pres_p;
_M_spec._M_alt = !_M_spec._M_alt;
++__first;
}
#if __glibcxx_format >= 202304L
else if (*__first == 'P')
{
__spec._M_type = _Pres_P;
_M_spec._M_alt = !_M_spec._M_alt;
++__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_format >= 202304L
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_format >= 202304L
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:
[[__gnu__::__always_inline__]]
constexpr void
_M_set_default(_Pres_type __type)
{
if (_M_spec._M_type == _Pres_none && __type != _Pres_none)
{
_M_spec._M_type = __type;
_M_spec._M_alt = !_M_spec._M_alt;
}
}
__format::_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_c)
return _M_f._M_format_character(__u, __fc);
else
return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc);
}
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void
set_debug_format() noexcept
{ _M_f._M_spec._M_debug = true; }
#endif
private:
__format::__formatter_int<_CharT> _M_f;
};
#if __glibcxx_print >= 202403L
template<__format::__char _CharT>
constexpr bool enable_nonlocking_formatter_optimization<_CharT> = true;
#endif
#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_c)
return _M_f._M_format_character(__u, __fc);
else
return _M_f.format(static_cast<unsigned char>(__u), __fc);
}
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void
set_debug_format() noexcept
{ _M_f._M_spec._M_debug = true; }
#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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<_CharT> _M_f;
};
#if __glibcxx_print >= 202403L
template<__format::__char _CharT>
constexpr bool enable_nonlocking_formatter_optimization<_CharT*> = true;
#endif
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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<_CharT> _M_f;
};
#if __glibcxx_print >= 202403L
template<__format::__char _CharT>
constexpr bool
enable_nonlocking_formatter_optimization<const _CharT*> = true;
#endif
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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<_CharT> _M_f;
};
#if __glibcxx_print >= 202403L
template<__format::__char _CharT, size_t _Nm>
constexpr bool enable_nonlocking_formatter_optimization<_CharT[_Nm]> = true;
#endif
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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<char> _M_f;
};
#if __glibcxx_print >= 202403L
template<typename _Tr, typename _Alloc>
constexpr bool
enable_nonlocking_formatter_optimization<basic_string<char, _Tr, _Alloc>>
= true;
#endif
#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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<wchar_t> _M_f;
};
#if __glibcxx_print >= 202403L
template<typename _Tr, typename _Alloc>
constexpr bool
enable_nonlocking_formatter_optimization<basic_string<wchar_t, _Tr, _Alloc>>
= true;
#endif
#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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<char> _M_f;
};
#if __glibcxx_print >= 202403L
template<typename _Tr>
constexpr bool
enable_nonlocking_formatter_optimization<basic_string_view<char, _Tr>>
= true;
#endif
#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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
#endif
private:
__format::__formatter_str<wchar_t> _M_f;
};
#if __glibcxx_print >= 202403L
template<typename _Tr>
constexpr bool
enable_nonlocking_formatter_optimization<basic_string_view<wchar_t, _Tr>>
= true;
#endif
#endif // USE_WCHAR_T
/// @}
/// @cond undocumented
namespace __format
{
// each cv-unqualified arithmetic type ArithmeticT other than
// char, wchar_t, char8_t, char16_t, or char32_t
template<typename _Tp>
constexpr bool __is_formattable_integer = __is_integer<_Tp>::__value;
#if defined __SIZEOF_INT128__
template<> inline constexpr bool __is_formattable_integer<__int128> = true;
template<> inline constexpr bool __is_formattable_integer<unsigned __int128>
= true;
#endif
template<> inline constexpr bool __is_formattable_integer<char> = false;
template<> inline constexpr bool __is_formattable_integer<wchar_t> = false;
#ifdef _GLIBCXX_USE_CHAR8_T
template<> inline constexpr bool __is_formattable_integer<char8_t> = false;
#endif
template<> inline constexpr bool __is_formattable_integer<char16_t> = false;
template<> inline constexpr bool __is_formattable_integer<char32_t> = false;
template<typename _Tp>
concept __formattable_integer = __is_formattable_integer<_Tp>;
}
/// @endcond
/// Format an integer.
template<__format::__formattable_integer _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.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 __glibcxx_print >= 202403L
template<__format::__formattable_integer _Tp>
constexpr bool
enable_nonlocking_formatter_optimization<_Tp> = true;
#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 __glibcxx_print >= 202403L
template<__format::__formattable_float _Tp>
constexpr bool
enable_nonlocking_formatter_optimization<_Tp> = true;
#endif
#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
#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
// 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__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
// 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__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
// 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
// Use __formatter_fp<C>::format<__format::__flt128_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::__flt128_t)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 == 2
// Use __formatter_fp<C>::format<__format::__flt128_t, Out> for __float128,
// when long double is not 128bit IEEE type.
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::__flt128_t)__u, __fc); }
private:
__format::__formatter_fp<_CharT> _M_f;
};
#endif
#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
// Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t.
template<__format::__char _CharT>
struct formatter<__format::__bflt16_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)
{ return _M_f.parse(__pc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const
{ return _M_f.format(__v, __fc); }
private:
__format::__formatter_ptr<_CharT> _M_f;
};
#if __glibcxx_print >= 202403L
template<>
inline constexpr bool
enable_nonlocking_formatter_optimization<const void*> = true;
#endif
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:
__format::__formatter_ptr<_CharT> _M_f;
};
#if __glibcxx_print >= 202403l
template<>
inline constexpr bool
enable_nonlocking_formatter_optimization<void*> = true;
#endif
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:
__format::__formatter_ptr<_CharT> _M_f;
};
/// @}
#if __glibcxx_print >= 202403L
template<>
inline constexpr bool
enable_nonlocking_formatter_optimization<nullptr_t> = true;
#endif
#if defined _GLIBCXX_USE_WCHAR_T && __glibcxx_format_ranges
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3944. Formatters converting sequences of char to sequences of wchar_t
struct __formatter_disabled
{
__formatter_disabled() = delete; // Cannot format char sequence to wchar_t
__formatter_disabled(const __formatter_disabled&) = delete;
__formatter_disabled& operator=(const __formatter_disabled&) = delete;
};
template<>
struct formatter<char*, wchar_t>
: private __formatter_disabled { };
template<>
struct formatter<const char*, wchar_t>
: private __formatter_disabled { };
template<size_t _Nm>
struct formatter<char[_Nm], wchar_t>
: private __formatter_disabled { };
template<class _Traits, class _Allocator>
struct formatter<basic_string<char, _Traits, _Allocator>, wchar_t>
: private __formatter_disabled { };
template<class _Traits>
struct formatter<basic_string_view<char, _Traits>, wchar_t>
: private __formatter_disabled { };
#endif
/// 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 _Drop_iter
{
public:
using iterator_category = output_iterator_tag;
using value_type = void;
using difference_type = ptrdiff_t;
using pointer = void;
using reference = void;
_Drop_iter() = default;
_Drop_iter(const _Drop_iter&) = default;
_Drop_iter& operator=(const _Drop_iter&) = default;
[[__gnu__::__always_inline__]]
constexpr _Drop_iter&
operator=(_CharT __c)
{ return *this; }
[[__gnu__::__always_inline__]]
constexpr _Drop_iter&
operator=(basic_string_view<_CharT> __s)
{ return *this; }
[[__gnu__::__always_inline__]]
constexpr _Drop_iter&
operator*() { return *this; }
[[__gnu__::__always_inline__]]
constexpr _Drop_iter&
operator++() { return *this; }
[[__gnu__::__always_inline__]]
constexpr _Drop_iter
operator++(int) { return *this; }
};
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); }
bool
_M_discarding() const
{ return _M_sink->_M_discarding(); }
};
// 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; }
// Returns true if the _Sink is discarding incoming characters.
virtual bool
_M_discarding() const
{ return false; }
public:
_Sink(const _Sink&) = delete;
_Sink& operator=(const _Sink&) = delete;
[[__gnu__::__always_inline__]]
constexpr _Sink_iter<_CharT>
out() noexcept
{ return _Sink_iter<_CharT>(*this); }
};
template<typename _CharT>
class _Fixedbuf_sink final : public _Sink<_CharT>
{
void
_M_overflow() override
{
__glibcxx_assert(false);
this->_M_rewind();
}
public:
[[__gnu__::__always_inline__]]
constexpr explicit
_Fixedbuf_sink(span<_CharT> __buf)
: _Sink<_CharT>(__buf)
{ }
constexpr basic_string_view<_CharT>
view() const
{
auto __s = this->_M_used();
return basic_string_view<_CharT>(__s.data(), __s.size());
}
};
// 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[__stackbuf_size<_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 : public _Buf_sink<typename _Seq::value_type>
{
using _CharT = typename _Seq::value_type;
_Seq _M_seq;
protected:
// 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);
}
}
void _M_trim(span<const _CharT> __s)
requires __is_specialization_of<_Seq, basic_string>
{
_GLIBCXX_DEBUG_ASSERT(__s.data() == this->_M_buf
|| __s.data() == _M_seq.data());
if (__s.data() == _M_seq.data())
_M_seq.resize(__s.size());
else
this->_M_reset(this->_M_buf, __s.size());
}
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>
_M_span()
{
auto __s = this->_M_used();
if (_M_seq.size())
{
if (__s.size() != 0)
_Seq_sink::_M_overflow();
return _M_seq;
}
return __s;
}
basic_string_view<_CharT>
view()
{
auto __span = _M_span();
return basic_string_view<_CharT>(__span.data(), __span.size());
}
};
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<_CharTthis-> 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();
}
bool
_M_discarding() const override
{
// format_to_n return total number of characters, that would be written,
// see C++20 [format.functions] p20
return false;
}
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());
}
}
bool
_M_discarding() const override
{
// format_to_n return total number of characters, that would be written,
// see C++20 [format.functions] p20
return false;
}
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 };
}
}
};
// A sink for handling the padded outputs (_M_padwidth) or truncated
// (_M_maxwidth). The handling is done by writting to buffer (_Str_strink)
// until sufficient number of characters is written. After that if sequence
// is longer than _M_padwidth it's written to _M_out, and further writes are
// either:
// * buffered and forwarded to _M_out, if below _M_maxwidth,
// * ignored otherwise
// If field width of written sequence is no greater than _M_padwidth, the
// sequence is written during _M_finish call.
template<typename _Out, typename _CharT>
class _Padding_sink : public _Str_sink<_CharT>
{
size_t _M_padwidth;
size_t _M_maxwidth;
_Out _M_out;
size_t _M_printwidth;
[[__gnu__::__always_inline__]]
bool
_M_ignoring() const
{ return _M_printwidth >= _M_maxwidth; }
[[__gnu__::__always_inline__]]
bool
_M_buffering() const
{
if (_M_printwidth < _M_padwidth)
return true;
if (_M_maxwidth != (size_t)-1)
return _M_printwidth < _M_maxwidth;
return false;
}
void
_M_sync_discarding()
{
if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
if (_M_out._M_discarding())
_M_maxwidth = _M_printwidth;
}
void
_M_flush()
{
span<_CharT> __new = this->_M_used();
basic_string_view<_CharT> __str(__new.data(), __new.size());
_M_out = __format::__write(std::move(_M_out), __str);
_M_sync_discarding();
this->_M_rewind();
}
bool
_M_force_update()
{
auto __str = this->view();
// Compute actual field width, possibly truncated.
_M_printwidth = __format::__truncate(__str, _M_maxwidth);
if (_M_ignoring())
this->_M_trim(__str);
if (_M_buffering())
return true;
// We have more characters than padidng, no padding is needed,
// write direclty to _M_out.
if (_M_printwidth >= _M_padwidth)
{
_M_out = __format::__write(std::move(_M_out), __str);
_M_sync_discarding();
}
// We reached _M_maxwidth that is smaller than _M_padwidth.
// Store the prefix sequence in _M_seq, and free _M_buf.
else
_Str_sink<_CharT>::_M_overflow();
// Use internal buffer for writes to _M_out.
this->_M_reset(this->_M_buf);
return false;
}
bool
_M_update(size_t __new)
{
_M_printwidth += __new;
// Compute estimated width, to see if is not reduced.
if (_M_printwidth >= _M_padwidth || _M_printwidth >= _M_maxwidth)
return _M_force_update();
return true;
}
void
_M_overflow() override
{
// Ignore characters in buffer, and override it.
if (_M_ignoring())
this->_M_rewind();
// Write buffer to _M_out, and override it.
else if (!_M_buffering())
_M_flush();
// Update written count, and if input still should be buffered,
// flush the to _M_seq.
else if (_M_update(this->_M_used().size()))
_Str_sink<_CharT>::_M_overflow();
}
bool
_M_discarding() const override
{ return _M_ignoring(); }
typename _Sink<_CharT>::_Reservation
_M_reserve(size_t __n) override
{
// Ignore characters in buffer, if any.
if (_M_ignoring())
this->_M_rewind();
else if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
if (!_M_buffering())
{
// Write pending characters if any
if (!this->_M_used().empty())
_M_flush();
// Try to reserve from _M_out sink.
if (auto __reserved = _M_out._M_reserve(__n))
return __reserved;
}
return _Sink<_CharT>::_M_reserve(__n);
}
void
_M_bump(size_t __n) override
{
// Ignore the written characters.
if (_M_ignoring())
return;
// If reservation was made directy sink associated _M_out,
// _M_bump will be called on that sink.
_Sink<_CharT>::_M_bump(__n);
if (_M_buffering())
_M_update(__n);
}
public:
[[__gnu__::__always_inline__]]
explicit
_Padding_sink(_Out __out, size_t __padwidth, size_t __maxwidth)
: _M_padwidth(__padwidth), _M_maxwidth(__maxwidth),
_M_out(std::move(__out)), _M_printwidth(0)
{ _M_sync_discarding(); }
[[__gnu__::__always_inline__]]
explicit
_Padding_sink(_Out __out, size_t __padwidth)
: _Padding_sink(std::move(__out), __padwidth, (size_t)-1)
{ }
_Out
_M_finish(_Align __align, char32_t __fill_char)
{
// Handle any characters in the buffer.
if (auto __rem = this->_M_used().size())
{
if (_M_ignoring())
this->_M_rewind();
else if (!_M_buffering())
_M_flush();
else
_M_update(__rem);
}
if (!_M_buffering() || !_M_force_update())
// Characters were already written to _M_out.
if (_M_printwidth >= _M_padwidth)
return std::move(_M_out);
const auto __str = this->view();
if (_M_printwidth >= _M_padwidth)
return __format::__write(std::move(_M_out), __str);
const size_t __nfill = _M_padwidth - _M_printwidth;
return __format::__write_padded(std::move(_M_out), __str,
__align, __nfill, __fill_char);
}
};
template<typename _Out, typename _CharT>
class _Escaping_sink : public _Buf_sink<_CharT>
{
using _Esc = _Escapes<_CharT>;
_Out _M_out;
_Term_char _M_term : 2;
unsigned _M_prev_escape : 1;
unsigned _M_out_discards : 1;
void
_M_sync_discarding()
{
if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
_M_out_discards = _M_out._M_discarding();
}
void
_M_write()
{
span<_CharT> __bytes = this->_M_used();
basic_string_view<_CharT> __str(__bytes.data(), __bytes.size());
size_t __rem = 0;
if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
{
bool __prev_escape = _M_prev_escape;
_M_out = __format::__write_escaped_unicode_part(
std::move(_M_out), __str, __prev_escape, _M_term);
_M_prev_escape = __prev_escape;
__rem = __str.size();
if (__rem > 0 && __str.data() != this->_M_buf) [[unlikely]]
ranges::move(__str, this->_M_buf);
}
else
_M_out = __format::__write_escaped_ascii(
std::move(_M_out), __str, _M_term);
this->_M_reset(this->_M_buf, __rem);
_M_sync_discarding();
}
void
_M_overflow() override
{
if (_M_out_discards)
this->_M_rewind();
else
_M_write();
}
bool
_M_discarding() const override
{ return _M_out_discards; }
public:
[[__gnu__::__always_inline__]]
explicit
_Escaping_sink(_Out __out, _Term_char __term)
: _M_out(std::move(__out)), _M_term(__term),
_M_prev_escape(true), _M_out_discards(false)
{
_M_out = __format::__write(std::move(_M_out), _Esc::_S_term(_M_term));
_M_sync_discarding();
}
_Out
_M_finish()
{
if (_M_out_discards)
return std::move(_M_out);
if (!this->_M_used().empty())
{
_M_write();
if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
if (auto __rem = this->_M_used(); !__rem.empty())
{
basic_string_view<_CharT> __str(__rem.data(), __rem.size());
_M_out = __format::__write_escape_seqs(std::move(_M_out), __str);
}
}
return __format::__write(std::move(_M_out), _Esc::_S_term(_M_term));
}
};
enum class _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_float128,
_Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64,
_Arg_max_,
#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
_Arg_ibm128 = _Arg_ldbl,
_Arg_ieee128 = _Arg_float128,
#endif
};
using enum _Arg_t;
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;
#else
__ibm128 _M_ibm128;
__ieee128 _M_ieee128;
#endif
#ifdef __SIZEOF_FLOAT128__
__float128 _M_float128;
#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 __BFLT16_DIG__
__bflt16_t _M_bf16;
#endif
#ifdef __FLT16_DIG__
_Float16 _M_f16;
#endif
#ifdef __FLT32_DIG__
_Float32 _M_f32;
#endif
#ifdef __FLT64_DIG__
_Float64 _M_f64;
#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, __ibm128>)
return __u._M_ibm128;
else if constexpr (is_same_v<_Tp, __ieee128>)
return __u._M_ieee128;
#endif
#ifdef __SIZEOF_FLOAT128__
else if constexpr (is_same_v<_Tp, __float128>)
return __u._M_float128;
#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
#ifdef __BFLT16_DIG__
else if constexpr (is_same_v<_Tp, __bflt16_t>)
return __u._M_bf16;
#endif
#ifdef __FLT16_DIG__
else if constexpr (is_same_v<_Tp, _Float16>)
return __u._M_f16;
#endif
#ifdef __FLT32_DIG__
else if constexpr (is_same_v<_Tp, _Float32>)
return __u._M_f32;
#endif
#ifdef __FLT64_DIG__
else if constexpr (is_same_v<_Tp, _Float64>)
return __u._M_f64;
#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;
template<typename _Visitor, typename _Ctx>
decltype(auto) __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
template<typename _Ch, typename _Tp>
consteval _Arg_t
__to_arg_t_enum() noexcept;
} // 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; }
#if __cpp_lib_format >= 202306L // >= C++26
template<typename _Visitor>
decltype(auto)
visit(this basic_format_arg __arg, _Visitor&& __vis)
{ return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
template<typename _Res, typename _Visitor>
_Res
visit(this basic_format_arg __arg, _Visitor&& __vis)
{ return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
#endif
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(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128
else if constexpr (is_same_v<_Td, __float128>)
return type_identity<__float128>();
#endif
#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Td, __format::__bflt16_t>)
return type_identity<__format::__bflt16_t>();
#endif
#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Td, _Float16>)
return type_identity<_Float16>();
#endif
#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Td, _Float32>)
return type_identity<_Float32>();
#endif
#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
else if constexpr (is_same_v<_Td, _Float64>)
return type_identity<_Float64>();
#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_ieee128;
#endif
#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128
else if constexpr (is_same_v<_Tp, __float128>)
return _Arg_float128;
#endif
#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Tp, __format::__bflt16_t>)
return _Arg_bf16;
#endif
#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Tp, _Float16>)
return _Arg_f16;
#endif
#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
else if constexpr (is_same_v<_Tp, _Float32>)
return _Arg_f32;
#endif
#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
else if constexpr (is_same_v<_Tp, _Float64>)
return _Arg_f64;
#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
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, typename _Ctx>
friend decltype(auto)
__format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
template<typename _Ch, typename _Tp>
friend consteval __format::_Arg_t
__format::__to_arg_t_enum() noexcept;
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);
#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128
case _Arg_float128:
return std::forward<_Visitor>(__vis)(_M_val._M_float128);
#endif
#else
case _Arg_ibm128:
return std::forward<_Visitor>(__vis)(_M_val._M_ibm128);
case _Arg_ieee128:
return std::forward<_Visitor>(__vis)(_M_val._M_ieee128);
#endif
#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
case _Arg_bf16:
return std::forward<_Visitor>(__vis)(_M_val._M_bf16);
#endif
#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
case _Arg_f16:
return std::forward<_Visitor>(__vis)(_M_val._M_f16);
#endif
#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
case _Arg_f32:
return std::forward<_Visitor>(__vis)(_M_val._M_f32);
#endif
#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
case _Arg_f64:
return std::forward<_Visitor>(__vis)(_M_val._M_f64);
#endif
#endif // __glibcxx_to_chars
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
default:
__builtin_unreachable();
}
}
template<typename _Visitor>
decltype(auto)
_M_visit_user(_Visitor&& __vis, __format::_Arg_t __type)
{
return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto)
{
constexpr bool __user_facing = __is_one_of<_Tp,
monostate, bool, _CharT,
int, unsigned int, long long int, unsigned long long int,
float, double, long double,
const _CharT*, basic_string_view<_CharT>,
const void*, handle>::value;
if constexpr (__user_facing)
return std::forward<_Visitor>(__vis)(__val);
else
{
handle __h(__val);
return std::forward<_Visitor>(__vis)(__h);
}
}, __type);
}
};
template<typename _Visitor, typename _Context>
_GLIBCXX26_DEPRECATED_SUGGEST("std::basic_format_arg::visit")
inline decltype(auto)
visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg)
{
return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type);
}
/// @cond undocumented
namespace __format
{
template<typename _Visitor, typename _Ctx>
inline decltype(auto)
__visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg)
{
return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
}
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");
}
};
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
template<typename _Context>
inline size_t
__int_from_arg(const basic_format_arg<_Context>& __arg)
{ return __format::__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) | (unsigned)*__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( (unsigned)__format::_Arg_max_ <= (1u << _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;
struct _Parse_context : basic_format_parse_context<_CharT>
{
using basic_format_parse_context<_CharT>::basic_format_parse_context;
const _Arg_t* _M_types = nullptr;
} _M_pc;
constexpr explicit
_Scanner(basic_string_view<_CharT> __str, size_t __nargs = (size_t)-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;
__format::__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));
}
};
template<typename _CharT, typename _Tp>
consteval _Arg_t
__to_arg_t_enum() noexcept
{
using _Context = __format::__format_context<_CharT>;
using _Fmt_arg = basic_format_arg<_Context>;
using _NormalizedTp = typename _Fmt_arg::template _Normalize<_Tp>;
return _Fmt_arg::template _S_to_enum<_NormalizedTp>();
}
// 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:
consteval
_Checking_scanner(basic_string_view<_CharT> __str)
: _Scanner<_CharT>(__str, sizeof...(_Args))
{
#if __cpp_lib_format >= 202305L
this->_M_pc._M_types = _M_types.data();
#endif
}
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();
}
#if __cpp_lib_format >= 202305L
array<_Arg_t, sizeof...(_Args)>
_M_types{ { __format::__to_arg_t_enum<_CharT, _Args>()... } };
#endif
};
template<typename _Out, typename _CharT, typename _Context>
inline _Out
__do_vformat_to(_Out __out, basic_string_view<_CharT> __fmt,
const basic_format_args<_Context>& __args,
const locale* __loc)
{
_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;
__format::__visit_format_arg([&](auto& __arg) {
using _Tp = remove_cvref_t<decltype(__arg)>;
if constexpr (is_same_v<_Tp, bool>)
{
size_t __len = 4 + !__arg;
const char* __chars[] = { "false", "true" };
if (auto __res = __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;
}
#pragma GCC diagnostic pop
} // namespace __format
/// @endcond
#if __cpp_lib_format >= 202305L // >= C++26
/// @cond undocumented
// Common implementation of check_dynamic_spec{,_string,_integral}
template<typename _CharT>
template<typename... _Ts>
consteval void
basic_format_parse_context<_CharT>::
__check_dynamic_spec(size_t __id) noexcept
{
if (__id >= _M_num_args)
__format::__invalid_arg_id_in_format_string();
if constexpr (sizeof...(_Ts) != 0)
{
using _Parse_ctx = __format::_Scanner<_CharT>::_Parse_context;
auto __arg = static_cast<_Parse_ctx*>(this)->_M_types[__id];
__format::_Arg_t __types[] = {
__format::__to_arg_t_enum<_CharT, _Ts>()...
};
for (auto __t : __types)
if (__arg == __t)
return;
}
__invalid_dynamic_spec("arg(id) type does not match");
}
/// @endcond
#endif
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 __glibcxx_format_ranges // C++ >= 23 && HOSTED
/// @cond undocumented
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>();
/// @cond undocumented
namespace __format
{
template<typename _CharT, typename _Out, typename _Callback>
typename basic_format_context<_Out, _CharT>::iterator
__format_padded(basic_format_context<_Out, _CharT>& __fc,
const _Spec<_CharT>& __spec,
_Callback&& __call)
{
if constexpr (is_same_v<_Out, _Drop_iter<_CharT>>)
return __fc.out();
else
{
// This is required to implement formatting with padding,
// as we need to format to temporary buffer, using the same iterator.
static_assert(is_same_v<_Out, _Sink_iter<_CharT>>);
const size_t __padwidth = __spec._M_get_width(__fc);
if (__padwidth == 0)
return __call(__fc);
struct _Restore_out
{
_Restore_out(basic_format_context<_Sink_iter<_CharT>, _CharT>& __fc)
: _M_ctx(std::addressof(__fc)), _M_out(__fc.out())
{ }
void
_M_disarm()
{ _M_ctx = nullptr; }
~_Restore_out()
{
if (_M_ctx)
_M_ctx->advance_to(_M_out);
}
private:
basic_format_context<_Sink_iter<_CharT>, _CharT>* _M_ctx;
_Sink_iter<_CharT> _M_out;
};
_Restore_out __restore(__fc);
_Padding_sink<_Sink_iter<_CharT>, _CharT> __sink(__fc.out(), __padwidth);
__fc.advance_to(__sink.out());
__call(__fc);
__fc.advance_to(__sink._M_finish(__spec._M_align, __spec._M_fill));
__restore._M_disarm();
return __fc.out();
}
}
template<size_t _Pos, typename _Tp, typename _CharT>
struct __indexed_formatter_storage
{
constexpr void
_M_parse()
{
basic_format_parse_context<_CharT> __pc({});
if (_M_formatter.parse(__pc) != __pc.end())
__format::__failed_to_parse_format_spec();
}
template<typename _Out>
void
_M_format(__maybe_const<_Tp, _CharT>& __elem,
basic_format_context<_Out, _CharT>& __fc,
basic_string_view<_CharT> __sep) const
{
if constexpr (_Pos != 0)
__fc.advance_to(__format::__write(__fc.out(), __sep));
__fc.advance_to(_M_formatter.format(__elem, __fc));
}
[[__gnu__::__always_inline__]]
constexpr void
set_debug_format()
{
if constexpr (__has_debug_format<formatter<_Tp, _CharT>>)
_M_formatter.set_debug_format();
}
private:
formatter<_Tp, _CharT> _M_formatter;
};
template<typename _CharT, typename... _Tps>
class __tuple_formatter
{
using _String_view = basic_string_view<_CharT>;
using _Seps = __format::_Separators<_CharT>;
public:
constexpr void
set_separator(basic_string_view<_CharT> __sep) noexcept
{ _M_sep = __sep; }
constexpr void
set_brackets(basic_string_view<_CharT> __open,
basic_string_view<_CharT> __close) noexcept
{
_M_open = __open;
_M_close = __close;
}
// We deviate from standard, that declares this as template accepting
// unconstrained ParseContext type, which seems unimplementable.
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
auto __first = __pc.begin();
const auto __last = __pc.end();
__format::_Spec<_CharT> __spec{};
auto __finished = [&]
{
if (__first != __last && *__first != '}')
return false;
_M_spec = __spec;
_M_felems._M_parse();
_M_felems.set_debug_format();
return true;
};
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;
if (*__first == 'n')
{
++__first;
_M_open = _M_close = _String_view();
}
else if (*__first == 'm')
{
++__first;
if constexpr (sizeof...(_Tps) == 2)
{
_M_sep = _Seps::_S_colon();
_M_open = _M_close = _String_view();
}
else
__throw_format_error("format error: 'm' specifier requires range"
" of pair or tuple of two elements");
}
if (__finished())
return __first;
__format::__failed_to_parse_format_spec();
}
protected:
template<typename _Tuple, typename _Out, size_t... _Ids>
typename basic_format_context<_Out, _CharT>::iterator
_M_format(_Tuple& __tuple, index_sequence<_Ids...>,
basic_format_context<_Out, _CharT>& __fc) const
{ return _M_format_elems(std::get<_Ids>(__tuple)..., __fc); }
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
_M_format_elems(__maybe_const<_Tps, _CharT>&... __elems,
basic_format_context<_Out, _CharT>& __fc) const
{
return __format::__format_padded(
__fc, _M_spec,
[this, &__elems...](basic_format_context<_Out, _CharT>& __nfc)
{
__nfc.advance_to(__format::__write(__nfc.out(), _M_open));
_M_felems._M_format(__elems..., __nfc, _M_sep);
return __format::__write(__nfc.out(), _M_close);
});
}
private:
template<size_t... _Ids>
struct __formatters_storage
: __indexed_formatter_storage<_Ids, _Tps, _CharT>...
{
template<size_t _Id, typename _Up>
using _Base = __indexed_formatter_storage<_Id, _Up, _CharT>;
constexpr void
_M_parse()
{
(_Base<_Ids, _Tps>::_M_parse(), ...);
}
template<typename _Out>
void
_M_format(__maybe_const<_Tps, _CharT>&... __elems,
basic_format_context<_Out, _CharT>& __fc,
_String_view __sep) const
{
(_Base<_Ids, _Tps>::_M_format(__elems, __fc, __sep), ...);
}
constexpr void
set_debug_format()
{
(_Base<_Ids, _Tps>::set_debug_format(), ...);
}
};
template<size_t... _Ids>
static auto
_S_create_storage(index_sequence<_Ids...>)
-> __formatters_storage<_Ids...>;
using _Formatters
= decltype(_S_create_storage(index_sequence_for<_Tps...>()));
_Spec<_CharT> _M_spec{};
_String_view _M_open = _Seps::_S_parens().substr(0, 1);
_String_view _M_close = _Seps::_S_parens().substr(1, 1);
_String_view _M_sep = _Seps::_S_comma();
_Formatters _M_felems;
};
template<typename _Tp>
concept __is_map_formattable
= __is_pair<_Tp> || (__is_tuple_v<_Tp> && tuple_size_v<_Tp> == 2);
} // namespace __format
/// @endcond
// [format.tuple] Tuple formatter
template<__format::__char _CharT, formattable<_CharT> _Fp,
formattable<_CharT> _Sp>
struct formatter<pair<_Fp, _Sp>, _CharT>
: __format::__tuple_formatter<_CharT, remove_cvref_t<_Fp>,
remove_cvref_t<_Sp>>
{
private:
using __maybe_const_pair
= __conditional_t<formattable<const _Fp, _CharT>
&& formattable<const _Sp, _CharT>,
const pair<_Fp, _Sp>, pair<_Fp, _Sp>>;
public:
// We deviate from standard, that declares this as template accepting
// unconstrained FormatContext type, which seems unimplementable.
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(__maybe_const_pair& __p,
basic_format_context<_Out, _CharT>& __fc) const
{ return this->_M_format_elems(__p.first, __p.second, __fc); }
};
#if __glibcxx_print >= 202406L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4399. enable_nonlocking_formatter_optimization for pair and tuple needs remove_cvref_t
template<typename _Fp, typename _Sp>
constexpr bool enable_nonlocking_formatter_optimization<pair<_Fp, _Sp>>
= enable_nonlocking_formatter_optimization<remove_cvref_t<_Fp>>
&& enable_nonlocking_formatter_optimization<remove_cvref_t<_Sp>>;
#endif
template<__format::__char _CharT, formattable<_CharT>... _Tps>
struct formatter<tuple<_Tps...>, _CharT>
: __format::__tuple_formatter<_CharT, remove_cvref_t<_Tps>...>
{
private:
using __maybe_const_tuple
= __conditional_t<(formattable<const _Tps, _CharT> && ...),
const tuple<_Tps...>, tuple<_Tps...>>;
public:
// We deviate from standard, that declares this as template accepting
// unconstrained FormatContext type, which seems unimplementable.
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(__maybe_const_tuple& __t,
basic_format_context<_Out, _CharT>& __fc) const
{ return this->_M_format(__t, index_sequence_for<_Tps...>(), __fc); }
};
#if __glibcxx_print >= 202406L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4399. enable_nonlocking_formatter_optimization for pair and tuple needs remove_cvref_t
template<typename... _Tps>
constexpr bool enable_nonlocking_formatter_optimization<tuple<_Tps...>>
= (enable_nonlocking_formatter_optimization<remove_cvref_t<_Tps>> && ...);
#endif
// [format.range.formatter], class template range_formatter
template<typename _Tp, __format::__char _CharT>
requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT>
class range_formatter
{
using _String_view = basic_string_view<_CharT>;
using _Seps = __format::_Separators<_CharT>;
public:
constexpr void
set_separator(basic_string_view<_CharT> __sep) noexcept
{ _M_sep = __sep; }
constexpr void
set_brackets(basic_string_view<_CharT> __open,
basic_string_view<_CharT> __close) noexcept
{
_M_open = __open;
_M_close = __close;
}
constexpr formatter<_Tp, _CharT>&
underlying() noexcept
{ return _M_fval; }
constexpr const formatter<_Tp, _CharT>&
underlying() const noexcept
{ return _M_fval; }
// We deviate from standard, that declares this as template accepting
// unconstrained ParseContext type, which seems unimplementable.
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
auto __first = __pc.begin();
const auto __last = __pc.end();
__format::_Spec<_CharT> __spec{};
bool __no_brace = false;
auto __finished = [&]
{ return __first == __last || *__first == '}'; };
auto __finalize = [&]
{
_M_spec = __spec;
return __first;
};
auto __parse_val = [&](_String_view __nfs = _String_view())
{
basic_format_parse_context<_CharT> __npc(__nfs);
if (_M_fval.parse(__npc) != __npc.end())
__format::__failed_to_parse_format_spec();
if constexpr (__format::__has_debug_format<formatter<_Tp, _CharT>>)
_M_fval.set_debug_format();
return __finalize();
};
if (__finished())
return __parse_val();
__first = __spec._M_parse_fill_and_align(__first, __last, "{:");
if (__finished())
return __parse_val();
__first = __spec._M_parse_width(__first, __last, __pc);
if (__finished())
return __parse_val();
if (*__first == '?')
{
++__first;
__spec._M_debug = true;
if (__finished() || *__first != 's')
__throw_format_error("format error: '?' is allowed only in"
" combination with 's'");
}
if (*__first == 's')
{
++__first;
if constexpr (same_as<_Tp, _CharT>)
{
__spec._M_type = __format::_Pres_s;
if (__finished())
return __finalize();
__throw_format_error("format error: element format specifier"
" cannot be provided when 's' specifier is used");
}
else
__throw_format_error("format error: 's' specifier requires"
" range of character types");
}
if (__finished())
return __parse_val();
if (*__first == 'n')
{
++__first;
_M_open = _M_close = _String_view();
__no_brace = true;
}
if (__finished())
return __parse_val();
if (*__first == 'm')
{
_String_view __m(__first, 1);
++__first;
if constexpr (__format::__is_map_formattable<_Tp>)
{
_M_sep = _Seps::_S_comma();
if (!__no_brace)
{
_M_open = _Seps::_S_braces().substr(0, 1);
_M_close = _Seps::_S_braces().substr(1, 1);
}
if (__finished())
return __parse_val(__m);
__throw_format_error("format error: element format specifier"
" cannot be provided when 'm' specifier is used");
}
else
__throw_format_error("format error: 'm' specifier requires"
" range of pairs or tuples of two elements");
}
if (__finished())
return __parse_val();
if (*__first == ':')
{
__pc.advance_to(++__first);
__first = _M_fval.parse(__pc);
}
if (__finished())
return __finalize();
__format::__failed_to_parse_format_spec();
}
// We deviate from standard, that declares this as template accepting
// unconstrained FormatContext type, which seems unimplementable.
template<ranges::input_range _Rg, typename _Out>
requires formattable<ranges::range_reference_t<_Rg>, _CharT> &&
same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>, _Tp>
typename basic_format_context<_Out, _CharT>::iterator
format(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const
{
using _Range = remove_reference_t<_Rg>;
if constexpr (__format::__simply_formattable_range<_Range, _CharT>)
return _M_format<const _Range>(__rg, __fc);
else
return _M_format(__rg, __fc);
}
private:
template<ranges::input_range _Rg, typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
_M_format(_Rg& __rg, basic_format_context<_Out, _CharT>& __fc) const
{
if constexpr (same_as<_Tp, _CharT>)
if (_M_spec._M_type == __format::_Pres_s)
{
__format::__formatter_str __fstr(_M_spec);
return __fstr._M_format_range(__rg, __fc);
}
return __format::__format_padded(
__fc, _M_spec,
[this, &__rg](basic_format_context<_Out, _CharT>& __nfc)
{ return _M_format_elems(__rg, __nfc); });
}
template<ranges::input_range _Rg, typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
_M_format_elems(_Rg& __rg,
basic_format_context<_Out, _CharT>& __fc) const
{
auto __out = __format::__write(__fc.out(), _M_open);
auto __first = ranges::begin(__rg);
auto const __last = ranges::end(__rg);
if (__first == __last)
return __format::__write(__out, _M_close);
__fc.advance_to(__out);
__out = _M_fval.format(*__first, __fc);
for (++__first; __first != __last; ++__first)
{
__out = __format::__write(__out, _M_sep);
__fc.advance_to(__out);
__out = _M_fval.format(*__first, __fc);
}
return __format::__write(__out, _M_close);
}
__format::_Spec<_CharT> _M_spec{};
_String_view _M_open = _Seps::_S_squares().substr(0, 1);
_String_view _M_close = _Seps::_S_squares().substr(1, 1);
_String_view _M_sep = _Seps::_S_comma();
formatter<_Tp, _CharT> _M_fval;
};
// In standard this is shown as inheriting from specialization of
// exposition only specialization for range-default-formatter for
// each range_format. We opt for simpler implementation.
// [format.range.fmtmap], [format.range.fmtset], [format.range.fmtstr],
// specializations for maps, sets, and strings
template<ranges::input_range _Rg, __format::__char _CharT>
requires (format_kind<_Rg> != range_format::disabled)
&& formattable<ranges::range_reference_t<_Rg>, _CharT>
struct formatter<_Rg, _CharT>
{
private:
static const bool _S_range_format_is_string =
(format_kind<_Rg> == range_format::string)
|| (format_kind<_Rg> == range_format::debug_string);
using _Vt = remove_cvref_t<
ranges::range_reference_t<
__format::__maybe_const_range<_Rg, _CharT>>>;
static consteval bool _S_is_correct()
{
if constexpr (_S_range_format_is_string)
static_assert(same_as<_Vt, _CharT>);
return true;
}
static_assert(_S_is_correct());
public:
constexpr formatter() noexcept
{
using _Seps = __format::_Separators<_CharT>;
if constexpr (format_kind<_Rg> == range_format::map)
{
static_assert(__format::__is_map_formattable<_Vt>);
_M_under.set_brackets(_Seps::_S_braces().substr(0, 1),
_Seps::_S_braces().substr(1, 1));
_M_under.underlying().set_brackets({}, {});
_M_under.underlying().set_separator(_Seps::_S_colon());
}
else if constexpr (format_kind<_Rg> == range_format::set)
_M_under.set_brackets(_Seps::_S_braces().substr(0, 1),
_Seps::_S_braces().substr(1, 1));
}
constexpr void
set_separator(basic_string_view<_CharT> __sep) noexcept
requires (format_kind<_Rg> == range_format::sequence)
{ _M_under.set_separator(__sep); }
constexpr void
set_brackets(basic_string_view<_CharT> __open,
basic_string_view<_CharT> __close) noexcept
requires (format_kind<_Rg> == range_format::sequence)
{ _M_under.set_brackets(__open, __close); }
// We deviate from standard, that declares this as template accepting
// unconstrained ParseContext type, which seems unimplementable.
constexpr typename basic_format_parse_context<_CharT>::iterator
parse(basic_format_parse_context<_CharT>& __pc)
{
auto __res = _M_under.parse(__pc);
if constexpr (format_kind<_Rg> == range_format::debug_string)
_M_under.set_debug_format();
return __res;
}
// We deviate from standard, that declares this as template accepting
// unconstrained FormatContext type, which seems unimplementable.
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
format(__format::__maybe_const_range<_Rg, _CharT>& __rg,
basic_format_context<_Out, _CharT>& __fc) const
{
if constexpr (_S_range_format_is_string)
return _M_under._M_format_range(__rg, __fc);
else
return _M_under.format(__rg, __fc);
}
private:
using _Formatter_under
= __conditional_t<_S_range_format_is_string,
__format::__formatter_str<_CharT>,
range_formatter<_Vt, _CharT>>;
_Formatter_under _M_under;
};
#if __glibcxx_print >= 202406L
template<ranges::input_range _Rg>
requires (format_kind<_Rg> != range_format::disabled)
constexpr bool enable_nonlocking_formatter_optimization<_Rg> = false;
#endif
#endif // C++23 formatting ranges
#undef _GLIBCXX_WIDEN
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // __cpp_lib_format
#pragma GCC diagnostic pop
#endif // _GLIBCXX_FORMAT