You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

722 lines
23 KiB

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