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