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