Commit | Line | Data |
---|---|---|
bd9231e4 | 1 | // Formatting library for C++ - range and tuple support |
05aa7e19 | 2 | // |
bd9231e4 | 3 | // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors |
05aa7e19 JG |
4 | // All rights reserved. |
5 | // | |
6 | // For the license information refer to format.h. | |
05aa7e19 JG |
7 | |
8 | #ifndef FMT_RANGES_H_ | |
9 | #define FMT_RANGES_H_ | |
10 | ||
bd9231e4 JG |
11 | #ifndef FMT_MODULE |
12 | # include <initializer_list> | |
13 | # include <iterator> | |
14 | # include <string> | |
15 | # include <tuple> | |
16 | # include <type_traits> | |
17 | # include <utility> | |
18 | #endif | |
05aa7e19 JG |
19 | |
20 | #include "format.h" | |
21 | ||
22 | FMT_BEGIN_NAMESPACE | |
23 | ||
bd9231e4 JG |
24 | FMT_EXPORT |
25 | enum class range_format { disabled, map, set, sequence, string, debug_string }; | |
05aa7e19 | 26 | |
bd9231e4 | 27 | namespace detail { |
05aa7e19 JG |
28 | |
29 | template <typename T> class is_map { | |
30 | template <typename U> static auto check(U*) -> typename U::mapped_type; | |
31 | template <typename> static void check(...); | |
32 | ||
33 | public: | |
8b75cd77 | 34 | static constexpr const bool value = |
05aa7e19 | 35 | !std::is_void<decltype(check<T>(nullptr))>::value; |
05aa7e19 JG |
36 | }; |
37 | ||
38 | template <typename T> class is_set { | |
39 | template <typename U> static auto check(U*) -> typename U::key_type; | |
40 | template <typename> static void check(...); | |
41 | ||
42 | public: | |
8b75cd77 | 43 | static constexpr const bool value = |
05aa7e19 | 44 | !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; |
05aa7e19 JG |
45 | }; |
46 | ||
47 | template <typename... Ts> struct conditional_helper {}; | |
48 | ||
49 | template <typename T, typename _ = void> struct is_range_ : std::false_type {}; | |
50 | ||
8b75cd77 | 51 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 |
05aa7e19 JG |
52 | |
53 | # define FMT_DECLTYPE_RETURN(val) \ | |
54 | ->decltype(val) { return val; } \ | |
55 | static_assert( \ | |
56 | true, "") // This makes it so that a semicolon is required after the | |
57 | // macro, which helps clang-format handle the formatting. | |
58 | ||
59 | // C array overload | |
60 | template <typename T, std::size_t N> | |
61 | auto range_begin(const T (&arr)[N]) -> const T* { | |
62 | return arr; | |
63 | } | |
64 | template <typename T, std::size_t N> | |
65 | auto range_end(const T (&arr)[N]) -> const T* { | |
66 | return arr + N; | |
67 | } | |
68 | ||
69 | template <typename T, typename Enable = void> | |
70 | struct has_member_fn_begin_end_t : std::false_type {}; | |
71 | ||
72 | template <typename T> | |
bd9231e4 | 73 | struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()), |
05aa7e19 JG |
74 | decltype(std::declval<T>().end())>> |
75 | : std::true_type {}; | |
76 | ||
bd9231e4 | 77 | // Member function overloads. |
05aa7e19 JG |
78 | template <typename T> |
79 | auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); | |
80 | template <typename T> | |
81 | auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); | |
82 | ||
bd9231e4 | 83 | // ADL overloads. Only participate in overload resolution if member functions |
05aa7e19 JG |
84 | // are not found. |
85 | template <typename T> | |
86 | auto range_begin(T&& rng) | |
87 | -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, | |
88 | decltype(begin(static_cast<T&&>(rng)))> { | |
89 | return begin(static_cast<T&&>(rng)); | |
90 | } | |
91 | template <typename T> | |
92 | auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, | |
93 | decltype(end(static_cast<T&&>(rng)))> { | |
94 | return end(static_cast<T&&>(rng)); | |
95 | } | |
96 | ||
97 | template <typename T, typename Enable = void> | |
98 | struct has_const_begin_end : std::false_type {}; | |
99 | template <typename T, typename Enable = void> | |
100 | struct has_mutable_begin_end : std::false_type {}; | |
101 | ||
102 | template <typename T> | |
103 | struct has_const_begin_end< | |
bd9231e4 JG |
104 | T, void_t<decltype(*detail::range_begin( |
105 | std::declval<const remove_cvref_t<T>&>())), | |
106 | decltype(detail::range_end( | |
107 | std::declval<const remove_cvref_t<T>&>()))>> | |
05aa7e19 JG |
108 | : std::true_type {}; |
109 | ||
110 | template <typename T> | |
111 | struct has_mutable_begin_end< | |
bd9231e4 JG |
112 | T, void_t<decltype(*detail::range_begin(std::declval<T&>())), |
113 | decltype(detail::range_end(std::declval<T&>())), | |
114 | // the extra int here is because older versions of MSVC don't | |
115 | // SFINAE properly unless there are distinct types | |
116 | int>> : std::true_type {}; | |
05aa7e19 JG |
117 | |
118 | template <typename T> | |
119 | struct is_range_<T, void> | |
120 | : std::integral_constant<bool, (has_const_begin_end<T>::value || | |
121 | has_mutable_begin_end<T>::value)> {}; | |
122 | # undef FMT_DECLTYPE_RETURN | |
123 | #endif | |
124 | ||
125 | // tuple_size and tuple_element check. | |
126 | template <typename T> class is_tuple_like_ { | |
127 | template <typename U> | |
128 | static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); | |
129 | template <typename> static void check(...); | |
130 | ||
131 | public: | |
8b75cd77 | 132 | static constexpr const bool value = |
05aa7e19 JG |
133 | !std::is_void<decltype(check<T>(nullptr))>::value; |
134 | }; | |
135 | ||
136 | // Check for integer_sequence | |
8b75cd77 | 137 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 |
05aa7e19 JG |
138 | template <typename T, T... N> |
139 | using integer_sequence = std::integer_sequence<T, N...>; | |
140 | template <size_t... N> using index_sequence = std::index_sequence<N...>; | |
141 | template <size_t N> using make_index_sequence = std::make_index_sequence<N>; | |
142 | #else | |
143 | template <typename T, T... N> struct integer_sequence { | |
144 | using value_type = T; | |
145 | ||
bd9231e4 | 146 | static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); } |
05aa7e19 JG |
147 | }; |
148 | ||
149 | template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; | |
150 | ||
151 | template <typename T, size_t N, T... Ns> | |
152 | struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; | |
153 | template <typename T, T... Ns> | |
154 | struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; | |
155 | ||
156 | template <size_t N> | |
157 | using make_index_sequence = make_integer_sequence<size_t, N>; | |
158 | #endif | |
159 | ||
8b75cd77 JG |
160 | template <typename T> |
161 | using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>; | |
162 | ||
163 | template <typename T, typename C, bool = is_tuple_like_<T>::value> | |
164 | class is_tuple_formattable_ { | |
165 | public: | |
166 | static constexpr const bool value = false; | |
167 | }; | |
168 | template <typename T, typename C> class is_tuple_formattable_<T, C, true> { | |
bd9231e4 JG |
169 | template <size_t... Is> |
170 | static auto all_true(index_sequence<Is...>, | |
171 | integer_sequence<bool, (Is >= 0)...>) -> std::true_type; | |
172 | static auto all_true(...) -> std::false_type; | |
173 | ||
174 | template <size_t... Is> | |
175 | static auto check(index_sequence<Is...>) -> decltype(all_true( | |
176 | index_sequence<Is...>{}, | |
177 | integer_sequence<bool, | |
178 | (is_formattable<typename std::tuple_element<Is, T>::type, | |
179 | C>::value)...>{})); | |
8b75cd77 JG |
180 | |
181 | public: | |
182 | static constexpr const bool value = | |
183 | decltype(check(tuple_index_sequence<T>{}))::value; | |
184 | }; | |
185 | ||
bd9231e4 JG |
186 | template <typename Tuple, typename F, size_t... Is> |
187 | FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) { | |
05aa7e19 | 188 | using std::get; |
bd9231e4 JG |
189 | // Using a free function get<Is>(Tuple) now. |
190 | const int unused[] = {0, ((void)f(get<Is>(t)), 0)...}; | |
191 | ignore_unused(unused); | |
05aa7e19 JG |
192 | } |
193 | ||
bd9231e4 JG |
194 | template <typename Tuple, typename F> |
195 | FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) { | |
196 | for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(), | |
197 | std::forward<Tuple>(t), std::forward<F>(f)); | |
05aa7e19 JG |
198 | } |
199 | ||
bd9231e4 JG |
200 | template <typename Tuple1, typename Tuple2, typename F, size_t... Is> |
201 | void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) { | |
202 | using std::get; | |
203 | const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...}; | |
204 | ignore_unused(unused); | |
05aa7e19 JG |
205 | } |
206 | ||
bd9231e4 JG |
207 | template <typename Tuple1, typename Tuple2, typename F> |
208 | void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) { | |
209 | for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(), | |
210 | std::forward<Tuple1>(t1), std::forward<Tuple2>(t2), | |
211 | std::forward<F>(f)); | |
212 | } | |
213 | ||
214 | namespace tuple { | |
215 | // Workaround a bug in MSVC 2019 (v140). | |
216 | template <typename Char, typename... T> | |
217 | using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>; | |
218 | ||
219 | using std::get; | |
220 | template <typename Tuple, typename Char, std::size_t... Is> | |
221 | auto get_formatters(index_sequence<Is...>) | |
222 | -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>; | |
223 | } // namespace tuple | |
224 | ||
8b75cd77 JG |
225 | #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 |
226 | // Older MSVC doesn't get the reference type correctly for arrays. | |
227 | template <typename R> struct range_reference_type_impl { | |
228 | using type = decltype(*detail::range_begin(std::declval<R&>())); | |
05aa7e19 JG |
229 | }; |
230 | ||
8b75cd77 JG |
231 | template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> { |
232 | using type = T&; | |
233 | }; | |
05aa7e19 | 234 | |
8b75cd77 JG |
235 | template <typename T> |
236 | using range_reference_type = typename range_reference_type_impl<T>::type; | |
237 | #else | |
238 | template <typename Range> | |
239 | using range_reference_type = | |
240 | decltype(*detail::range_begin(std::declval<Range&>())); | |
241 | #endif | |
05aa7e19 | 242 | |
8b75cd77 JG |
243 | // We don't use the Range's value_type for anything, but we do need the Range's |
244 | // reference type, with cv-ref stripped. | |
245 | template <typename Range> | |
246 | using uncvref_type = remove_cvref_t<range_reference_type<Range>>; | |
05aa7e19 | 247 | |
bd9231e4 JG |
248 | template <typename Formatter> |
249 | FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) | |
250 | -> decltype(f.set_debug_format(set)) { | |
251 | f.set_debug_format(set); | |
05aa7e19 | 252 | } |
bd9231e4 JG |
253 | template <typename Formatter> |
254 | FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} | |
05aa7e19 | 255 | |
bd9231e4 JG |
256 | template <typename T> |
257 | struct range_format_kind_ | |
258 | : std::integral_constant<range_format, | |
259 | std::is_same<uncvref_type<T>, T>::value | |
260 | ? range_format::disabled | |
261 | : is_map<T>::value ? range_format::map | |
262 | : is_set<T>::value ? range_format::set | |
263 | : range_format::sequence> {}; | |
05aa7e19 | 264 | |
bd9231e4 JG |
265 | template <range_format K> |
266 | using range_format_constant = std::integral_constant<range_format, K>; | |
05aa7e19 | 267 | |
bd9231e4 JG |
268 | // These are not generic lambdas for compatibility with C++11. |
269 | template <typename ParseContext> struct parse_empty_specs { | |
270 | template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) { | |
271 | f.parse(ctx); | |
272 | detail::maybe_set_debug_format(f, true); | |
273 | } | |
274 | ParseContext& ctx; | |
275 | }; | |
276 | template <typename FormatContext> struct format_tuple_element { | |
277 | using char_type = typename FormatContext::char_type; | |
278 | ||
279 | template <typename T> | |
280 | void operator()(const formatter<T, char_type>& f, const T& v) { | |
281 | if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out())); | |
282 | ctx.advance_to(f.format(v, ctx)); | |
283 | ++i; | |
284 | } | |
05aa7e19 | 285 | |
bd9231e4 JG |
286 | int i; |
287 | FormatContext& ctx; | |
288 | basic_string_view<char_type> separator; | |
289 | }; | |
05aa7e19 JG |
290 | |
291 | } // namespace detail | |
292 | ||
293 | template <typename T> struct is_tuple_like { | |
8b75cd77 | 294 | static constexpr const bool value = |
05aa7e19 JG |
295 | detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; |
296 | }; | |
297 | ||
8b75cd77 JG |
298 | template <typename T, typename C> struct is_tuple_formattable { |
299 | static constexpr const bool value = | |
300 | detail::is_tuple_formattable_<T, C>::value; | |
301 | }; | |
302 | ||
bd9231e4 JG |
303 | template <typename Tuple, typename Char> |
304 | struct formatter<Tuple, Char, | |
305 | enable_if_t<fmt::is_tuple_like<Tuple>::value && | |
306 | fmt::is_tuple_formattable<Tuple, Char>::value>> { | |
05aa7e19 | 307 | private: |
bd9231e4 JG |
308 | decltype(detail::tuple::get_formatters<Tuple, Char>( |
309 | detail::tuple_index_sequence<Tuple>())) formatters_; | |
310 | ||
8b75cd77 JG |
311 | basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; |
312 | basic_string_view<Char> opening_bracket_ = | |
313 | detail::string_literal<Char, '('>{}; | |
314 | basic_string_view<Char> closing_bracket_ = | |
315 | detail::string_literal<Char, ')'>{}; | |
316 | ||
05aa7e19 | 317 | public: |
8b75cd77 JG |
318 | FMT_CONSTEXPR formatter() {} |
319 | ||
320 | FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { | |
321 | separator_ = sep; | |
322 | } | |
323 | ||
324 | FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, | |
325 | basic_string_view<Char> close) { | |
326 | opening_bracket_ = open; | |
327 | closing_bracket_ = close; | |
328 | } | |
329 | ||
05aa7e19 JG |
330 | template <typename ParseContext> |
331 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
bd9231e4 JG |
332 | auto it = ctx.begin(); |
333 | if (it != ctx.end() && *it != '}') report_error("invalid format specifier"); | |
334 | detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx}); | |
335 | return it; | |
05aa7e19 JG |
336 | } |
337 | ||
bd9231e4 JG |
338 | template <typename FormatContext> |
339 | auto format(const Tuple& value, FormatContext& ctx) const | |
8b75cd77 | 340 | -> decltype(ctx.out()) { |
bd9231e4 JG |
341 | ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out())); |
342 | detail::for_each2( | |
343 | formatters_, value, | |
344 | detail::format_tuple_element<FormatContext>{0, ctx, separator_}); | |
345 | return detail::copy<Char>(closing_bracket_, ctx.out()); | |
05aa7e19 JG |
346 | } |
347 | }; | |
348 | ||
349 | template <typename T, typename Char> struct is_range { | |
8b75cd77 | 350 | static constexpr const bool value = |
bd9231e4 | 351 | detail::is_range_<T>::value && !detail::has_to_string_view<T>::value; |
8b75cd77 JG |
352 | }; |
353 | ||
354 | namespace detail { | |
355 | template <typename Context> struct range_mapper { | |
356 | using mapper = arg_mapper<Context>; | |
357 | ||
358 | template <typename T, | |
359 | FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)> | |
360 | static auto map(T&& value) -> T&& { | |
361 | return static_cast<T&&>(value); | |
362 | } | |
363 | template <typename T, | |
364 | FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)> | |
365 | static auto map(T&& value) | |
366 | -> decltype(mapper().map(static_cast<T&&>(value))) { | |
367 | return mapper().map(static_cast<T&&>(value)); | |
368 | } | |
05aa7e19 JG |
369 | }; |
370 | ||
8b75cd77 | 371 | template <typename Char, typename Element> |
bd9231e4 JG |
372 | using range_formatter_type = |
373 | formatter<remove_cvref_t<decltype(range_mapper<buffered_context<Char>>{} | |
374 | .map(std::declval<Element>()))>, | |
375 | Char>; | |
8b75cd77 JG |
376 | |
377 | template <typename R> | |
378 | using maybe_const_range = | |
379 | conditional_t<has_const_begin_end<R>::value, const R, R>; | |
380 | ||
381 | // Workaround a bug in MSVC 2015 and earlier. | |
382 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | |
383 | template <typename R, typename Char> | |
384 | struct is_formattable_delayed | |
bd9231e4 | 385 | : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {}; |
8b75cd77 | 386 | #endif |
8b75cd77 JG |
387 | } // namespace detail |
388 | ||
bd9231e4 JG |
389 | template <typename...> struct conjunction : std::true_type {}; |
390 | template <typename P> struct conjunction<P> : P {}; | |
391 | template <typename P1, typename... Pn> | |
392 | struct conjunction<P1, Pn...> | |
393 | : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {}; | |
394 | ||
8b75cd77 JG |
395 | template <typename T, typename Char, typename Enable = void> |
396 | struct range_formatter; | |
397 | ||
05aa7e19 | 398 | template <typename T, typename Char> |
8b75cd77 | 399 | struct range_formatter< |
05aa7e19 | 400 | T, Char, |
bd9231e4 JG |
401 | enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>, |
402 | is_formattable<T, Char>>::value>> { | |
8b75cd77 JG |
403 | private: |
404 | detail::range_formatter_type<Char, T> underlying_; | |
8b75cd77 JG |
405 | basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; |
406 | basic_string_view<Char> opening_bracket_ = | |
407 | detail::string_literal<Char, '['>{}; | |
408 | basic_string_view<Char> closing_bracket_ = | |
409 | detail::string_literal<Char, ']'>{}; | |
bd9231e4 JG |
410 | bool is_debug = false; |
411 | ||
412 | template <typename Output, typename It, typename Sentinel, typename U = T, | |
413 | FMT_ENABLE_IF(std::is_same<U, Char>::value)> | |
414 | auto write_debug_string(Output& out, It it, Sentinel end) const -> Output { | |
415 | auto buf = basic_memory_buffer<Char>(); | |
416 | for (; it != end; ++it) buf.push_back(*it); | |
417 | auto specs = format_specs(); | |
418 | specs.type = presentation_type::debug; | |
419 | return detail::write<Char>( | |
420 | out, basic_string_view<Char>(buf.data(), buf.size()), specs); | |
8b75cd77 JG |
421 | } |
422 | ||
bd9231e4 JG |
423 | template <typename Output, typename It, typename Sentinel, typename U = T, |
424 | FMT_ENABLE_IF(!std::is_same<U, Char>::value)> | |
425 | auto write_debug_string(Output& out, It, Sentinel) const -> Output { | |
426 | return out; | |
8b75cd77 JG |
427 | } |
428 | ||
429 | public: | |
430 | FMT_CONSTEXPR range_formatter() {} | |
431 | ||
432 | FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& { | |
433 | return underlying_; | |
434 | } | |
435 | ||
436 | FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { | |
437 | separator_ = sep; | |
438 | } | |
439 | ||
440 | FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, | |
441 | basic_string_view<Char> close) { | |
442 | opening_bracket_ = open; | |
443 | closing_bracket_ = close; | |
444 | } | |
445 | ||
05aa7e19 JG |
446 | template <typename ParseContext> |
447 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
8b75cd77 JG |
448 | auto it = ctx.begin(); |
449 | auto end = ctx.end(); | |
bd9231e4 JG |
450 | detail::maybe_set_debug_format(underlying_, true); |
451 | if (it == end) return underlying_.parse(ctx); | |
8b75cd77 | 452 | |
bd9231e4 JG |
453 | switch (detail::to_ascii(*it)) { |
454 | case 'n': | |
8b75cd77 JG |
455 | set_brackets({}, {}); |
456 | ++it; | |
bd9231e4 JG |
457 | break; |
458 | case '?': | |
459 | is_debug = true; | |
460 | set_brackets({}, {}); | |
461 | ++it; | |
462 | if (it == end || *it != 's') report_error("invalid format specifier"); | |
463 | FMT_FALLTHROUGH; | |
464 | case 's': | |
465 | if (!std::is_same<T, Char>::value) | |
466 | report_error("invalid format specifier"); | |
467 | if (!is_debug) { | |
468 | set_brackets(detail::string_literal<Char, '"'>{}, | |
469 | detail::string_literal<Char, '"'>{}); | |
470 | set_separator({}); | |
471 | detail::maybe_set_debug_format(underlying_, false); | |
472 | } | |
473 | ++it; | |
8b75cd77 JG |
474 | return it; |
475 | } | |
476 | ||
bd9231e4 JG |
477 | if (it != end && *it != '}') { |
478 | if (*it != ':') report_error("invalid format specifier"); | |
479 | detail::maybe_set_debug_format(underlying_, false); | |
480 | ++it; | |
481 | } | |
8b75cd77 | 482 | |
8b75cd77 JG |
483 | ctx.advance_to(it); |
484 | return underlying_.parse(ctx); | |
05aa7e19 JG |
485 | } |
486 | ||
bd9231e4 | 487 | template <typename R, typename FormatContext> |
8b75cd77 | 488 | auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { |
bd9231e4 | 489 | auto mapper = detail::range_mapper<buffered_context<Char>>(); |
05aa7e19 | 490 | auto out = ctx.out(); |
8b75cd77 JG |
491 | auto it = detail::range_begin(range); |
492 | auto end = detail::range_end(range); | |
bd9231e4 JG |
493 | if (is_debug) return write_debug_string(out, std::move(it), end); |
494 | ||
495 | out = detail::copy<Char>(opening_bracket_, out); | |
496 | int i = 0; | |
05aa7e19 | 497 | for (; it != end; ++it) { |
bd9231e4 | 498 | if (i > 0) out = detail::copy<Char>(separator_, out); |
8b75cd77 | 499 | ctx.advance_to(out); |
bd9231e4 JG |
500 | auto&& item = *it; // Need an lvalue |
501 | out = underlying_.format(mapper.map(item), ctx); | |
05aa7e19 JG |
502 | ++i; |
503 | } | |
bd9231e4 | 504 | out = detail::copy<Char>(closing_bracket_, out); |
05aa7e19 JG |
505 | return out; |
506 | } | |
507 | }; | |
508 | ||
bd9231e4 JG |
509 | FMT_EXPORT |
510 | template <typename T, typename Char, typename Enable = void> | |
511 | struct range_format_kind | |
512 | : conditional_t< | |
513 | is_range<T, Char>::value, detail::range_format_kind_<T>, | |
514 | std::integral_constant<range_format, range_format::disabled>> {}; | |
8b75cd77 | 515 | |
bd9231e4 JG |
516 | template <typename R, typename Char> |
517 | struct formatter< | |
518 | R, Char, | |
519 | enable_if_t<conjunction< | |
520 | bool_constant< | |
521 | range_format_kind<R, Char>::value != range_format::disabled && | |
522 | range_format_kind<R, Char>::value != range_format::map && | |
523 | range_format_kind<R, Char>::value != range_format::string && | |
524 | range_format_kind<R, Char>::value != range_format::debug_string> | |
525 | // Workaround a bug in MSVC 2015 and earlier. | |
526 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | |
527 | , | |
528 | detail::is_formattable_delayed<R, Char> | |
529 | #endif | |
530 | >::value>> { | |
531 | private: | |
532 | using range_type = detail::maybe_const_range<R>; | |
533 | range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_; | |
8b75cd77 | 534 | |
bd9231e4 JG |
535 | public: |
536 | using nonlocking = void; | |
537 | ||
538 | FMT_CONSTEXPR formatter() { | |
539 | if (detail::const_check(range_format_kind<R, Char>::value != | |
540 | range_format::set)) | |
541 | return; | |
542 | range_formatter_.set_brackets(detail::string_literal<Char, '{'>{}, | |
543 | detail::string_literal<Char, '}'>{}); | |
544 | } | |
8b75cd77 | 545 | |
bd9231e4 JG |
546 | template <typename ParseContext> |
547 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
548 | return range_formatter_.parse(ctx); | |
549 | } | |
8b75cd77 | 550 | |
bd9231e4 JG |
551 | template <typename FormatContext> |
552 | auto format(range_type& range, FormatContext& ctx) const | |
553 | -> decltype(ctx.out()) { | |
554 | return range_formatter_.format(range, ctx); | |
555 | } | |
556 | }; | |
8b75cd77 | 557 | |
bd9231e4 JG |
558 | // A map formatter. |
559 | template <typename R, typename Char> | |
560 | struct formatter< | |
561 | R, Char, | |
562 | enable_if_t<range_format_kind<R, Char>::value == range_format::map>> { | |
563 | private: | |
564 | using map_type = detail::maybe_const_range<R>; | |
565 | using element_type = detail::uncvref_type<map_type>; | |
566 | ||
567 | decltype(detail::tuple::get_formatters<element_type, Char>( | |
568 | detail::tuple_index_sequence<element_type>())) formatters_; | |
569 | bool no_delimiters_ = false; | |
570 | ||
571 | public: | |
572 | FMT_CONSTEXPR formatter() {} | |
8b75cd77 | 573 | |
bd9231e4 JG |
574 | template <typename ParseContext> |
575 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
576 | auto it = ctx.begin(); | |
577 | auto end = ctx.end(); | |
578 | if (it != end) { | |
579 | if (detail::to_ascii(*it) == 'n') { | |
580 | no_delimiters_ = true; | |
581 | ++it; | |
582 | } | |
583 | if (it != end && *it != '}') { | |
584 | if (*it != ':') report_error("invalid format specifier"); | |
585 | ++it; | |
586 | } | |
587 | ctx.advance_to(it); | |
588 | } | |
589 | detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx}); | |
590 | return it; | |
8b75cd77 JG |
591 | } |
592 | ||
bd9231e4 JG |
593 | template <typename FormatContext> |
594 | auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { | |
595 | auto out = ctx.out(); | |
596 | basic_string_view<Char> open = detail::string_literal<Char, '{'>{}; | |
597 | if (!no_delimiters_) out = detail::copy<Char>(open, out); | |
598 | int i = 0; | |
599 | auto mapper = detail::range_mapper<buffered_context<Char>>(); | |
600 | basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{}; | |
601 | for (auto&& value : map) { | |
602 | if (i > 0) out = detail::copy<Char>(sep, out); | |
603 | ctx.advance_to(out); | |
604 | detail::for_each2(formatters_, mapper.map(value), | |
605 | detail::format_tuple_element<FormatContext>{ | |
606 | 0, ctx, detail::string_literal<Char, ':', ' '>{}}); | |
607 | ++i; | |
608 | } | |
609 | basic_string_view<Char> close = detail::string_literal<Char, '}'>{}; | |
610 | if (!no_delimiters_) out = detail::copy<Char>(close, out); | |
611 | return out; | |
8b75cd77 | 612 | } |
bd9231e4 JG |
613 | }; |
614 | ||
615 | // A (debug_)string formatter. | |
616 | template <typename R, typename Char> | |
617 | struct formatter< | |
618 | R, Char, | |
619 | enable_if_t<range_format_kind<R, Char>::value == range_format::string || | |
620 | range_format_kind<R, Char>::value == | |
621 | range_format::debug_string>> { | |
622 | private: | |
623 | using range_type = detail::maybe_const_range<R>; | |
624 | using string_type = | |
625 | conditional_t<std::is_constructible< | |
626 | detail::std_string_view<Char>, | |
627 | decltype(detail::range_begin(std::declval<R>())), | |
628 | decltype(detail::range_end(std::declval<R>()))>::value, | |
629 | detail::std_string_view<Char>, std::basic_string<Char>>; | |
8b75cd77 | 630 | |
bd9231e4 | 631 | formatter<string_type, Char> underlying_; |
8b75cd77 | 632 | |
bd9231e4 | 633 | public: |
05aa7e19 JG |
634 | template <typename ParseContext> |
635 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
8b75cd77 | 636 | return underlying_.parse(ctx); |
05aa7e19 JG |
637 | } |
638 | ||
8b75cd77 JG |
639 | template <typename FormatContext> |
640 | auto format(range_type& range, FormatContext& ctx) const | |
641 | -> decltype(ctx.out()) { | |
bd9231e4 JG |
642 | auto out = ctx.out(); |
643 | if (detail::const_check(range_format_kind<R, Char>::value == | |
644 | range_format::debug_string)) | |
645 | *out++ = '"'; | |
646 | out = underlying_.format( | |
647 | string_type{detail::range_begin(range), detail::range_end(range)}, ctx); | |
648 | if (detail::const_check(range_format_kind<R, Char>::value == | |
649 | range_format::debug_string)) | |
650 | *out++ = '"'; | |
651 | return out; | |
05aa7e19 JG |
652 | } |
653 | }; | |
8b75cd77 | 654 | |
bd9231e4 JG |
655 | template <typename It, typename Sentinel, typename Char = char> |
656 | struct join_view : detail::view { | |
657 | It begin; | |
658 | Sentinel end; | |
659 | basic_string_view<Char> sep; | |
8b75cd77 | 660 | |
bd9231e4 JG |
661 | join_view(It b, Sentinel e, basic_string_view<Char> s) |
662 | : begin(std::move(b)), end(e), sep(s) {} | |
663 | }; | |
664 | ||
665 | template <typename It, typename Sentinel, typename Char> | |
666 | struct formatter<join_view<It, Sentinel, Char>, Char> { | |
667 | private: | |
668 | using value_type = | |
669 | #ifdef __cpp_lib_ranges | |
670 | std::iter_value_t<It>; | |
671 | #else | |
672 | typename std::iterator_traits<It>::value_type; | |
8b75cd77 | 673 | #endif |
bd9231e4 JG |
674 | formatter<remove_cvref_t<value_type>, Char> value_formatter_; |
675 | ||
676 | using view_ref = conditional_t<std::is_copy_constructible<It>::value, | |
677 | const join_view<It, Sentinel, Char>&, | |
678 | join_view<It, Sentinel, Char>&&>; | |
679 | ||
680 | public: | |
681 | using nonlocking = void; | |
682 | ||
683 | template <typename ParseContext> | |
684 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { | |
685 | return value_formatter_.parse(ctx); | |
686 | } | |
687 | ||
688 | template <typename FormatContext> | |
689 | auto format(view_ref& value, FormatContext& ctx) const | |
690 | -> decltype(ctx.out()) { | |
691 | auto it = std::forward<view_ref>(value).begin; | |
692 | auto out = ctx.out(); | |
693 | if (it == value.end) return out; | |
694 | out = value_formatter_.format(*it, ctx); | |
695 | ++it; | |
696 | while (it != value.end) { | |
697 | out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out); | |
698 | ctx.advance_to(out); | |
699 | out = value_formatter_.format(*it, ctx); | |
700 | ++it; | |
701 | } | |
702 | return out; | |
703 | } | |
8b75cd77 | 704 | }; |
05aa7e19 | 705 | |
bd9231e4 JG |
706 | /// Returns a view that formats the iterator range `[begin, end)` with elements |
707 | /// separated by `sep`. | |
708 | template <typename It, typename Sentinel> | |
709 | auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> { | |
710 | return {std::move(begin), end, sep}; | |
711 | } | |
712 | ||
713 | /** | |
714 | * Returns a view that formats `range` with elements separated by `sep`. | |
715 | * | |
716 | * **Example**: | |
717 | * | |
718 | * auto v = std::vector<int>{1, 2, 3}; | |
719 | * fmt::print("{}", fmt::join(v, ", ")); | |
720 | * // Output: 1, 2, 3 | |
721 | * | |
722 | * `fmt::join` applies passed format specifiers to the range elements: | |
723 | * | |
724 | * fmt::print("{:02}", fmt::join(v, ", ")); | |
725 | * // Output: 01, 02, 03 | |
726 | */ | |
727 | template <typename Range> | |
728 | auto join(Range&& r, string_view sep) | |
729 | -> join_view<decltype(detail::range_begin(r)), | |
730 | decltype(detail::range_end(r))> { | |
731 | return {detail::range_begin(r), detail::range_end(r), sep}; | |
732 | } | |
733 | ||
05aa7e19 JG |
734 | template <typename Char, typename... T> struct tuple_join_view : detail::view { |
735 | const std::tuple<T...>& tuple; | |
736 | basic_string_view<Char> sep; | |
737 | ||
738 | tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) | |
739 | : tuple(t), sep{s} {} | |
740 | }; | |
741 | ||
05aa7e19 JG |
742 | // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers |
743 | // support in tuple_join. It is disabled by default because of issues with | |
744 | // the dynamic width and precision. | |
745 | #ifndef FMT_TUPLE_JOIN_SPECIFIERS | |
746 | # define FMT_TUPLE_JOIN_SPECIFIERS 0 | |
747 | #endif | |
748 | ||
749 | template <typename Char, typename... T> | |
750 | struct formatter<tuple_join_view<Char, T...>, Char> { | |
751 | template <typename ParseContext> | |
752 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
753 | return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); | |
754 | } | |
755 | ||
756 | template <typename FormatContext> | |
757 | auto format(const tuple_join_view<Char, T...>& value, | |
758 | FormatContext& ctx) const -> typename FormatContext::iterator { | |
759 | return do_format(value, ctx, | |
760 | std::integral_constant<size_t, sizeof...(T)>()); | |
761 | } | |
762 | ||
763 | private: | |
764 | std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; | |
765 | ||
766 | template <typename ParseContext> | |
767 | FMT_CONSTEXPR auto do_parse(ParseContext& ctx, | |
768 | std::integral_constant<size_t, 0>) | |
769 | -> decltype(ctx.begin()) { | |
770 | return ctx.begin(); | |
771 | } | |
772 | ||
773 | template <typename ParseContext, size_t N> | |
774 | FMT_CONSTEXPR auto do_parse(ParseContext& ctx, | |
775 | std::integral_constant<size_t, N>) | |
776 | -> decltype(ctx.begin()) { | |
777 | auto end = ctx.begin(); | |
778 | #if FMT_TUPLE_JOIN_SPECIFIERS | |
779 | end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); | |
780 | if (N > 1) { | |
781 | auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); | |
782 | if (end != end1) | |
bd9231e4 | 783 | report_error("incompatible format specs for tuple elements"); |
05aa7e19 JG |
784 | } |
785 | #endif | |
786 | return end; | |
787 | } | |
788 | ||
789 | template <typename FormatContext> | |
790 | auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, | |
791 | std::integral_constant<size_t, 0>) const -> | |
792 | typename FormatContext::iterator { | |
793 | return ctx.out(); | |
794 | } | |
795 | ||
796 | template <typename FormatContext, size_t N> | |
797 | auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, | |
798 | std::integral_constant<size_t, N>) const -> | |
799 | typename FormatContext::iterator { | |
800 | auto out = std::get<sizeof...(T) - N>(formatters_) | |
801 | .format(std::get<sizeof...(T) - N>(value.tuple), ctx); | |
bd9231e4 JG |
802 | if (N <= 1) return out; |
803 | out = detail::copy<Char>(value.sep, out); | |
804 | ctx.advance_to(out); | |
805 | return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); | |
05aa7e19 JG |
806 | } |
807 | }; | |
808 | ||
bd9231e4 JG |
809 | namespace detail { |
810 | // Check if T has an interface like a container adaptor (e.g. std::stack, | |
811 | // std::queue, std::priority_queue). | |
812 | template <typename T> class is_container_adaptor_like { | |
813 | template <typename U> static auto check(U* p) -> typename U::container_type; | |
814 | template <typename> static void check(...); | |
05aa7e19 | 815 | |
bd9231e4 JG |
816 | public: |
817 | static constexpr const bool value = | |
818 | !std::is_void<decltype(check<T>(nullptr))>::value; | |
819 | }; | |
05aa7e19 | 820 | |
bd9231e4 JG |
821 | template <typename Container> struct all { |
822 | const Container& c; | |
823 | auto begin() const -> typename Container::const_iterator { return c.begin(); } | |
824 | auto end() const -> typename Container::const_iterator { return c.end(); } | |
825 | }; | |
826 | } // namespace detail | |
05aa7e19 | 827 | |
bd9231e4 JG |
828 | template <typename T, typename Char> |
829 | struct formatter< | |
830 | T, Char, | |
831 | enable_if_t<conjunction<detail::is_container_adaptor_like<T>, | |
832 | bool_constant<range_format_kind<T, Char>::value == | |
833 | range_format::disabled>>::value>> | |
834 | : formatter<detail::all<typename T::container_type>, Char> { | |
835 | using all = detail::all<typename T::container_type>; | |
836 | template <typename FormatContext> | |
837 | auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { | |
838 | struct getter : T { | |
839 | static auto get(const T& t) -> all { | |
840 | return {t.*(&getter::c)}; // Access c through the derived class. | |
841 | } | |
842 | }; | |
843 | return formatter<all>::format(getter::get(t), ctx); | |
844 | } | |
845 | }; | |
846 | ||
847 | FMT_BEGIN_EXPORT | |
848 | ||
849 | /** | |
850 | * Returns an object that formats `std::tuple` with elements separated by `sep`. | |
851 | * | |
852 | * **Example**: | |
853 | * | |
854 | * auto t = std::tuple<int, char>{1, 'a'}; | |
855 | * fmt::print("{}", fmt::join(t, ", ")); | |
856 | * // Output: 1, a | |
05aa7e19 JG |
857 | */ |
858 | template <typename... T> | |
859 | FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) | |
860 | -> tuple_join_view<char, T...> { | |
861 | return {tuple, sep}; | |
862 | } | |
863 | ||
05aa7e19 | 864 | /** |
bd9231e4 JG |
865 | * Returns an object that formats `std::initializer_list` with elements |
866 | * separated by `sep`. | |
867 | * | |
868 | * **Example**: | |
869 | * | |
870 | * fmt::print("{}", fmt::join({1, 2, 3}, ", ")); | |
871 | * // Output: "1, 2, 3" | |
05aa7e19 JG |
872 | */ |
873 | template <typename T> | |
874 | auto join(std::initializer_list<T> list, string_view sep) | |
875 | -> join_view<const T*, const T*> { | |
876 | return join(std::begin(list), std::end(list), sep); | |
877 | } | |
878 | ||
bd9231e4 | 879 | FMT_END_EXPORT |
05aa7e19 JG |
880 | FMT_END_NAMESPACE |
881 | ||
882 | #endif // FMT_RANGES_H_ |