1 // -*- C++ -*- 2 //===----------------------------------------------------------------------===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef _LIBCPP___FORMAT_FORMATTER_TUPLE_H 11 #define _LIBCPP___FORMAT_FORMATTER_TUPLE_H 12 13 #include <__algorithm/ranges_copy.h> 14 #include <__availability> 15 #include <__chrono/statically_widen.h> 16 #include <__config> 17 #include <__format/buffer.h> 18 #include <__format/concepts.h> 19 #include <__format/format_args.h> 20 #include <__format/format_context.h> 21 #include <__format/format_error.h> 22 #include <__format/format_parse_context.h> 23 #include <__format/formatter.h> 24 #include <__format/formatter_output.h> 25 #include <__format/parser_std_format_spec.h> 26 #include <__iterator/back_insert_iterator.h> 27 #include <__type_traits/remove_cvref.h> 28 #include <__utility/integer_sequence.h> 29 #include <__utility/pair.h> 30 #include <string_view> 31 #include <tuple> 32 33 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 34 # pragma GCC system_header 35 #endif 36 37 _LIBCPP_BEGIN_NAMESPACE_STD 38 39 #if _LIBCPP_STD_VER >= 23 40 41 template <__fmt_char_type _CharT, class _Tuple, formattable<_CharT>... _Args> 42 struct _LIBCPP_TEMPLATE_VIS __formatter_tuple { set_separator__formatter_tuple43 _LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) noexcept { 44 __separator_ = __separator; 45 } 46 _LIBCPP_HIDE_FROM_ABI constexpr void set_brackets__formatter_tuple47 set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) noexcept { 48 __opening_bracket_ = __opening_bracket; 49 __closing_bracket_ = __closing_bracket; 50 } 51 52 template <class _ParseContext> parse__formatter_tuple53 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __parse_ctx) { 54 auto __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_tuple); 55 56 auto __end = __parse_ctx.end(); 57 if (__begin != __end) { 58 if (*__begin == _CharT('m')) { 59 if constexpr (sizeof...(_Args) == 2) { 60 set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": ")); 61 set_brackets({}, {}); 62 ++__begin; 63 } else 64 std::__throw_format_error("The format specifier m requires a pair or a two-element tuple"); 65 } else if (*__begin == _CharT('n')) { 66 set_brackets({}, {}); 67 ++__begin; 68 } 69 } 70 71 if (__begin != __end && *__begin != _CharT('}')) 72 std::__throw_format_error("The format-spec should consume the input or end with a '}'"); 73 74 __parse_ctx.advance_to(__begin); 75 76 // [format.tuple]/7 77 // ... For each element e in underlying_, if e.set_debug_format() 78 // is a valid expression, calls e.set_debug_format(). 79 std::__for_each_index_sequence(make_index_sequence<sizeof...(_Args)>(), [&]<size_t _Index> { 80 auto& __formatter = std::get<_Index>(__underlying_); 81 __formatter.parse(__parse_ctx); 82 // Unlike the range_formatter we don't guard against evil parsers. Since 83 // this format-spec never has a format-spec for the underlying type 84 // adding the test would give additional overhead. 85 std::__set_debug_format(__formatter); 86 }); 87 88 return __begin; 89 } 90 91 template <class _FormatContext> 92 typename _FormatContext::iterator _LIBCPP_HIDE_FROM_ABI format__formatter_tuple93 format(conditional_t<(formattable<const _Args, _CharT> && ...), const _Tuple&, _Tuple&> __tuple, 94 _FormatContext& __ctx) const { 95 __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx); 96 97 if (!__specs.__has_width()) 98 return __format_tuple(__tuple, __ctx); 99 100 // The size of the buffer needed is: 101 // - open bracket characters 102 // - close bracket character 103 // - n elements where every element may have a different size 104 // - (n -1) separators 105 // The size of the element is hard to predict, knowing the type helps but 106 // it depends on the format-spec. As an initial estimate we guess 6 107 // characters. 108 // Typically both brackets are 1 character and the separator is 2 109 // characters. Which means there will be 110 // (n - 1) * 2 + 1 + 1 = n * 2 character 111 // So estimate 8 times the range size as buffer. 112 __format::__retarget_buffer<_CharT> __buffer{8 * tuple_size_v<_Tuple>}; 113 basic_format_context<typename __format::__retarget_buffer<_CharT>::__iterator, _CharT> __c{ 114 __buffer.__make_output_iterator(), __ctx}; 115 116 __format_tuple(__tuple, __c); 117 118 return __formatter::__write_string_no_precision(basic_string_view{__buffer.__view()}, __ctx.out(), __specs); 119 } 120 121 template <class _FormatContext> __format_tuple__formatter_tuple122 _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator __format_tuple(auto&& __tuple, _FormatContext& __ctx) const { 123 __ctx.advance_to(std::ranges::copy(__opening_bracket_, __ctx.out()).out); 124 125 std::__for_each_index_sequence(make_index_sequence<sizeof...(_Args)>(), [&]<size_t _Index> { 126 if constexpr (_Index) 127 __ctx.advance_to(std::ranges::copy(__separator_, __ctx.out()).out); 128 __ctx.advance_to(std::get<_Index>(__underlying_).format(std::get<_Index>(__tuple), __ctx)); 129 }); 130 131 return std::ranges::copy(__closing_bracket_, __ctx.out()).out; 132 } 133 134 __format_spec::__parser<_CharT> __parser_{.__alignment_ = __format_spec::__alignment::__left}; 135 136 private: 137 tuple<formatter<remove_cvref_t<_Args>, _CharT>...> __underlying_; 138 basic_string_view<_CharT> __separator_ = _LIBCPP_STATICALLY_WIDEN(_CharT, ", "); 139 basic_string_view<_CharT> __opening_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, "("); 140 basic_string_view<_CharT> __closing_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, ")"); 141 }; 142 143 template <__fmt_char_type _CharT, formattable<_CharT>... _Args> 144 struct _LIBCPP_TEMPLATE_VIS formatter<pair<_Args...>, _CharT> 145 : public __formatter_tuple<_CharT, pair<_Args...>, _Args...> {}; 146 147 template <__fmt_char_type _CharT, formattable<_CharT>... _Args> 148 struct _LIBCPP_TEMPLATE_VIS formatter<tuple<_Args...>, _CharT> 149 : public __formatter_tuple<_CharT, tuple<_Args...>, _Args...> {}; 150 151 #endif //_LIBCPP_STD_VER >= 23 152 153 _LIBCPP_END_NAMESPACE_STD 154 155 #endif // _LIBCPP___FORMAT_FORMATTER_TUPLE_H 156