Commit | Line | Data |
---|---|---|
05aa7e19 JG |
1 | // Formatting library for C++ - experimental range support |
2 | // | |
3 | // Copyright (c) 2012 - present, Victor Zverovich | |
4 | // All rights reserved. | |
5 | // | |
6 | // For the license information refer to format.h. | |
7 | // | |
8 | // Copyright (c) 2018 - present, Remotion (Igor Schulz) | |
9 | // All Rights Reserved | |
10 | // {fmt} support for ranges, containers and types tuple interface. | |
11 | ||
12 | #ifndef FMT_RANGES_H_ | |
13 | #define FMT_RANGES_H_ | |
14 | ||
15 | #include <initializer_list> | |
16 | #include <tuple> | |
17 | #include <type_traits> | |
18 | ||
19 | #include "format.h" | |
20 | ||
21 | FMT_BEGIN_NAMESPACE | |
22 | ||
23 | namespace detail { | |
24 | ||
25 | template <typename RangeT, typename OutputIterator> | |
26 | OutputIterator copy(const RangeT& range, OutputIterator out) { | |
27 | for (auto it = range.begin(), end = range.end(); it != end; ++it) | |
28 | *out++ = *it; | |
29 | return out; | |
30 | } | |
31 | ||
32 | template <typename OutputIterator> | |
33 | OutputIterator copy(const char* str, OutputIterator out) { | |
34 | while (*str) *out++ = *str++; | |
35 | return out; | |
36 | } | |
37 | ||
38 | template <typename OutputIterator> | |
39 | OutputIterator copy(char ch, OutputIterator out) { | |
40 | *out++ = ch; | |
41 | return out; | |
42 | } | |
43 | ||
44 | template <typename OutputIterator> | |
45 | OutputIterator copy(wchar_t ch, OutputIterator out) { | |
46 | *out++ = ch; | |
47 | return out; | |
48 | } | |
49 | ||
50 | // Returns true if T has a std::string-like interface, like std::string_view. | |
51 | template <typename T> class is_std_string_like { | |
52 | template <typename U> | |
53 | static auto check(U* p) | |
54 | -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); | |
55 | template <typename> static void check(...); | |
56 | ||
57 | public: | |
8b75cd77 | 58 | static constexpr const bool value = |
05aa7e19 JG |
59 | is_string<T>::value || |
60 | std::is_convertible<T, std_string_view<char>>::value || | |
61 | !std::is_void<decltype(check<T>(nullptr))>::value; | |
62 | }; | |
63 | ||
64 | template <typename Char> | |
65 | struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {}; | |
66 | ||
67 | template <typename T> class is_map { | |
68 | template <typename U> static auto check(U*) -> typename U::mapped_type; | |
69 | template <typename> static void check(...); | |
70 | ||
71 | public: | |
72 | #ifdef FMT_FORMAT_MAP_AS_LIST | |
8b75cd77 | 73 | static constexpr const bool value = false; |
05aa7e19 | 74 | #else |
8b75cd77 | 75 | static constexpr const bool value = |
05aa7e19 JG |
76 | !std::is_void<decltype(check<T>(nullptr))>::value; |
77 | #endif | |
78 | }; | |
79 | ||
80 | template <typename T> class is_set { | |
81 | template <typename U> static auto check(U*) -> typename U::key_type; | |
82 | template <typename> static void check(...); | |
83 | ||
84 | public: | |
85 | #ifdef FMT_FORMAT_SET_AS_LIST | |
8b75cd77 | 86 | static constexpr const bool value = false; |
05aa7e19 | 87 | #else |
8b75cd77 | 88 | static constexpr const bool value = |
05aa7e19 JG |
89 | !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; |
90 | #endif | |
91 | }; | |
92 | ||
93 | template <typename... Ts> struct conditional_helper {}; | |
94 | ||
95 | template <typename T, typename _ = void> struct is_range_ : std::false_type {}; | |
96 | ||
8b75cd77 | 97 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 |
05aa7e19 JG |
98 | |
99 | # define FMT_DECLTYPE_RETURN(val) \ | |
100 | ->decltype(val) { return val; } \ | |
101 | static_assert( \ | |
102 | true, "") // This makes it so that a semicolon is required after the | |
103 | // macro, which helps clang-format handle the formatting. | |
104 | ||
105 | // C array overload | |
106 | template <typename T, std::size_t N> | |
107 | auto range_begin(const T (&arr)[N]) -> const T* { | |
108 | return arr; | |
109 | } | |
110 | template <typename T, std::size_t N> | |
111 | auto range_end(const T (&arr)[N]) -> const T* { | |
112 | return arr + N; | |
113 | } | |
114 | ||
115 | template <typename T, typename Enable = void> | |
116 | struct has_member_fn_begin_end_t : std::false_type {}; | |
117 | ||
118 | template <typename T> | |
119 | struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), | |
120 | decltype(std::declval<T>().end())>> | |
121 | : std::true_type {}; | |
122 | ||
123 | // Member function overload | |
124 | template <typename T> | |
125 | auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); | |
126 | template <typename T> | |
127 | auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); | |
128 | ||
129 | // ADL overload. Only participates in overload resolution if member functions | |
130 | // are not found. | |
131 | template <typename T> | |
132 | auto range_begin(T&& rng) | |
133 | -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, | |
134 | decltype(begin(static_cast<T&&>(rng)))> { | |
135 | return begin(static_cast<T&&>(rng)); | |
136 | } | |
137 | template <typename T> | |
138 | auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, | |
139 | decltype(end(static_cast<T&&>(rng)))> { | |
140 | return end(static_cast<T&&>(rng)); | |
141 | } | |
142 | ||
143 | template <typename T, typename Enable = void> | |
144 | struct has_const_begin_end : std::false_type {}; | |
145 | template <typename T, typename Enable = void> | |
146 | struct has_mutable_begin_end : std::false_type {}; | |
147 | ||
148 | template <typename T> | |
149 | struct has_const_begin_end< | |
150 | T, | |
151 | void_t< | |
152 | decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())), | |
153 | decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>> | |
154 | : std::true_type {}; | |
155 | ||
156 | template <typename T> | |
157 | struct has_mutable_begin_end< | |
158 | T, void_t<decltype(detail::range_begin(std::declval<T>())), | |
159 | decltype(detail::range_end(std::declval<T>())), | |
160 | enable_if_t<std::is_copy_constructible<T>::value>>> | |
161 | : std::true_type {}; | |
162 | ||
163 | template <typename T> | |
164 | struct is_range_<T, void> | |
165 | : std::integral_constant<bool, (has_const_begin_end<T>::value || | |
166 | has_mutable_begin_end<T>::value)> {}; | |
167 | # undef FMT_DECLTYPE_RETURN | |
168 | #endif | |
169 | ||
170 | // tuple_size and tuple_element check. | |
171 | template <typename T> class is_tuple_like_ { | |
172 | template <typename U> | |
173 | static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); | |
174 | template <typename> static void check(...); | |
175 | ||
176 | public: | |
8b75cd77 | 177 | static constexpr const bool value = |
05aa7e19 JG |
178 | !std::is_void<decltype(check<T>(nullptr))>::value; |
179 | }; | |
180 | ||
181 | // Check for integer_sequence | |
8b75cd77 | 182 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 |
05aa7e19 JG |
183 | template <typename T, T... N> |
184 | using integer_sequence = std::integer_sequence<T, N...>; | |
185 | template <size_t... N> using index_sequence = std::index_sequence<N...>; | |
186 | template <size_t N> using make_index_sequence = std::make_index_sequence<N>; | |
187 | #else | |
188 | template <typename T, T... N> struct integer_sequence { | |
189 | using value_type = T; | |
190 | ||
191 | static FMT_CONSTEXPR size_t size() { return sizeof...(N); } | |
192 | }; | |
193 | ||
194 | template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; | |
195 | ||
196 | template <typename T, size_t N, T... Ns> | |
197 | struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; | |
198 | template <typename T, T... Ns> | |
199 | struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; | |
200 | ||
201 | template <size_t N> | |
202 | using make_index_sequence = make_integer_sequence<size_t, N>; | |
203 | #endif | |
204 | ||
8b75cd77 JG |
205 | template <typename T> |
206 | using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>; | |
207 | ||
208 | template <typename T, typename C, bool = is_tuple_like_<T>::value> | |
209 | class is_tuple_formattable_ { | |
210 | public: | |
211 | static constexpr const bool value = false; | |
212 | }; | |
213 | template <typename T, typename C> class is_tuple_formattable_<T, C, true> { | |
214 | template <std::size_t... I> | |
215 | static std::true_type check2(index_sequence<I...>, | |
216 | integer_sequence<bool, (I == I)...>); | |
217 | static std::false_type check2(...); | |
218 | template <std::size_t... I> | |
219 | static decltype(check2( | |
220 | index_sequence<I...>{}, | |
221 | integer_sequence< | |
222 | bool, (is_formattable<typename std::tuple_element<I, T>::type, | |
223 | C>::value)...>{})) check(index_sequence<I...>); | |
224 | ||
225 | public: | |
226 | static constexpr const bool value = | |
227 | decltype(check(tuple_index_sequence<T>{}))::value; | |
228 | }; | |
229 | ||
05aa7e19 | 230 | template <class Tuple, class F, size_t... Is> |
8b75cd77 | 231 | void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept { |
05aa7e19 JG |
232 | using std::get; |
233 | // using free function get<I>(T) now. | |
234 | const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; | |
235 | (void)_; // blocks warnings | |
236 | } | |
237 | ||
238 | template <class T> | |
239 | FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( | |
240 | T const&) { | |
241 | return {}; | |
242 | } | |
243 | ||
244 | template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { | |
245 | const auto indexes = get_indexes(tup); | |
246 | for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); | |
247 | } | |
248 | ||
8b75cd77 JG |
249 | #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 |
250 | // Older MSVC doesn't get the reference type correctly for arrays. | |
251 | template <typename R> struct range_reference_type_impl { | |
252 | using type = decltype(*detail::range_begin(std::declval<R&>())); | |
05aa7e19 JG |
253 | }; |
254 | ||
8b75cd77 JG |
255 | template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> { |
256 | using type = T&; | |
257 | }; | |
05aa7e19 | 258 | |
8b75cd77 JG |
259 | template <typename T> |
260 | using range_reference_type = typename range_reference_type_impl<T>::type; | |
261 | #else | |
262 | template <typename Range> | |
263 | using range_reference_type = | |
264 | decltype(*detail::range_begin(std::declval<Range&>())); | |
265 | #endif | |
05aa7e19 | 266 | |
8b75cd77 JG |
267 | // We don't use the Range's value_type for anything, but we do need the Range's |
268 | // reference type, with cv-ref stripped. | |
269 | template <typename Range> | |
270 | using uncvref_type = remove_cvref_t<range_reference_type<Range>>; | |
05aa7e19 | 271 | |
8b75cd77 JG |
272 | template <typename Range> |
273 | using uncvref_first_type = | |
274 | remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>; | |
05aa7e19 | 275 | |
8b75cd77 JG |
276 | template <typename Range> |
277 | using uncvref_second_type = remove_cvref_t< | |
278 | decltype(std::declval<range_reference_type<Range>>().second)>; | |
05aa7e19 | 279 | |
8b75cd77 JG |
280 | template <typename OutputIt> OutputIt write_delimiter(OutputIt out) { |
281 | *out++ = ','; | |
282 | *out++ = ' '; | |
283 | return out; | |
05aa7e19 JG |
284 | } |
285 | ||
286 | template <typename Char, typename OutputIt> | |
287 | auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { | |
8b75cd77 | 288 | return write_escaped_string(out, str); |
05aa7e19 JG |
289 | } |
290 | ||
291 | template <typename Char, typename OutputIt, typename T, | |
292 | FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)> | |
293 | inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { | |
294 | auto sv = std_string_view<Char>(str); | |
295 | return write_range_entry<Char>(out, basic_string_view<Char>(sv)); | |
296 | } | |
297 | ||
298 | template <typename Char, typename OutputIt, typename Arg, | |
299 | FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> | |
300 | OutputIt write_range_entry(OutputIt out, const Arg v) { | |
8b75cd77 | 301 | return write_escaped_char(out, v); |
05aa7e19 JG |
302 | } |
303 | ||
304 | template < | |
305 | typename Char, typename OutputIt, typename Arg, | |
306 | FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value && | |
307 | !std::is_same<Arg, Char>::value)> | |
308 | OutputIt write_range_entry(OutputIt out, const Arg& v) { | |
309 | return write<Char>(out, v); | |
310 | } | |
311 | ||
312 | } // namespace detail | |
313 | ||
314 | template <typename T> struct is_tuple_like { | |
8b75cd77 | 315 | static constexpr const bool value = |
05aa7e19 JG |
316 | detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; |
317 | }; | |
318 | ||
8b75cd77 JG |
319 | template <typename T, typename C> struct is_tuple_formattable { |
320 | static constexpr const bool value = | |
321 | detail::is_tuple_formattable_<T, C>::value; | |
322 | }; | |
323 | ||
05aa7e19 | 324 | template <typename TupleT, typename Char> |
8b75cd77 JG |
325 | struct formatter<TupleT, Char, |
326 | enable_if_t<fmt::is_tuple_like<TupleT>::value && | |
327 | fmt::is_tuple_formattable<TupleT, Char>::value>> { | |
05aa7e19 | 328 | private: |
8b75cd77 JG |
329 | basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; |
330 | basic_string_view<Char> opening_bracket_ = | |
331 | detail::string_literal<Char, '('>{}; | |
332 | basic_string_view<Char> closing_bracket_ = | |
333 | detail::string_literal<Char, ')'>{}; | |
334 | ||
05aa7e19 JG |
335 | // C++11 generic lambda for format(). |
336 | template <typename FormatContext> struct format_each { | |
337 | template <typename T> void operator()(const T& v) { | |
8b75cd77 | 338 | if (i > 0) out = detail::copy_str<Char>(separator, out); |
05aa7e19 JG |
339 | out = detail::write_range_entry<Char>(out, v); |
340 | ++i; | |
341 | } | |
342 | int i; | |
343 | typename FormatContext::iterator& out; | |
8b75cd77 | 344 | basic_string_view<Char> separator; |
05aa7e19 JG |
345 | }; |
346 | ||
347 | public: | |
8b75cd77 JG |
348 | FMT_CONSTEXPR formatter() {} |
349 | ||
350 | FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { | |
351 | separator_ = sep; | |
352 | } | |
353 | ||
354 | FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, | |
355 | basic_string_view<Char> close) { | |
356 | opening_bracket_ = open; | |
357 | closing_bracket_ = close; | |
358 | } | |
359 | ||
05aa7e19 JG |
360 | template <typename ParseContext> |
361 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
362 | return ctx.begin(); | |
363 | } | |
364 | ||
365 | template <typename FormatContext = format_context> | |
8b75cd77 JG |
366 | auto format(const TupleT& values, FormatContext& ctx) const |
367 | -> decltype(ctx.out()) { | |
05aa7e19 | 368 | auto out = ctx.out(); |
8b75cd77 JG |
369 | out = detail::copy_str<Char>(opening_bracket_, out); |
370 | detail::for_each(values, format_each<FormatContext>{0, out, separator_}); | |
371 | out = detail::copy_str<Char>(closing_bracket_, out); | |
05aa7e19 JG |
372 | return out; |
373 | } | |
374 | }; | |
375 | ||
376 | template <typename T, typename Char> struct is_range { | |
8b75cd77 | 377 | static constexpr const bool value = |
05aa7e19 | 378 | detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && |
05aa7e19 | 379 | !std::is_convertible<T, std::basic_string<Char>>::value && |
8b75cd77 JG |
380 | !std::is_convertible<T, detail::std_string_view<Char>>::value; |
381 | }; | |
382 | ||
383 | namespace detail { | |
384 | template <typename Context> struct range_mapper { | |
385 | using mapper = arg_mapper<Context>; | |
386 | ||
387 | template <typename T, | |
388 | FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)> | |
389 | static auto map(T&& value) -> T&& { | |
390 | return static_cast<T&&>(value); | |
391 | } | |
392 | template <typename T, | |
393 | FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)> | |
394 | static auto map(T&& value) | |
395 | -> decltype(mapper().map(static_cast<T&&>(value))) { | |
396 | return mapper().map(static_cast<T&&>(value)); | |
397 | } | |
05aa7e19 JG |
398 | }; |
399 | ||
8b75cd77 JG |
400 | template <typename Char, typename Element> |
401 | using range_formatter_type = conditional_t< | |
402 | is_formattable<Element, Char>::value, | |
403 | formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( | |
404 | std::declval<Element>()))>, | |
405 | Char>, | |
406 | fallback_formatter<Element, Char>>; | |
407 | ||
408 | template <typename R> | |
409 | using maybe_const_range = | |
410 | conditional_t<has_const_begin_end<R>::value, const R, R>; | |
411 | ||
412 | // Workaround a bug in MSVC 2015 and earlier. | |
413 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | |
414 | template <typename R, typename Char> | |
415 | struct is_formattable_delayed | |
416 | : disjunction< | |
417 | is_formattable<uncvref_type<maybe_const_range<R>>, Char>, | |
418 | has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {}; | |
419 | #endif | |
420 | ||
421 | } // namespace detail | |
422 | ||
423 | template <typename T, typename Char, typename Enable = void> | |
424 | struct range_formatter; | |
425 | ||
05aa7e19 | 426 | template <typename T, typename Char> |
8b75cd77 | 427 | struct range_formatter< |
05aa7e19 | 428 | T, Char, |
8b75cd77 JG |
429 | enable_if_t<conjunction< |
430 | std::is_same<T, remove_cvref_t<T>>, | |
431 | disjunction<is_formattable<T, Char>, | |
432 | detail::has_fallback_formatter<T, Char>>>::value>> { | |
433 | private: | |
434 | detail::range_formatter_type<Char, T> underlying_; | |
435 | bool custom_specs_ = false; | |
436 | basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; | |
437 | basic_string_view<Char> opening_bracket_ = | |
438 | detail::string_literal<Char, '['>{}; | |
439 | basic_string_view<Char> closing_bracket_ = | |
440 | detail::string_literal<Char, ']'>{}; | |
441 | ||
442 | template <class U> | |
443 | FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int) | |
444 | -> decltype(u.set_debug_format()) { | |
445 | u.set_debug_format(); | |
446 | } | |
447 | ||
448 | template <class U> | |
449 | FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} | |
450 | ||
451 | FMT_CONSTEXPR void maybe_set_debug_format() { | |
452 | maybe_set_debug_format(underlying_, 0); | |
453 | } | |
454 | ||
455 | public: | |
456 | FMT_CONSTEXPR range_formatter() {} | |
457 | ||
458 | FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& { | |
459 | return underlying_; | |
460 | } | |
461 | ||
462 | FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { | |
463 | separator_ = sep; | |
464 | } | |
465 | ||
466 | FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, | |
467 | basic_string_view<Char> close) { | |
468 | opening_bracket_ = open; | |
469 | closing_bracket_ = close; | |
470 | } | |
471 | ||
05aa7e19 JG |
472 | template <typename ParseContext> |
473 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
8b75cd77 JG |
474 | auto it = ctx.begin(); |
475 | auto end = ctx.end(); | |
476 | if (it == end || *it == '}') { | |
477 | maybe_set_debug_format(); | |
478 | return it; | |
479 | } | |
480 | ||
481 | if (*it == 'n') { | |
482 | set_brackets({}, {}); | |
483 | ++it; | |
484 | } | |
485 | ||
486 | if (*it == '}') { | |
487 | maybe_set_debug_format(); | |
488 | return it; | |
489 | } | |
490 | ||
491 | if (*it != ':') | |
492 | FMT_THROW(format_error("no other top-level range formatters supported")); | |
493 | ||
494 | custom_specs_ = true; | |
495 | ++it; | |
496 | ctx.advance_to(it); | |
497 | return underlying_.parse(ctx); | |
05aa7e19 JG |
498 | } |
499 | ||
8b75cd77 JG |
500 | template <typename R, class FormatContext> |
501 | auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { | |
502 | detail::range_mapper<buffer_context<Char>> mapper; | |
05aa7e19 | 503 | auto out = ctx.out(); |
8b75cd77 | 504 | out = detail::copy_str<Char>(opening_bracket_, out); |
05aa7e19 | 505 | int i = 0; |
8b75cd77 JG |
506 | auto it = detail::range_begin(range); |
507 | auto end = detail::range_end(range); | |
05aa7e19 | 508 | for (; it != end; ++it) { |
8b75cd77 JG |
509 | if (i > 0) out = detail::copy_str<Char>(separator_, out); |
510 | ; | |
511 | ctx.advance_to(out); | |
512 | out = underlying_.format(mapper.map(*it), ctx); | |
05aa7e19 JG |
513 | ++i; |
514 | } | |
8b75cd77 | 515 | out = detail::copy_str<Char>(closing_bracket_, out); |
05aa7e19 JG |
516 | return out; |
517 | } | |
518 | }; | |
519 | ||
8b75cd77 JG |
520 | enum class range_format { disabled, map, set, sequence, string, debug_string }; |
521 | ||
522 | namespace detail { | |
523 | template <typename T> struct range_format_kind_ { | |
524 | static constexpr auto value = std::is_same<range_reference_type<T>, T>::value | |
525 | ? range_format::disabled | |
526 | : is_map<T>::value ? range_format::map | |
527 | : is_set<T>::value ? range_format::set | |
528 | : range_format::sequence; | |
529 | }; | |
530 | ||
531 | template <range_format K, typename R, typename Char, typename Enable = void> | |
532 | struct range_default_formatter; | |
533 | ||
534 | template <range_format K> | |
535 | using range_format_constant = std::integral_constant<range_format, K>; | |
536 | ||
537 | template <range_format K, typename R, typename Char> | |
538 | struct range_default_formatter< | |
539 | K, R, Char, | |
540 | enable_if_t<(K == range_format::sequence || K == range_format::map || | |
541 | K == range_format::set)>> { | |
542 | using range_type = detail::maybe_const_range<R>; | |
543 | range_formatter<detail::uncvref_type<range_type>, Char> underlying_; | |
544 | ||
545 | FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); } | |
546 | ||
547 | FMT_CONSTEXPR void init(range_format_constant<range_format::set>) { | |
548 | underlying_.set_brackets(detail::string_literal<Char, '{'>{}, | |
549 | detail::string_literal<Char, '}'>{}); | |
550 | } | |
551 | ||
552 | FMT_CONSTEXPR void init(range_format_constant<range_format::map>) { | |
553 | underlying_.set_brackets(detail::string_literal<Char, '{'>{}, | |
554 | detail::string_literal<Char, '}'>{}); | |
555 | underlying_.underlying().set_brackets({}, {}); | |
556 | underlying_.underlying().set_separator( | |
557 | detail::string_literal<Char, ':', ' '>{}); | |
558 | } | |
559 | ||
560 | FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {} | |
561 | ||
05aa7e19 JG |
562 | template <typename ParseContext> |
563 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
8b75cd77 | 564 | return underlying_.parse(ctx); |
05aa7e19 JG |
565 | } |
566 | ||
8b75cd77 JG |
567 | template <typename FormatContext> |
568 | auto format(range_type& range, FormatContext& ctx) const | |
569 | -> decltype(ctx.out()) { | |
570 | return underlying_.format(range, ctx); | |
05aa7e19 JG |
571 | } |
572 | }; | |
8b75cd77 JG |
573 | } // namespace detail |
574 | ||
575 | template <typename T, typename Char, typename Enable = void> | |
576 | struct range_format_kind | |
577 | : conditional_t< | |
578 | is_range<T, Char>::value, detail::range_format_kind_<T>, | |
579 | std::integral_constant<range_format, range_format::disabled>> {}; | |
580 | ||
581 | template <typename R, typename Char> | |
582 | struct formatter< | |
583 | R, Char, | |
584 | enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != | |
585 | range_format::disabled> | |
586 | // Workaround a bug in MSVC 2015 and earlier. | |
587 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | |
588 | , | |
589 | detail::is_formattable_delayed<R, Char> | |
590 | #endif | |
591 | >::value>> | |
592 | : detail::range_default_formatter<range_format_kind<R, Char>::value, R, | |
593 | Char> { | |
594 | }; | |
05aa7e19 JG |
595 | |
596 | template <typename Char, typename... T> struct tuple_join_view : detail::view { | |
597 | const std::tuple<T...>& tuple; | |
598 | basic_string_view<Char> sep; | |
599 | ||
600 | tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) | |
601 | : tuple(t), sep{s} {} | |
602 | }; | |
603 | ||
604 | template <typename Char, typename... T> | |
605 | using tuple_arg_join = tuple_join_view<Char, T...>; | |
606 | ||
607 | // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers | |
608 | // support in tuple_join. It is disabled by default because of issues with | |
609 | // the dynamic width and precision. | |
610 | #ifndef FMT_TUPLE_JOIN_SPECIFIERS | |
611 | # define FMT_TUPLE_JOIN_SPECIFIERS 0 | |
612 | #endif | |
613 | ||
614 | template <typename Char, typename... T> | |
615 | struct formatter<tuple_join_view<Char, T...>, Char> { | |
616 | template <typename ParseContext> | |
617 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
618 | return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); | |
619 | } | |
620 | ||
621 | template <typename FormatContext> | |
622 | auto format(const tuple_join_view<Char, T...>& value, | |
623 | FormatContext& ctx) const -> typename FormatContext::iterator { | |
624 | return do_format(value, ctx, | |
625 | std::integral_constant<size_t, sizeof...(T)>()); | |
626 | } | |
627 | ||
628 | private: | |
629 | std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; | |
630 | ||
631 | template <typename ParseContext> | |
632 | FMT_CONSTEXPR auto do_parse(ParseContext& ctx, | |
633 | std::integral_constant<size_t, 0>) | |
634 | -> decltype(ctx.begin()) { | |
635 | return ctx.begin(); | |
636 | } | |
637 | ||
638 | template <typename ParseContext, size_t N> | |
639 | FMT_CONSTEXPR auto do_parse(ParseContext& ctx, | |
640 | std::integral_constant<size_t, N>) | |
641 | -> decltype(ctx.begin()) { | |
642 | auto end = ctx.begin(); | |
643 | #if FMT_TUPLE_JOIN_SPECIFIERS | |
644 | end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); | |
645 | if (N > 1) { | |
646 | auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); | |
647 | if (end != end1) | |
648 | FMT_THROW(format_error("incompatible format specs for tuple elements")); | |
649 | } | |
650 | #endif | |
651 | return end; | |
652 | } | |
653 | ||
654 | template <typename FormatContext> | |
655 | auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, | |
656 | std::integral_constant<size_t, 0>) const -> | |
657 | typename FormatContext::iterator { | |
658 | return ctx.out(); | |
659 | } | |
660 | ||
661 | template <typename FormatContext, size_t N> | |
662 | auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, | |
663 | std::integral_constant<size_t, N>) const -> | |
664 | typename FormatContext::iterator { | |
665 | auto out = std::get<sizeof...(T) - N>(formatters_) | |
666 | .format(std::get<sizeof...(T) - N>(value.tuple), ctx); | |
667 | if (N > 1) { | |
668 | out = std::copy(value.sep.begin(), value.sep.end(), out); | |
669 | ctx.advance_to(out); | |
670 | return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); | |
671 | } | |
672 | return out; | |
673 | } | |
674 | }; | |
675 | ||
676 | FMT_MODULE_EXPORT_BEGIN | |
677 | ||
678 | /** | |
679 | \rst | |
680 | Returns an object that formats `tuple` with elements separated by `sep`. | |
681 | ||
682 | **Example**:: | |
683 | ||
684 | std::tuple<int, char> t = {1, 'a'}; | |
685 | fmt::print("{}", fmt::join(t, ", ")); | |
686 | // Output: "1, a" | |
687 | \endrst | |
688 | */ | |
689 | template <typename... T> | |
690 | FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) | |
691 | -> tuple_join_view<char, T...> { | |
692 | return {tuple, sep}; | |
693 | } | |
694 | ||
695 | template <typename... T> | |
696 | FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, | |
697 | basic_string_view<wchar_t> sep) | |
698 | -> tuple_join_view<wchar_t, T...> { | |
699 | return {tuple, sep}; | |
700 | } | |
701 | ||
702 | /** | |
703 | \rst | |
704 | Returns an object that formats `initializer_list` with elements separated by | |
705 | `sep`. | |
706 | ||
707 | **Example**:: | |
708 | ||
709 | fmt::print("{}", fmt::join({1, 2, 3}, ", ")); | |
710 | // Output: "1, 2, 3" | |
711 | \endrst | |
712 | */ | |
713 | template <typename T> | |
714 | auto join(std::initializer_list<T> list, string_view sep) | |
715 | -> join_view<const T*, const T*> { | |
716 | return join(std::begin(list), std::end(list), sep); | |
717 | } | |
718 | ||
719 | FMT_MODULE_EXPORT_END | |
720 | FMT_END_NAMESPACE | |
721 | ||
722 | #endif // FMT_RANGES_H_ |