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