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