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 <__availability>
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 <__ranges/concepts.h>
25 #include <__type_traits/remove_cvref.h>
26 #include <__utility/pair.h>
27 #include <string_view>
28 #include <tuple>
29 
30 _LIBCPP_BEGIN_NAMESPACE_STD
31 
32 #if _LIBCPP_STD_VER > 20
33 
34 template <class _Rp, class _CharT>
35 concept __const_formattable_range =
36     ranges::input_range<const _Rp> && formattable<ranges::range_reference_t<const _Rp>, _CharT>;
37 
38 template <class _Rp, class _CharT>
39 using __fmt_maybe_const = conditional_t<__const_formattable_range<_Rp, _CharT>, const _Rp, _Rp>;
40 
41 _LIBCPP_DIAGNOSTIC_PUSH
42 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wshadow")
43 _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshadow")
44 // This shadows map, set, and string.
45 enum class range_format { disabled, map, set, sequence, string, debug_string };
46 _LIBCPP_DIAGNOSTIC_POP
47 
48 // There is no definition of this struct, it's purely intended to be used to
49 // generate diagnostics.
50 template <class _Rp>
51 struct _LIBCPP_TEMPLATE_VIS __instantiated_the_primary_template_of_format_kind;
52 
53 template <class _Rp>
54 constexpr range_format format_kind = [] {
55   // [format.range.fmtkind]/1
56   // A program that instantiates the primary template of format_kind is ill-formed.
57   static_assert(sizeof(_Rp) != sizeof(_Rp), "create a template specialization of format_kind for your type");
58   return range_format::disabled;
59 }();
60 
61 template <ranges::input_range _Rp>
62   requires same_as<_Rp, remove_cvref_t<_Rp>>
63 inline constexpr range_format format_kind<_Rp> = [] {
64   // [format.range.fmtkind]/2
65 
66   // 2.1 If same_as<remove_cvref_t<ranges::range_reference_t<R>>, R> is true,
67   // Otherwise format_kind<R> is range_format::disabled.
68   if constexpr (same_as<remove_cvref_t<ranges::range_reference_t<_Rp>>, _Rp>)
69     return range_format::disabled;
70   // 2.2 Otherwise, if the qualified-id R::key_type is valid and denotes a type:
71   else if constexpr (requires { typename _Rp::key_type; }) {
72     // 2.2.1 If the qualified-id R::mapped_type is valid and denotes a type ...
73     if constexpr (requires { typename _Rp::mapped_type; } &&
74                   // 2.2.1 ... If either U is a specialization of pair or U is a specialization
75                   // of tuple and tuple_size_v<U> == 2
76                   __fmt_pair_like<remove_cvref_t<ranges::range_reference_t<_Rp>>>)
77       return range_format::map;
78     else
79       // 2.2.2 Otherwise format_kind<R> is range_format::set.
80       return range_format::set;
81   } else
82     // 2.3 Otherwise, format_kind<R> is range_format::sequence.
83     return range_format::sequence;
84 }();
85 
86 // This is a non-standard work-around to fix instantiation of
87 //   formatter<const _CharT[N], _CharT>
88 // const _CharT[N] satisfies the ranges::input_range concept.
89 // remove_cvref_t<const _CharT[N]> is _CharT[N] so it does not satisfy the
90 // requirement of the above specialization. Instead it will instantiate the
91 // primary template, which is ill-formed.
92 //
93 // An alternative solution is to remove the offending formatter.
94 //
95 // https://godbolt.org/z/bqjhhaexx
96 //
97 // The removal is proposed in LWG3833, but use the work-around until the issue
98 // has been adopted.
99 // TODO FMT Implement LWG3833.
100 template <class _CharT, size_t N>
101 inline constexpr range_format format_kind<const _CharT[N]> = range_format::disabled;
102 
103 template <range_format _Kp, ranges::input_range _Rp, class _CharT>
104 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter;
105 
106 // Required specializations
107 
108 template <ranges::input_range _Rp, class _CharT>
109 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<range_format::sequence, _Rp, _CharT> {
110 private:
111   using __maybe_const_r = __fmt_maybe_const<_Rp, _CharT>;
112   range_formatter<remove_cvref_t<ranges::range_reference_t<__maybe_const_r>>, _CharT> __underlying_;
113 
114 public:
115   _LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) {
116     __underlying_.set_separator(__separator);
117   }
118   _LIBCPP_HIDE_FROM_ABI constexpr void
119   set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) {
120     __underlying_.set_brackets(__opening_bracket, __closing_bracket);
121   }
122 
123   template <class _ParseContext>
124   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
125     return __underlying_.parse(__ctx);
126   }
127 
128   template <class FormatContext>
129   _LIBCPP_HIDE_FROM_ABI typename FormatContext::iterator format(__maybe_const_r& __range, FormatContext& __ctx) const {
130     return __underlying_.format(__range, __ctx);
131   }
132 };
133 
134 template <ranges::input_range _Rp, class _CharT>
135 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<range_format::map, _Rp, _CharT> {
136 private:
137   using __maybe_const_map = __fmt_maybe_const<_Rp, _CharT>;
138   using __element_type    = remove_cvref_t<ranges::range_reference_t<__maybe_const_map>>;
139   range_formatter<__element_type, _CharT> __underlying_;
140 
141 public:
142   _LIBCPP_HIDE_FROM_ABI constexpr __range_default_formatter()
143     requires(__fmt_pair_like<__element_type>)
144   {
145     __underlying_.set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}"));
146     __underlying_.underlying().set_brackets({}, {});
147     __underlying_.underlying().set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": "));
148   }
149 
150   template <class _ParseContext>
151   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
152     return __underlying_.parse(__ctx);
153   }
154 
155   template <class _FormatContext>
156   _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
157   format(__maybe_const_map& __range, _FormatContext& __ctx) const {
158     return __underlying_.format(__range, __ctx);
159   }
160 };
161 
162 template <ranges::input_range _Rp, class _CharT>
163 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<range_format::set, _Rp, _CharT> {
164 private:
165   using __maybe_const_set = __fmt_maybe_const<_Rp, _CharT>;
166   using __element_type    = remove_cvref_t<ranges::range_reference_t<__maybe_const_set>>;
167   range_formatter<__element_type, _CharT> __underlying_;
168 
169 public:
170   _LIBCPP_HIDE_FROM_ABI constexpr __range_default_formatter() {
171     __underlying_.set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}"));
172   }
173 
174   template <class _ParseContext>
175   _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
176     return __underlying_.parse(__ctx);
177   }
178 
179   template <class _FormatContext>
180   _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
181   format(__maybe_const_set& __range, _FormatContext& __ctx) const {
182     return __underlying_.format(__range, __ctx);
183   }
184 };
185 
186 template <range_format _Kp, ranges::input_range _Rp, class _CharT>
187   requires(_Kp == range_format::string || _Kp == range_format::debug_string)
188 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<_Kp, _Rp, _CharT> {
189   __range_default_formatter() = delete; // TODO FMT Implement
190 };
191 
192 template <ranges::input_range _Rp, class _CharT>
193   requires(format_kind<_Rp> != range_format::disabled && formattable<ranges::range_reference_t<_Rp>, _CharT>)
194 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<_Rp, _CharT>
195     : __range_default_formatter<format_kind<_Rp>, _Rp, _CharT> {};
196 
197 #endif //_LIBCPP_STD_VER > 20
198 
199 _LIBCPP_END_NAMESPACE_STD
200 
201 #endif // _LIBCPP___FORMAT_RANGE_DEFAULT_FORMATTER_H
202