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_RANGE_DEFAULT_FORMATTER_H 11 #define _LIBCPP___FORMAT_RANGE_DEFAULT_FORMATTER_H 12 13 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 14 # pragma GCC system_header 15 #endif 16 17 #include <__algorithm/ranges_copy.h> 18 #include <__chrono/statically_widen.h> 19 #include <__concepts/same_as.h> 20 #include <__config> 21 #include <__format/concepts.h> 22 #include <__format/formatter.h> 23 #include <__format/range_formatter.h> 24 #include <__iterator/back_insert_iterator.h> 25 #include <__ranges/concepts.h> 26 #include <__ranges/data.h> 27 #include <__ranges/size.h> 28 #include <__type_traits/conditional.h> 29 #include <__type_traits/remove_cvref.h> 30 #include <__utility/pair.h> 31 #include <string_view> 32 33 _LIBCPP_BEGIN_NAMESPACE_STD 34 35 #if _LIBCPP_STD_VER >= 23 36 37 template <class _Rp, class _CharT> 38 concept __const_formattable_range = 39 ranges::input_range<const _Rp> && formattable<ranges::range_reference_t<const _Rp>, _CharT>; 40 41 template <class _Rp, class _CharT> 42 using __fmt_maybe_const = conditional_t<__const_formattable_range<_Rp, _CharT>, const _Rp, _Rp>; 43 44 _LIBCPP_DIAGNOSTIC_PUSH 45 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wshadow") 46 _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshadow") 47 // This shadows map, set, and string. 48 enum class range_format { disabled, map, set, sequence, string, debug_string }; 49 _LIBCPP_DIAGNOSTIC_POP 50 51 // There is no definition of this struct, it's purely intended to be used to 52 // generate diagnostics. 53 template <class _Rp> 54 struct _LIBCPP_TEMPLATE_VIS __instantiated_the_primary_template_of_format_kind; 55 56 template <class _Rp> 57 constexpr range_format format_kind = [] { 58 // [format.range.fmtkind]/1 59 // A program that instantiates the primary template of format_kind is ill-formed. 60 static_assert(sizeof(_Rp) != sizeof(_Rp), "create a template specialization of format_kind for your type"); 61 return range_format::disabled; 62 }(); 63 64 template <ranges::input_range _Rp> 65 requires same_as<_Rp, remove_cvref_t<_Rp>> 66 inline constexpr range_format format_kind<_Rp> = [] { 67 // [format.range.fmtkind]/2 68 69 // 2.1 If same_as<remove_cvref_t<ranges::range_reference_t<R>>, R> is true, 70 // Otherwise format_kind<R> is range_format::disabled. 71 if constexpr (same_as<remove_cvref_t<ranges::range_reference_t<_Rp>>, _Rp>) 72 return range_format::disabled; 73 // 2.2 Otherwise, if the qualified-id R::key_type is valid and denotes a type: 74 else if constexpr (requires { typename _Rp::key_type; }) { 75 // 2.2.1 If the qualified-id R::mapped_type is valid and denotes a type ... 76 if constexpr (requires { typename _Rp::mapped_type; } && 77 // 2.2.1 ... If either U is a specialization of pair or U is a specialization 78 // of tuple and tuple_size_v<U> == 2 79 __fmt_pair_like<remove_cvref_t<ranges::range_reference_t<_Rp>>>) 80 return range_format::map; 81 else 82 // 2.2.2 Otherwise format_kind<R> is range_format::set. 83 return range_format::set; 84 } else 85 // 2.3 Otherwise, format_kind<R> is range_format::sequence. 86 return range_format::sequence; 87 }(); 88 89 template <range_format _Kp, ranges::input_range _Rp, class _CharT> 90 struct _LIBCPP_TEMPLATE_VIS __range_default_formatter; 91 92 // Required specializations 93 94 template <ranges::input_range _Rp, class _CharT> 95 struct _LIBCPP_TEMPLATE_VIS __range_default_formatter<range_format::sequence, _Rp, _CharT> { 96 private: 97 using __maybe_const_r = __fmt_maybe_const<_Rp, _CharT>; 98 range_formatter<remove_cvref_t<ranges::range_reference_t<__maybe_const_r>>, _CharT> __underlying_; 99 100 public: 101 _LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) noexcept { 102 __underlying_.set_separator(__separator); 103 } 104 _LIBCPP_HIDE_FROM_ABI constexpr void 105 set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) noexcept { 106 __underlying_.set_brackets(__opening_bracket, __closing_bracket); 107 } 108 109 template <class _ParseContext> 110 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 111 return __underlying_.parse(__ctx); 112 } 113 114 template <class _FormatContext> 115 _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator 116 format(__maybe_const_r& __range, _FormatContext& __ctx) const { 117 return __underlying_.format(__range, __ctx); 118 } 119 }; 120 121 template <ranges::input_range _Rp, class _CharT> 122 struct _LIBCPP_TEMPLATE_VIS __range_default_formatter<range_format::map, _Rp, _CharT> { 123 private: 124 using __maybe_const_map = __fmt_maybe_const<_Rp, _CharT>; 125 using __element_type = remove_cvref_t<ranges::range_reference_t<__maybe_const_map>>; 126 range_formatter<__element_type, _CharT> __underlying_; 127 128 public: 129 _LIBCPP_HIDE_FROM_ABI constexpr __range_default_formatter() 130 requires(__fmt_pair_like<__element_type>) 131 { 132 __underlying_.set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}")); 133 __underlying_.underlying().set_brackets({}, {}); 134 __underlying_.underlying().set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": ")); 135 } 136 137 template <class _ParseContext> 138 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 139 return __underlying_.parse(__ctx); 140 } 141 142 template <class _FormatContext> 143 _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator 144 format(__maybe_const_map& __range, _FormatContext& __ctx) const { 145 return __underlying_.format(__range, __ctx); 146 } 147 }; 148 149 template <ranges::input_range _Rp, class _CharT> 150 struct _LIBCPP_TEMPLATE_VIS __range_default_formatter<range_format::set, _Rp, _CharT> { 151 private: 152 using __maybe_const_set = __fmt_maybe_const<_Rp, _CharT>; 153 using __element_type = remove_cvref_t<ranges::range_reference_t<__maybe_const_set>>; 154 range_formatter<__element_type, _CharT> __underlying_; 155 156 public: 157 _LIBCPP_HIDE_FROM_ABI constexpr __range_default_formatter() { 158 __underlying_.set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}")); 159 } 160 161 template <class _ParseContext> 162 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 163 return __underlying_.parse(__ctx); 164 } 165 166 template <class _FormatContext> 167 _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator 168 format(__maybe_const_set& __range, _FormatContext& __ctx) const { 169 return __underlying_.format(__range, __ctx); 170 } 171 }; 172 173 template <range_format _Kp, ranges::input_range _Rp, class _CharT> 174 requires(_Kp == range_format::string || _Kp == range_format::debug_string) 175 struct _LIBCPP_TEMPLATE_VIS __range_default_formatter<_Kp, _Rp, _CharT> { 176 private: 177 // This deviates from the Standard, there the exposition only type is 178 // formatter<basic_string<charT>, charT> underlying_; 179 // Using a string_view allows the format function to avoid a copy of the 180 // input range when it is a contigious range. 181 formatter<basic_string_view<_CharT>, _CharT> __underlying_; 182 183 public: 184 template <class _ParseContext> 185 _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { 186 typename _ParseContext::iterator __i = __underlying_.parse(__ctx); 187 if constexpr (_Kp == range_format::debug_string) 188 __underlying_.set_debug_format(); 189 return __i; 190 } 191 192 template <class _FormatContext> 193 _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator 194 format(conditional_t<ranges::input_range<const _Rp>, const _Rp&, _Rp&> __range, _FormatContext& __ctx) const { 195 // When the range is contiguous use a basic_string_view instead to avoid a 196 // copy of the underlying data. The basic_string_view formatter 197 // specialization is the "basic" string formatter in libc++. 198 if constexpr (ranges::contiguous_range<_Rp> && std::ranges::sized_range<_Rp>) 199 return __underlying_.format(basic_string_view<_CharT>{ranges::data(__range), ranges::size(__range)}, __ctx); 200 else { 201 // P2106's from_range has not been implemented yet. Instead use a simple 202 // copy operation. 203 // TODO FMT use basic_string's "from_range" constructor. 204 // return __underlying_.format(basic_string<_CharT>{from_range, __range}, __ctx); 205 basic_string<_CharT> __str; 206 std::ranges::copy(__range, back_insert_iterator{__str}); 207 return __underlying_.format(static_cast<basic_string_view<_CharT>>(__str), __ctx); 208 } 209 } 210 }; 211 212 template <ranges::input_range _Rp, class _CharT> 213 requires(format_kind<_Rp> != range_format::disabled && formattable<ranges::range_reference_t<_Rp>, _CharT>) 214 struct _LIBCPP_TEMPLATE_VIS formatter<_Rp, _CharT> : __range_default_formatter<format_kind<_Rp>, _Rp, _CharT> {}; 215 216 #endif //_LIBCPP_STD_VER >= 23 217 218 _LIBCPP_END_NAMESPACE_STD 219 220 #endif // _LIBCPP___FORMAT_RANGE_DEFAULT_FORMATTER_H 221