| // <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 |
| |
| #if __cplusplus >= 202002L |
| |
| #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_algobase.h> // ranges::copy |
| #include <bits/stl_iterator.h> // back_insert_iterator |
| #include <bits/stl_pair.h> // __is_pair |
| #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 |
| |
| // 201907 Text Formatting, Integration of chrono, printf corner cases. |
| // 202106 std::format improvements. |
| // 202110 Fixing locale handling in chrono formatters, generator-like types. |
| // 202207 Encodings in localized formatting of chrono, basic-format-string. |
| #define __cpp_lib_format 202106L |
| |
| #if __cplusplus > 202002L |
| // 202207 P2286R8 Formatting Ranges |
| // 202207 P2585R1 Improving default container formatting |
| // TODO: #define __cpp_lib_format_ranges 202207L |
| #endif |
| |
| // [format.context], class template basic_format_context |
| template<typename _Out, typename _CharT> class basic_format_context; |
| |
| /// @cond undocumented |
| namespace __format |
| { |
| // Type-erased character sink. |
| template<typename _CharT> struct _Sink; |
| // Output iterator that writes to a type-erase character sink. |
| template<typename _CharT> |
| class _Sink_iter; |
| } // namespace __format |
| /// @endcond |
| |
| using format_context |
| = basic_format_context<__format::_Sink_iter<char>, char>; |
| using wformat_context |
| = basic_format_context<__format::_Sink_iter<wchar_t>, wchar_t>; |
| |
| // [format.args], class template basic_format_args |
| template<typename _Context> class basic_format_args; |
| using format_args = basic_format_args<format_context>; |
| using wformat_args = basic_format_args<wformat_context>; |
| |
| // [format.arguments], arguments |
| // [format.arg], class template basic_format_arg |
| template<typename _Context> |
| class basic_format_arg; |
| |
| // [format.fmt.string], class template basic_format_string |
| |
| /** 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<convertible_to<basic_string_view<_CharT>> _Tp> |
| consteval |
| basic_format_string(const _Tp& __s); |
| |
| [[__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>...>; |
| |
| template<typename... _Args> |
| using wformat_string |
| = basic_format_string<wchar_t, type_identity_t<_Args>...>; |
| |
| // [format.formatter], formatter |
| |
| /// The primary template of std::formatter is disabled. |
| template<typename _Tp, typename _CharT = char> |
| struct formatter |
| { |
| formatter() = delete; |
| 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>; |
| using wformat_parse_context = basic_format_parse_context<wchar_t>; |
| |
| 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; |
| // if (std::is_constant_evaluated()) // XXX skip runtime check? |
| 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()) // XXX skip runtime check? |
| 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(); |
| |
| // TODO: use this loop unconditionally? |
| // Most integers used for arg-id, width or precision will be small. |
| if (is_constant_evaluated()) |
| { |
| auto __next = __first; |
| unsigned short __val = 0; |
| while (__next != __last && '0' <= *__next && *__next <= '9') |
| { |
| __val = (__val * 10) + (*__next - '0'); // TODO check overflow? |
| ++__next; |
| } |
| if (__next == __first) |
| return {0, nullptr}; |
| return {__val, __next}; |
| } |
| |
| unsigned short __val = 0; |
| if constexpr (is_same_v<_CharT, char>) |
| { |
| auto [ptr, ec] = std::from_chars(__first, __last, __val); |
| if (ec == errc{}) |
| return {__val, ptr}; |
| return {0, nullptr}; |
| } |
| else |
| { |
| constexpr size_t __n = 32; |
| char __buf[__n]{}; |
| for (int __i = 0; __i < __n && __first != __last; ++__i) |
| __buf[__i] = __first[__i]; |
| auto [__v, __ptr] = __format::__parse_integer(__buf, __buf + __n); |
| return {__v, __first + (__ptr - __buf)}; |
| } |
| } |
| |
| 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_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); |
| |
| 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 short _M_width; |
| unsigned short _M_prec; |
| _CharT _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 != '{') |
| { |
| // TODO: accept any UCS scalar value as fill character. |
| // If narrow source encoding is UTF-8 then accept multibyte char. |
| 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 (std::isdigit(*__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; |
| |
| ++__first; |
| bool __arg_id = false; |
| auto __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; |
| } |
| |
| 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 std::move(__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, _CharT __fill_char) |
| { |
| const size_t __buflen = 0x20; |
| _CharT __padding_chars[__buflen]; |
| 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; |
| } |
| 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 std::move(__out); |
| } |
| |
| // A lightweight optional<locale>. |
| struct _Optional_locale |
| { |
| [[__gnu__::__always_inline__]] |
| _Optional_locale() : _M_dummy(), _M_hasval(false) { } |
| |
| _Optional_locale(const locale& __loc) noexcept |
| : _M_loc(__loc), _M_hasval(true) |
| { } |
| |
| _Optional_locale(const _Optional_locale& __l) noexcept |
| : _M_dummy(), _M_hasval(__l._M_hasval) |
| { |
| if (_M_hasval) |
| std::construct_at(&_M_loc, __l._M_loc); |
| } |
| |
| _Optional_locale& |
| operator=(const _Optional_locale& __l) noexcept |
| { |
| if (_M_hasval) |
| { |
| if (__l._M_hasval) |
| _M_loc = __l._M_loc; |
| else |
| { |
| _M_loc.~locale(); |
| _M_hasval = false; |
| } |
| } |
| else if (__l._M_hasval) |
| { |
| std::construct_at(&_M_loc, __l._M_loc); |
| _M_hasval = true; |
| } |
| return *this; |
| } |
| |
| ~_Optional_locale() { if (_M_hasval) _M_loc.~locale(); } |
| |
| _Optional_locale& |
| operator=(locale&& __loc) noexcept |
| { |
| if (_M_hasval) |
| _M_loc = std::move(__loc); |
| else |
| { |
| std::construct_at(&_M_loc, std::move(__loc)); |
| _M_hasval = true; |
| } |
| return *this; |
| } |
| |
| const locale& |
| value() noexcept |
| { |
| if (!_M_hasval) |
| { |
| std::construct_at(&_M_loc); |
| _M_hasval = true; |
| } |
| return _M_loc; |
| } |
| |
| bool has_value() const noexcept { return _M_hasval; } |
| |
| union { |
| char _M_dummy = '\0'; |
| std::locale _M_loc; |
| }; |
| bool _M_hasval = false; |
| }; |
| |
| template<typename _CharT> |
| concept __char = same_as<_CharT, char> || same_as<_CharT, wchar_t>; |
| |
| 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> |
| typename basic_format_context<_Out, _CharT>::iterator |
| format(basic_string_view<_CharT> __s, |
| basic_format_context<_Out, _CharT>& __fc) const |
| { |
| if (_M_spec._M_type == _Pres_esc) |
| { |
| // TODO: C++20 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 = __s.size(); // TODO: Unicode-aware estim. |
| |
| if (_M_spec._M_prec_kind != _WP_none) |
| { |
| size_t __prec = _M_spec._M_get_precision(__fc); |
| if (__estimated_width > __prec) |
| { |
| __s = __s.substr(0, __prec); // TODO: do not split code points |
| __estimated_width = __prec; |
| } |
| } |
| |
| size_t __width = _M_spec._M_get_width(__fc); |
| |
| if (__width <= __estimated_width) |
| return __format::__write(__fc.out(), __s); |
| |
| const size_t __nfill = __width - __estimated_width; |
| _Align __align = _M_spec._M_align ? _M_spec._M_align : _Align_left; |
| |
| return __format::__write_padded(__fc.out(), __s, |
| __align, __nfill, _M_spec._M_fill); |
| } |
| |
| #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 _M_format_str(__s, __est_width, __fc); |
| } |
| |
| template<typename _Out> |
| typename basic_format_context<_Out, _CharT>::iterator |
| _M_format_character(_CharT __c, |
| basic_format_context<_Out, _CharT>& __fc) const |
| { return _M_format_str({&__c, 1u}, 1, __fc); } |
| |
| template<typename _Out> |
| typename basic_format_context<_Out, _CharT>::iterator |
| _M_format_str(basic_string_view<_CharT> __str, size_t __est_width, |
| basic_format_context<_Out, _CharT>& __fc) const |
| { |
| // TODO: this is identical to last part of __formatter_str::format |
| // so refactor to reuse the same code. |
| |
| size_t __width = _M_spec._M_get_width(__fc); |
| |
| if (__width <= __est_width) |
| return __format::__write(__fc.out(), __str); |
| |
| size_t __nfill = __width - __est_width; |
| _Align __align = _M_spec._M_align ? _M_spec._M_align : _Align_left; |
| return __format::__write_padded(__fc.out(), __str, |
| __align, __nfill, _M_spec._M_fill); |
| } |
| |
| 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); |
| |
| _Optional_locale __loc; |
| |
| basic_string_view<_CharT> __str; |
| if constexpr (is_same_v<char, _CharT>) |
| __str = __narrow_str; |
| else |
| { |
| __loc = __fc.locale(); |
| auto& __ct = use_facet<ctype<_CharT>>(__loc.value()); |
| size_t __n = __narrow_str.size(); |
| auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT)); |
| __ct.widen(__narrow_str.data(), __narrow_str.data() + __n, __p); |
| __str = {__p, __n}; |
| } |
| |
| if (_M_spec._M_localized) |
| { |
| if constexpr (is_same_v<char, _CharT>) |
| __loc = __fc.locale(); |
| const auto& __l = __loc.value(); |
| if (__l.name() != "C") |
| { |
| auto& __np = use_facet<numpunct<_CharT>>(__l); |
| string __grp = __np.grouping(); |
| if (!__grp.empty()) |
| { |
| size_t __n = __str.size(); |
| auto __p = (_CharT*)__builtin_alloca(2 * __n |
| * sizeof(_CharT)); |
| auto __end = std::__add_grouping(__p, |
| __np.thousands_sep(), |
| __grp.data(), |
| __grp.size(), |
| __str.data(), |
| __str.data() + __n); |
| __str = {__p, size_t(__end - __p)}; |
| } |
| } |
| } |
| |
| if (__width <= __str.size()) |
| return __format::__write(__fc.out(), __str); |
| |
| _CharT __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 |
| __asm("_ZSt8to_charsPcS_DF128_"); |
| |
| to_chars_result |
| to_chars(char*, char*, _Float128, chars_format) noexcept |
| __asm("_ZSt8to_charsPcS_DF128_St12chars_format"); |
| |
| to_chars_result |
| to_chars(char*, char*, _Float128, chars_format, int) noexcept |
| __asm("_ZSt8to_charsPcS_DF128_St12chars_formati"); |
| # 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': |
| 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 = 0; |
| |
| switch (_M_spec._M_type) |
| { |
| case _Pres_A: |
| __upper = true; |
| [[fallthrough]]; |
| case _Pres_a: |
| __expc = 'p'; |
| __fmt = chars_format::hex; |
| break; |
| case _Pres_E: |
| __upper = true; |
| [[fallthrough]]; |
| case _Pres_e: |
| __expc = 'e'; |
| __use_prec = true; |
| __fmt = chars_format::scientific; |
| break; |
| case _Pres_f: |
| __use_prec = true; |
| __fmt = chars_format::fixed; |
| break; |
| case _Pres_G: |
| __upper = true; |
| [[fallthrough]]; |
| case _Pres_g: |
| __trailing_zeros = true; |
| __expc = 'e'; |
| __use_prec = true; |
| __fmt = chars_format::general; |
| break; |
| case _Pres_none: |
| if (__use_prec) |
| __fmt = chars_format::general; |
| break; |
| } |
| |
| // 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 = __prec + sizeof(__buf); |
| if (__fmt == chars_format::fixed) |
| __guess += max((int)__builtin_log10(__builtin_abs(__v)) / 2, 1); |
| __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; |
| }; |
| |
| _S_resize_and_overwrite(__dynbuf, __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); |
| __expc = std::toupper(__expc); |
| } |
| |
| // 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 = ' '; |
| } |
| |
| string_view __narrow_str(__start, __res.ptr - __start); |
| |
| // Use alternate form. |
| if (_M_spec._M_alt && __builtin_isfinite(__v)) |
| { |
| string_view __s = __narrow_str; |
| size_t __z = 0; |
| size_t __p; |
| size_t __d = __s.find('.'); |
| size_t __sigfigs; |
| if (__d != __s.npos) |
| { |
| __p = __s.find(__expc, __d + 1); |
| if (__p == __s.npos) |
| __p = __s.size(); |
| __sigfigs = __p - 1; |
| } |
| else |
| { |
| __p = __s.find(__expc); |
| if (__p == __s.npos) |
| __p = __s.size(); |
| __d = __p; |
| __sigfigs = __d; |
| } |
| |
| if (__trailing_zeros) |
| { |
| if (!isxdigit(__s[0])) |
| --__sigfigs; |
| __z = __prec - __sigfigs; |
| } |
| |
| if (size_t __extras = int(__d == __p) + __z) |
| { |
| if (__dynbuf.empty() && __extras <= (__end - __res.ptr)) |
| { |
| // 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 |
| { |
| __dynbuf.reserve(__s.size() + __extras); |
| if (__dynbuf.empty()) |
| { |
| __dynbuf = __s.substr(0, __p); |
| if (__d == __p) |
| __dynbuf += '.'; |
| if (__z) |
| __dynbuf.append(__z, '0'); |
| } |
| else |
| { |
| __dynbuf.insert(__p, __extras, '0'); |
| if (__d == __p) |
| __dynbuf[__p] = '.'; |
| } |
| __narrow_str = __dynbuf; |
| } |
| } |
| } |
| |
| // TODO move everything below to a new member function that |
| // doesn't depend on _Fp type. |
| |
| |
| _Optional_locale __loc; |
| basic_string_view<_CharT> __str; |
| basic_string<_CharT> __wstr; |
| if constexpr (is_same_v<_CharT, char>) |
| __str = __narrow_str; |
| else |
| { |
| __loc = __fc.locale(); |
| auto& __ct = use_facet<ctype<_CharT>>(__loc.value()); |
| const char* __data = __narrow_str.data(); |
| auto __overwrite = [&__data, &__ct](_CharT* __p, size_t __n) |
| { |
| __ct.widen(__data, __data + __n, __p); |
| return __n; |
| }; |
| _S_resize_and_overwrite(__wstr, __narrow_str.size(), __overwrite); |
| __str = __wstr; |
| } |
| |
| if (_M_spec._M_localized) |
| { |
| if constexpr (is_same_v<char, _CharT>) |
| __wstr = _M_localize(__str, __expc, __fc.locale()); |
| else |
| __wstr = _M_localize(__str, __expc, __loc.value()); |
| __str = __wstr; |
| } |
| |
| size_t __width = _M_spec._M_get_width(__fc); |
| |
| if (__width <= __str.size()) |
| return __format::__write(__fc.out(), __str); |
| |
| _CharT __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 (!isxdigit(__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 |
| { |
| const auto& __ct = use_facet<ctype<_CharT>>(__loc); |
| __dot = __ct.widen('.'); |
| __exp = __ct.widen(__expc); |
| } |
| |
| 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); |
| }; |
| _S_resize_and_overwrite(__lstr, __e * 2 + __r, __overwrite); |
| return __lstr; |
| } |
| |
| template<typename _Ch, typename _Func> |
| static void |
| _S_resize_and_overwrite(basic_string<_Ch>& __str, size_t __n, _Func __f) |
| { |
| #if __cpp_lib_string_resize_and_overwrite |
| __str.resize_and_overwrite(__n, __f); |
| #else |
| __str.resize(__n); |
| __str.resize(__f(__str.data(), __n)); |
| #endif |
| } |
| |
| _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) |
| 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(__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; |
| }; |
| |
| // 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) |
| 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(__u, __fc); |
| } |
| |
| constexpr void |
| set_debug_format() noexcept |
| { _M_f._M_spec._M_type = __format::_Pres_esc; } |
| |
| private: |
| __format::__formatter_int<wchar_t> _M_f; |
| }; |
| |
| /** 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); } |
| |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } |
| |
| 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); } |
| |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } |
| |
| 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); } |
| |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } |
| |
| private: |
| __format::__formatter_str<_CharT> _M_f; |
| }; |
| |
| template<__format::__char _CharT, size_t _Nm> |
| struct formatter<const _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); } |
| |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } |
| |
| 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; |
| }; |
| |
| 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; |
| }; |
| |
| 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; |
| }; |
| |
| 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; |
| }; |
| /// @} |
| |
| /// 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 |
| |
| /// 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; |
| }; |
| |
| /** 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 |
| // P2519R3 Formatting pointers |
| __first = __spec._M_parse_zero_fill(__first, __last); |
| if (__finished()) |
| return __first; |
| |
| __first = __spec._M_parse_width(__first, __last, __pc); |
| |
| if (__first != __last && (*__first == 'p' || *__first == 'P')) |
| { |
| if (*__first == 'P') |
| __spec._M_type = __format::_Pres_P; |
| ++__first; |
| } |
| |
| 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<__UINT64_TYPE__>(__v); |
| char __buf[2 + sizeof(__v) * 2]; |
| auto [__ptr, __ec] = std::to_chars(__buf + 2, std::end(__buf), |
| __u, 16); |
| const int __n = __ptr - __buf; |
| __buf[0] = '0'; |
| __buf[1] = 'x'; |
| |
| basic_string_view<_CharT> __str; |
| if constexpr (is_same_v<_CharT, char>) |
| __str = string_view(__buf, __n); |
| else |
| { |
| const std::locale& __loc = __fc.locale(); |
| auto& __ct = use_facet<ctype<_CharT>>(__loc); |
| auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT)); |
| __ct.widen(__buf, __buf + __n, __p); |
| __str = wstring_view(__p, __n); |
| } |
| |
| size_t __width = _M_spec._M_get_width(__fc); |
| |
| if (__width <= (size_t)__n) |
| return __format::__write(__fc.out(), __str); |
| |
| size_t __nfill = __width - __n; |
| __format::_Align __align |
| = _M_spec._M_align ? _M_spec._M_align : __format::_Align_right; |
| return __format::__write_padded(__fc.out(), __str, |
| __align, __nfill, _M_spec._M_fill); |
| } |
| |
| private: |
| __format::_Spec<_CharT> _M_spec{}; // XXX don't need full 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; |
| }; |
| /// @} |
| |
| |
| /// @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; |
| }; |
| |
| /// @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; } |
| }; |
| |
| // 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() |
| 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, |
| typename span<_CharT>::iterator __next) noexcept |
| { |
| _M_span = __s; |
| _M_next = __next; |
| } |
| |
| // Called by the iterator for *it++ = c |
| constexpr void |
| _M_write(_CharT __c) |
| { |
| *_M_next++ = __c; |
| if (_M_next - _M_span.begin() == _M_span.size()) [[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(); |
| } |
| } |
| |
| 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) |
| { } |
| }; |
| |
| // A sink that fills a sequence (e.g. std::string, std::vector, std::deque). |
| // Writes to a buffer then appends that to the sequence when it fills up. |
| template<typename _Seq> |
| class _Seq_sink : public _Buf_sink<typename _Seq::value_type> |
| { |
| using _CharT = typename _Seq::value_type; |
| |
| _Seq _M_seq; |
| |
| // Transfer buffer contents to the sequence, so buffer can be refilled. |
| void |
| _M_overflow() override |
| { |
| auto __s = this->_M_used(); |
| 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()); |
| this->_M_rewind(); |
| } |
| |
| public: |
| [[__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() && |
| { |
| _Seq_sink::_M_overflow(); |
| return std::move(_M_seq); |
| } |
| }; |
| |
| 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 __used = this->_M_used(); |
| if (_M_max < 0) // No maximum. |
| _M_out = ranges::copy(__used, std::move(_M_out)).out; |
| else if (_M_count < _M_max) |
| { |
| auto __max = _M_max - _M_count; |
| span<_CharT> __first; |
| if (__max < __used.size()) |
| __first = __used.first(__max); |
| else |
| __first = __used; |
| _M_out = ranges::copy(__first, std::move(_M_out)).out; |
| } |
| this->_M_rewind(); |
| _M_count += __used.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() && |
| { |
| _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> |
| class _Iter_sink<_CharT, _OutIter> : public _Sink<_CharT> |
| { |
| using uint64_t = __UINTPTR_TYPE__; |
| _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() |
| { |
| auto __used = this->_M_used(); |
| _M_count += __used.size(); |
| |
| if (_M_max >= 0) |
| { |
| // 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. |
| span<_CharT> __buf{_M_buf}; |
| this->_M_reset(__buf, __buf.begin()); |
| } |
| else |
| { |
| // No maximum character count. Just extend the span to allow |
| // writing more characters to it. |
| this->_M_reset({__used.data(), __used.size() + 1024}, __used.end()); |
| } |
| } |
| |
| 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<decltype(__n)> |
| || sizeof(__n) > sizeof(size_t)) |
| { |
| // __int128 or __detail::__max_diff_type |
| auto __m = (decltype(__n))(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. |
| uint64_t __off = reinterpret_cast<uint64_t>(__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() && |
| { |
| _Iter_sink::_M_overflow(); |
| iter_difference_t<_OutIter> __count(_M_count); |
| auto __used = this->_M_used(); |
| auto __last = _M_first; |
| if (__used.data() == _M_buf) // Wrote at least _M_max characters. |
| __last += _M_max; |
| else |
| __last += iter_difference_t<_OutIter>(__used.size()); |
| return { __last, __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, |
| #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 |
| // TODO _Float16 etc. |
| #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; |
| } |
| }; |
| |
| } // 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<__format::__formattable_with<_Tp, _Context>, |
| 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 |
| { |
| if constexpr (!__format::__formattable_with<const _Tp, _Context>) |
| static_assert(!is_const_v<_Tp>, "std::format argument must be " |
| "non-const for this type"); |
| |
| 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; |
| |
| 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 |
| |
| // TODO bfloat16 and float16 |
| |
| #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>) |
| return type_identity<basic_string_view<_CharT>>(); |
| else if constexpr (__is_specialization_of<_Td, basic_string>) |
| return type_identity<basic_string_view<_CharT>>(); |
| 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 |
| _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); |
| 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 |
| 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 |
| // TODO _Arg_f16 etc. |
| |
| #if _GLIBCXX_FORMAT_F128 == 2 |
| case _Arg_f128: |
| return std::forward<_Visitor>(__vis)(_M_val._M_f128); |
| #endif |
| } |
| __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)) |
| { |
| 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 = 0; |
| for (auto __i = __types.rbegin(); __i != __types.rend(); ++__i) |
| __packed = (__packed << _Bits) | *__i; |
| return __packed; |
| } |
| } // 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) ); |
| |
| // [format.arg.store], class template format-arg-store |
| // XXX: Should this be defined outside the class, so basic_format_args |
| // can use CTAD with a _Store argument? |
| template<typename... _Args> |
| class _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: |
| basic_format_args() noexcept = default; |
| |
| 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; |
| } |
| }; |
| |
| // An array of type-erased formatting arguments. |
| template<typename _Context> |
| template<typename... _Args> |
| class basic_format_args<_Context>::_Store |
| { |
| friend class basic_format_args; |
| |
| template<typename _Ctx, typename... _Argz> |
| friend auto |
| 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) <= _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) |
| { |
| 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__]] |
| _Store(_Tp&... __a) noexcept |
| : _M_args{_S_make_elt(__a)...} |
| { } |
| }; |
| |
| template<typename _Context> |
| template<typename... _Args> requires (sizeof...(_Args) == 0) |
| class basic_format_args<_Context>::_Store<_Args...> |
| { }; |
| |
| template<typename _Context> |
| template<typename... _Args> |
| 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_args = basic_format_args<_Context>; |
| using _Fmt_arg = basic_format_arg<_Context>; |
| using _Store = typename _Fmt_args::template |
| _Store<typename _Fmt_arg::template |
| _Normalize<remove_reference_t<_Args>>...>; |
| return _Store(__fmt_args...); |
| } |
| |
| /// 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...); } |
| |
| /// @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); |
| } // 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`). |
| */ |
| 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) |
| { } |
| |
| template<typename _Out_, typename _CharT_, typename _Context_> |
| friend _Out_ |
| __format::__do_vformat_to(_Out_, basic_string_view<_CharT_>, |
| const basic_format_args<_Context_>&, |
| const locale*); |
| |
| public: |
| basic_format_context() = default; |
| ~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 |
| { |
| template<typename _Ctx, typename _CharT> |
| [[__gnu__::__always_inline__]] |
| inline void |
| __write(_Ctx& __ctx, basic_string_view<_CharT> __str) |
| requires requires { { __ctx.out() } -> output_iterator<const _CharT&>; } |
| { |
| __ctx.advance_to(__format::__write(__ctx.out())); |
| } |
| |
| // TODO define __process_format_string which takes an object with callbacks |
| // can use that for initial constexpr parse of format string (with callbacks |
| // that have no side effects, just non-constant on error). |
| |
| 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); |
| _M_pc.advance_to(_M_pc.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; |
| |
| 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)); |
| } |
| |
| 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> |
| { |
| 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 _Head, typename... _Tail> |
| constexpr void |
| _M_parse_format_spec(size_t __id) |
| { |
| if (__id == 0) |
| { |
| formatter<_Head, _CharT> __f; |
| this->_M_pc.advance_to(__f.parse(this->_M_pc)); |
| } |
| else if constexpr (sizeof...(_Tail) != 0) |
| _M_parse_format_spec<_Tail...>(__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(); |
| |
| 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<convertible_to<basic_string_view<_CharT>> _Tp> |
| 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); } |
| |
| 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); } |
| |
| 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); } |
| |
| 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); } |
| |
| [[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(); |
| } |
| |
| [[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(); |
| } |
| |
| [[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(); |
| } |
| |
| [[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(); |
| } |
| |
| template<typename... _Args> |
| [[nodiscard]] |
| inline string |
| format(format_string<_Args...> __fmt, _Args&&... __args) |
| { return std::vformat(__fmt.get(), std::make_format_args(__args...)); } |
| |
| template<typename... _Args> |
| [[nodiscard]] |
| inline wstring |
| format(wformat_string<_Args...> __fmt, _Args&&... __args) |
| { return std::vformat(__fmt.get(), std::make_wformat_args(__args...)); } |
| |
| 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...)); |
| } |
| |
| 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...)); |
| } |
| |
| 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(std::forward<_Args>(__args)...)); |
| } |
| |
| 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(std::forward<_Args>(__args)...)); |
| } |
| |
| 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(std::forward<_Args>(__args)...)); |
| } |
| |
| 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(std::forward<_Args>(__args)...)); |
| } |
| |
| 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(); |
| } |
| |
| 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(); |
| } |
| |
| 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(); |
| } |
| |
| 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(); |
| } |
| |
| /// @cond undocumented |
| namespace __format |
| { |
| #if 1 |
| template<typename _CharT> |
| class _Counting_sink : public _Iter_sink<_CharT, _CharT*> |
| { |
| public: |
| _Counting_sink() : _Iter_sink<_CharT, _CharT*>(nullptr, 0) { } |
| |
| [[__gnu__::__always_inline__]] |
| size_t |
| count() |
| { |
| _Counting_sink::_M_overflow(); |
| return this->_M_count; |
| } |
| }; |
| #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(std::forward<_Args>(__args)...)); |
| return __buf.count(); |
| } |
| |
| 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(std::forward<_Args>(__args)...)); |
| return __buf.count(); |
| } |
| |
| 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(std::forward<_Args>(__args)...)); |
| return __buf.count(); |
| } |
| |
| 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(std::forward<_Args>(__args)...)); |
| return __buf.count(); |
| } |
| |
| #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 // C++20 |
| #endif // _GLIBCXX_FORMAT |