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.

396 lines
12 KiB

1 year ago
  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 <type_traits>
  15. #include "format.h"
  16. // output only up to N items from the range.
  17. #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
  18. # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
  19. #endif
  20. FMT_BEGIN_NAMESPACE
  21. template <typename Char> struct formatting_base {
  22. template <typename ParseContext>
  23. FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
  24. return ctx.begin();
  25. }
  26. };
  27. template <typename Char, typename Enable = void>
  28. struct formatting_range : formatting_base<Char> {
  29. static FMT_CONSTEXPR_DECL const size_t range_length_limit =
  30. FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
  31. // range.
  32. Char prefix;
  33. Char delimiter;
  34. Char postfix;
  35. formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
  36. static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
  37. static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
  38. };
  39. template <typename Char, typename Enable = void>
  40. struct formatting_tuple : formatting_base<Char> {
  41. Char prefix;
  42. Char delimiter;
  43. Char postfix;
  44. formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
  45. static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
  46. static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
  47. };
  48. namespace detail {
  49. template <typename RangeT, typename OutputIterator>
  50. OutputIterator copy(const RangeT& range, OutputIterator out) {
  51. for (auto it = range.begin(), end = range.end(); it != end; ++it)
  52. *out++ = *it;
  53. return out;
  54. }
  55. template <typename OutputIterator>
  56. OutputIterator copy(const char* str, OutputIterator out) {
  57. while (*str) *out++ = *str++;
  58. return out;
  59. }
  60. template <typename OutputIterator>
  61. OutputIterator copy(char ch, OutputIterator out) {
  62. *out++ = ch;
  63. return out;
  64. }
  65. /// Return true value if T has std::string interface, like std::string_view.
  66. template <typename T> class is_like_std_string {
  67. template <typename U>
  68. static auto check(U* p)
  69. -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
  70. template <typename> static void check(...);
  71. public:
  72. static FMT_CONSTEXPR_DECL const bool value =
  73. is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
  74. };
  75. template <typename Char>
  76. struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
  77. template <typename... Ts> struct conditional_helper {};
  78. template <typename T, typename _ = void> struct is_range_ : std::false_type {};
  79. #if !FMT_MSC_VER || FMT_MSC_VER > 1800
  80. template <typename T>
  81. struct is_range_<
  82. T, conditional_t<false,
  83. conditional_helper<decltype(std::declval<T>().begin()),
  84. decltype(std::declval<T>().end())>,
  85. void>> : std::true_type {};
  86. #endif
  87. /// tuple_size and tuple_element check.
  88. template <typename T> class is_tuple_like_ {
  89. template <typename U>
  90. static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
  91. template <typename> static void check(...);
  92. public:
  93. static FMT_CONSTEXPR_DECL const bool value =
  94. !std::is_void<decltype(check<T>(nullptr))>::value;
  95. };
  96. // Check for integer_sequence
  97. #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
  98. template <typename T, T... N>
  99. using integer_sequence = std::integer_sequence<T, N...>;
  100. template <size_t... N> using index_sequence = std::index_sequence<N...>;
  101. template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
  102. #else
  103. template <typename T, T... N> struct integer_sequence {
  104. using value_type = T;
  105. static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
  106. };
  107. template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
  108. template <typename T, size_t N, T... Ns>
  109. struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
  110. template <typename T, T... Ns>
  111. struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
  112. template <size_t N>
  113. using make_index_sequence = make_integer_sequence<size_t, N>;
  114. #endif
  115. template <class Tuple, class F, size_t... Is>
  116. void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
  117. using std::get;
  118. // using free function get<I>(T) now.
  119. const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
  120. (void)_; // blocks warnings
  121. }
  122. template <class T>
  123. FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
  124. T const&) {
  125. return {};
  126. }
  127. template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
  128. const auto indexes = get_indexes(tup);
  129. for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
  130. }
  131. template <typename Range>
  132. using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
  133. template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
  134. typename std::decay<Arg>::type>::value)>
  135. FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
  136. return add_space ? " {}" : "{}";
  137. }
  138. template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
  139. typename std::decay<Arg>::type>::value)>
  140. FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
  141. return add_space ? " \"{}\"" : "\"{}\"";
  142. }
  143. FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
  144. return add_space ? " \"{}\"" : "\"{}\"";
  145. }
  146. FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
  147. return add_space ? L" \"{}\"" : L"\"{}\"";
  148. }
  149. FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
  150. return add_space ? " '{}'" : "'{}'";
  151. }
  152. FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
  153. return add_space ? L" '{}'" : L"'{}'";
  154. }
  155. } // namespace detail
  156. template <typename T> struct is_tuple_like {
  157. static FMT_CONSTEXPR_DECL const bool value =
  158. detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
  159. };
  160. template <typename TupleT, typename Char>
  161. struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
  162. private:
  163. // C++11 generic lambda for format()
  164. template <typename FormatContext> struct format_each {
  165. template <typename T> void operator()(const T& v) {
  166. if (i > 0) {
  167. if (formatting.add_prepostfix_space) {
  168. *out++ = ' ';
  169. }
  170. out = detail::copy(formatting.delimiter, out);
  171. }
  172. out = format_to(out,
  173. detail::format_str_quoted(
  174. (formatting.add_delimiter_spaces && i > 0), v),
  175. v);
  176. ++i;
  177. }
  178. formatting_tuple<Char>& formatting;
  179. size_t& i;
  180. typename std::add_lvalue_reference<decltype(
  181. std::declval<FormatContext>().out())>::type out;
  182. };
  183. public:
  184. formatting_tuple<Char> formatting;
  185. template <typename ParseContext>
  186. FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
  187. return formatting.parse(ctx);
  188. }
  189. template <typename FormatContext = format_context>
  190. auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
  191. auto out = ctx.out();
  192. size_t i = 0;
  193. detail::copy(formatting.prefix, out);
  194. detail::for_each(values, format_each<FormatContext>{formatting, i, out});
  195. if (formatting.add_prepostfix_space) {
  196. *out++ = ' ';
  197. }
  198. detail::copy(formatting.postfix, out);
  199. return ctx.out();
  200. }
  201. };
  202. template <typename T, typename Char> struct is_range {
  203. static FMT_CONSTEXPR_DECL const bool value =
  204. detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
  205. !std::is_convertible<T, std::basic_string<Char>>::value &&
  206. !std::is_constructible<detail::std_string_view<Char>, T>::value;
  207. };
  208. template <typename T, typename Char>
  209. struct formatter<
  210. T, Char,
  211. enable_if_t<fmt::is_range<T, Char>::value
  212. // Workaround a bug in MSVC 2017 and earlier.
  213. #if !FMT_MSC_VER || FMT_MSC_VER >= 1927
  214. &&
  215. (has_formatter<detail::value_type<T>, format_context>::value ||
  216. detail::has_fallback_formatter<detail::value_type<T>,
  217. format_context>::value)
  218. #endif
  219. >> {
  220. formatting_range<Char> formatting;
  221. template <typename ParseContext>
  222. FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
  223. return formatting.parse(ctx);
  224. }
  225. template <typename FormatContext>
  226. typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
  227. auto out = detail::copy(formatting.prefix, ctx.out());
  228. size_t i = 0;
  229. auto it = values.begin();
  230. auto end = values.end();
  231. for (; it != end; ++it) {
  232. if (i > 0) {
  233. if (formatting.add_prepostfix_space) *out++ = ' ';
  234. out = detail::copy(formatting.delimiter, out);
  235. }
  236. out = format_to(out,
  237. detail::format_str_quoted(
  238. (formatting.add_delimiter_spaces && i > 0), *it),
  239. *it);
  240. if (++i > formatting.range_length_limit) {
  241. out = format_to(out, " ... <other elements>");
  242. break;
  243. }
  244. }
  245. if (formatting.add_prepostfix_space) *out++ = ' ';
  246. return detail::copy(formatting.postfix, out);
  247. }
  248. };
  249. template <typename Char, typename... T> struct tuple_arg_join : detail::view {
  250. const std::tuple<T...>& tuple;
  251. basic_string_view<Char> sep;
  252. tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
  253. : tuple{t}, sep{s} {}
  254. };
  255. template <typename Char, typename... T>
  256. struct formatter<tuple_arg_join<Char, T...>, Char> {
  257. template <typename ParseContext>
  258. FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
  259. return ctx.begin();
  260. }
  261. template <typename FormatContext>
  262. typename FormatContext::iterator format(
  263. const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
  264. return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
  265. }
  266. private:
  267. template <typename FormatContext, size_t... N>
  268. typename FormatContext::iterator format(
  269. const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
  270. detail::index_sequence<N...>) {
  271. return format_args(value, ctx, std::get<N>(value.tuple)...);
  272. }
  273. template <typename FormatContext>
  274. typename FormatContext::iterator format_args(
  275. const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
  276. // NOTE: for compilers that support C++17, this empty function instantiation
  277. // can be replaced with a constexpr branch in the variadic overload.
  278. return ctx.out();
  279. }
  280. template <typename FormatContext, typename Arg, typename... Args>
  281. typename FormatContext::iterator format_args(
  282. const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
  283. const Arg& arg, const Args&... args) {
  284. using base = formatter<typename std::decay<Arg>::type, Char>;
  285. auto out = ctx.out();
  286. out = base{}.format(arg, ctx);
  287. if (sizeof...(Args) > 0) {
  288. out = std::copy(value.sep.begin(), value.sep.end(), out);
  289. ctx.advance_to(out);
  290. return format_args(value, ctx, args...);
  291. }
  292. return out;
  293. }
  294. };
  295. /**
  296. \rst
  297. Returns an object that formats `tuple` with elements separated by `sep`.
  298. **Example**::
  299. std::tuple<int, char> t = {1, 'a'};
  300. fmt::print("{}", fmt::join(t, ", "));
  301. // Output: "1, a"
  302. \endrst
  303. */
  304. template <typename... T>
  305. FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
  306. string_view sep) {
  307. return {tuple, sep};
  308. }
  309. template <typename... T>
  310. FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
  311. wstring_view sep) {
  312. return {tuple, sep};
  313. }
  314. /**
  315. \rst
  316. Returns an object that formats `initializer_list` with elements separated by
  317. `sep`.
  318. **Example**::
  319. fmt::print("{}", fmt::join({1, 2, 3}, ", "));
  320. // Output: "1, 2, 3"
  321. \endrst
  322. */
  323. template <typename T>
  324. arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
  325. string_view sep) {
  326. return join(std::begin(list), std::end(list), sep);
  327. }
  328. template <typename T>
  329. arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
  330. wstring_view sep) {
  331. return join(std::begin(list), std::end(list), sep);
  332. }
  333. FMT_END_NAMESPACE
  334. #endif // FMT_RANGES_H_