compile.h
Go to the documentation of this file.
1 // Formatting library for C++ - experimental format string compilation
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_COMPILE_H_
9 #define FMT_COMPILE_H_
10 
11 #include "format.h"
12 
14 namespace detail {
15 
16 template <typename Char, typename InputIt>
17 FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end,
19  return it + (end - begin);
20 }
21 
22 // A compile-time string which is compiled into fast formatting code.
23 class compiled_string {};
24 
25 template <typename S>
26 struct is_compiled_string : std::is_base_of<compiled_string, S> {};
27 
41 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
42 # define FMT_COMPILE(s) \
43  FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
44 #else
45 # define FMT_COMPILE(s) FMT_STRING(s)
46 #endif
47 
48 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
49 template <typename Char, size_t N,
50  fmt::detail_exported::fixed_string<Char, N> Str>
51 struct udl_compiled_string : compiled_string {
52  using char_type = Char;
53  explicit constexpr operator basic_string_view<char_type>() const {
54  return {Str.data, N - 1};
55  }
56 };
57 #endif
58 
59 template <typename T, typename... Tail>
60 auto first(const T& value, const Tail&...) -> const T& {
61  return value;
62 }
63 
64 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
65 template <typename... Args> struct type_list {};
66 
67 // Returns a reference to the argument at index N from [first, rest...].
68 template <int N, typename T, typename... Args>
69 constexpr const auto& get([[maybe_unused]] const T& first,
70  [[maybe_unused]] const Args&... rest) {
71  static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
72  if constexpr (N == 0)
73  return first;
74  else
75  return detail::get<N - 1>(rest...);
76 }
77 
78 template <typename Char, typename... Args>
79 constexpr int get_arg_index_by_name(basic_string_view<Char> name,
80  type_list<Args...>) {
81  return get_arg_index_by_name<Args...>(name);
82 }
83 
84 template <int N, typename> struct get_type_impl;
85 
86 template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
87  using type =
88  remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
89 };
90 
91 template <int N, typename T>
92 using get_type = typename get_type_impl<N, T>::type;
93 
94 template <typename T> struct is_compiled_format : std::false_type {};
95 
96 template <typename Char> struct text {
98  using char_type = Char;
99 
100  template <typename OutputIt, typename... Args>
101  constexpr OutputIt format(OutputIt out, const Args&...) const {
102  return write<Char>(out, data);
103  }
104 };
105 
106 template <typename Char>
107 struct is_compiled_format<text<Char>> : std::true_type {};
108 
109 template <typename Char>
110 constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
111  size_t size) {
112  return {{&s[pos], size}};
113 }
114 
115 template <typename Char> struct code_unit {
116  Char value;
117  using char_type = Char;
118 
119  template <typename OutputIt, typename... Args>
120  constexpr OutputIt format(OutputIt out, const Args&...) const {
121  *out++ = value;
122  return out;
123  }
124 };
125 
126 // This ensures that the argument type is convertible to `const T&`.
127 template <typename T, int N, typename... Args>
128 constexpr const T& get_arg_checked(const Args&... args) {
129  const auto& arg = detail::get<N>(args...);
130  if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
131  return arg.value;
132  } else {
133  return arg;
134  }
135 }
136 
137 template <typename Char>
138 struct is_compiled_format<code_unit<Char>> : std::true_type {};
139 
140 // A replacement field that refers to argument N.
141 template <typename Char, typename T, int N> struct field {
142  using char_type = Char;
143 
144  template <typename OutputIt, typename... Args>
145  constexpr OutputIt format(OutputIt out, const Args&... args) const {
146  const T& arg = get_arg_checked<T, N>(args...);
147  if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) {
148  auto s = basic_string_view<Char>(arg);
149  return copy_str<Char>(s.begin(), s.end(), out);
150  }
151  return write<Char>(out, arg);
152  }
153 };
154 
155 template <typename Char, typename T, int N>
156 struct is_compiled_format<field<Char, T, N>> : std::true_type {};
157 
158 // A replacement field that refers to argument with name.
159 template <typename Char> struct runtime_named_field {
160  using char_type = Char;
162 
163  template <typename OutputIt, typename T>
164  constexpr static bool try_format_argument(
165  OutputIt& out,
166  // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
167  [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
168  if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
169  if (arg_name == arg.name) {
170  out = write<Char>(out, arg.value);
171  return true;
172  }
173  }
174  return false;
175  }
176 
177  template <typename OutputIt, typename... Args>
178  constexpr OutputIt format(OutputIt out, const Args&... args) const {
179  bool found = (try_format_argument(out, name, args) || ...);
180  if (!found) {
181  FMT_THROW(format_error("argument with specified name is not found"));
182  }
183  return out;
184  }
185 };
186 
187 template <typename Char>
188 struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
189 
190 // A replacement field that refers to argument N and has format specifiers.
191 template <typename Char, typename T, int N> struct spec_field {
192  using char_type = Char;
193  formatter<T, Char> fmt;
194 
195  template <typename OutputIt, typename... Args>
196  constexpr FMT_INLINE OutputIt format(OutputIt out,
197  const Args&... args) const {
198  const auto& vargs =
199  fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
200  basic_format_context<OutputIt, Char> ctx(out, vargs);
201  return fmt.format(get_arg_checked<T, N>(args...), ctx);
202  }
203 };
204 
205 template <typename Char, typename T, int N>
206 struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
207 
208 template <typename L, typename R> struct concat {
209  L lhs;
210  R rhs;
211  using char_type = typename L::char_type;
212 
213  template <typename OutputIt, typename... Args>
214  constexpr OutputIt format(OutputIt out, const Args&... args) const {
215  out = lhs.format(out, args...);
216  return rhs.format(out, args...);
217  }
218 };
219 
220 template <typename L, typename R>
221 struct is_compiled_format<concat<L, R>> : std::true_type {};
222 
223 template <typename L, typename R>
224 constexpr concat<L, R> make_concat(L lhs, R rhs) {
225  return {lhs, rhs};
226 }
227 
228 struct unknown_format {};
229 
230 template <typename Char>
231 constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
232  for (size_t size = str.size(); pos != size; ++pos) {
233  if (str[pos] == '{' || str[pos] == '}') break;
234  }
235  return pos;
236 }
237 
238 template <typename Args, size_t POS, int ID, typename S>
239 constexpr auto compile_format_string(S format_str);
240 
241 template <typename Args, size_t POS, int ID, typename T, typename S>
242 constexpr auto parse_tail(T head, S format_str) {
243  if constexpr (POS !=
245  constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
246  if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
247  unknown_format>())
248  return tail;
249  else
250  return make_concat(head, tail);
251  } else {
252  return head;
253  }
254 }
255 
256 template <typename T, typename Char> struct parse_specs_result {
257  formatter<T, Char> fmt;
258  size_t end;
259  int next_arg_id;
260 };
261 
262 enum { manual_indexing_id = -1 };
263 
264 template <typename T, typename Char>
265 constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
266  size_t pos, int next_arg_id) {
267  str.remove_prefix(pos);
268  auto ctx =
269  compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
270  auto f = formatter<T, Char>();
271  auto end = f.parse(ctx);
272  return {f, pos + fmt::detail::to_unsigned(end - str.data()),
273  next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
274 }
275 
276 template <typename Char> struct arg_id_handler {
277  arg_ref<Char> arg_id;
278 
279  constexpr int on_auto() {
280  FMT_ASSERT(false, "handler cannot be used with automatic indexing");
281  return 0;
282  }
283  constexpr int on_index(int id) {
284  arg_id = arg_ref<Char>(id);
285  return 0;
286  }
287  constexpr int on_name(basic_string_view<Char> id) {
288  arg_id = arg_ref<Char>(id);
289  return 0;
290  }
291 };
292 
293 template <typename Char> struct parse_arg_id_result {
294  arg_ref<Char> arg_id;
295  const Char* arg_id_end;
296 };
297 
298 template <int ID, typename Char>
299 constexpr auto parse_arg_id(const Char* begin, const Char* end) {
300  auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
301  auto arg_id_end = parse_arg_id(begin, end, handler);
302  return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
303 }
304 
305 template <typename T, typename Enable = void> struct field_type {
306  using type = remove_cvref_t<T>;
307 };
308 
309 template <typename T>
310 struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
311  using type = remove_cvref_t<decltype(T::value)>;
312 };
313 
314 template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
315  typename S>
316 constexpr auto parse_replacement_field_then_tail(S format_str) {
317  using char_type = typename S::char_type;
318  constexpr auto str = basic_string_view<char_type>(format_str);
319  constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
320  if constexpr (c == '}') {
321  return parse_tail<Args, END_POS + 1, NEXT_ID>(
322  field<char_type, typename field_type<T>::type, ARG_INDEX>(),
323  format_str);
324  } else if constexpr (c != ':') {
325  FMT_THROW(format_error("expected ':'"));
326  } else {
327  constexpr auto result = parse_specs<typename field_type<T>::type>(
328  str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
329  if constexpr (result.end >= str.size() || str[result.end] != '}') {
330  FMT_THROW(format_error("expected '}'"));
331  return 0;
332  } else {
333  return parse_tail<Args, result.end + 1, result.next_arg_id>(
335  result.fmt},
336  format_str);
337  }
338  }
339 }
340 
341 // Compiles a non-empty format string and returns the compiled representation
342 // or unknown_format() on unrecognized input.
343 template <typename Args, size_t POS, int ID, typename S>
344 constexpr auto compile_format_string(S format_str) {
345  using char_type = typename S::char_type;
346  constexpr auto str = basic_string_view<char_type>(format_str);
347  if constexpr (str[POS] == '{') {
348  if constexpr (POS + 1 == str.size())
349  FMT_THROW(format_error("unmatched '{' in format string"));
350  if constexpr (str[POS + 1] == '{') {
351  return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
352  } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
353  static_assert(ID != manual_indexing_id,
354  "cannot switch from manual to automatic argument indexing");
355  constexpr auto next_id =
356  ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
357  return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
358  POS + 1, ID, next_id>(
359  format_str);
360  } else {
361  constexpr auto arg_id_result =
362  parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
363  constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
364  constexpr char_type c =
365  arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
366  static_assert(c == '}' || c == ':', "missing '}' in format string");
367  if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
368  static_assert(
369  ID == manual_indexing_id || ID == 0,
370  "cannot switch from automatic to manual argument indexing");
371  constexpr auto arg_index = arg_id_result.arg_id.val.index;
372  return parse_replacement_field_then_tail<get_type<arg_index, Args>,
373  Args, arg_id_end_pos,
374  arg_index, manual_indexing_id>(
375  format_str);
376  } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
377  constexpr auto arg_index =
378  get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
379  if constexpr (arg_index >= 0) {
380  constexpr auto next_id =
381  ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
382  return parse_replacement_field_then_tail<
383  decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
384  arg_index, next_id>(format_str);
385  } else if constexpr (c == '}') {
386  return parse_tail<Args, arg_id_end_pos + 1, ID>(
387  runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
388  format_str);
389  } else if constexpr (c == ':') {
390  return unknown_format(); // no type info for specs parsing
391  }
392  }
393  }
394  } else if constexpr (str[POS] == '}') {
395  if constexpr (POS + 1 == str.size())
396  FMT_THROW(format_error("unmatched '}' in format string"));
397  return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
398  } else {
399  constexpr auto end = parse_text(str, POS + 1);
400  if constexpr (end - POS > 1) {
401  return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
402  format_str);
403  } else {
404  return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
405  format_str);
406  }
407  }
408 }
409 
410 template <typename... Args, typename S,
411  FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
412 constexpr auto compile(S format_str) {
413  constexpr auto str = basic_string_view<typename S::char_type>(format_str);
414  if constexpr (str.size() == 0) {
415  return detail::make_text(str, 0, 0);
416  } else {
417  constexpr auto result =
418  detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
419  format_str);
420  return result;
421  }
422 }
423 #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
424 } // namespace detail
425 
426 FMT_BEGIN_EXPORT
427 
428 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
429 
430 template <typename CompiledFormat, typename... Args,
431  typename Char = typename CompiledFormat::char_type,
432  FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
433 FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
434  const Args&... args) {
435  auto s = std::basic_string<Char>();
436  cf.format(std::back_inserter(s), args...);
437  return s;
438 }
439 
440 template <typename OutputIt, typename CompiledFormat, typename... Args,
441  FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
442 constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
443  const Args&... args) {
444  return cf.format(out, args...);
445 }
446 
447 template <typename S, typename... Args,
448  FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
449 FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
450  Args&&... args) {
451  if constexpr (std::is_same<typename S::char_type, char>::value) {
452  constexpr auto str = basic_string_view<typename S::char_type>(S());
453  if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
454  const auto& first = detail::first(args...);
455  if constexpr (detail::is_named_arg<
456  remove_cvref_t<decltype(first)>>::value) {
457  return fmt::to_string(first.value);
458  } else {
459  return fmt::to_string(first);
460  }
461  }
462  }
463  constexpr auto compiled = detail::compile<Args...>(S());
464  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
465  detail::unknown_format>()) {
466  return fmt::format(
467  static_cast<basic_string_view<typename S::char_type>>(S()),
468  std::forward<Args>(args)...);
469  } else {
470  return fmt::format(compiled, std::forward<Args>(args)...);
471  }
472 }
473 
474 template <typename OutputIt, typename S, typename... Args,
475  FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
476 FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
477  constexpr auto compiled = detail::compile<Args...>(S());
478  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
479  detail::unknown_format>()) {
480  return fmt::format_to(
481  out, static_cast<basic_string_view<typename S::char_type>>(S()),
482  std::forward<Args>(args)...);
483  } else {
484  return fmt::format_to(out, compiled, std::forward<Args>(args)...);
485  }
486 }
487 #endif
488 
489 template <typename OutputIt, typename S, typename... Args,
490  FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
491 auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args)
492  -> format_to_n_result<OutputIt> {
493  using traits = detail::fixed_buffer_traits;
494  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
495  fmt::format_to(std::back_inserter(buf), format_str,
496  std::forward<Args>(args)...);
497  return {buf.out(), buf.count()};
498 }
499 
500 template <typename S, typename... Args,
501  FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
502 FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args)
503  -> size_t {
504  return fmt::format_to(detail::counting_iterator(), format_str, args...)
505  .count();
506 }
507 
508 template <typename S, typename... Args,
509  FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
510 void print(std::FILE* f, const S& format_str, const Args&... args) {
511  memory_buffer buffer;
512  fmt::format_to(std::back_inserter(buffer), format_str, args...);
513  detail::print(f, {buffer.data(), buffer.size()});
514 }
515 
516 template <typename S, typename... Args,
517  FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
518 void print(const S& format_str, const Args&... args) {
519  print(stdout, format_str, args...);
520 }
521 
522 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
523 inline namespace literals {
524 template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
525  using char_t = remove_cvref_t<decltype(Str.data[0])>;
526  return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
527  Str>();
528 }
529 } // namespace literals
530 #endif
531 
532 FMT_END_EXPORT
533 FMT_END_NAMESPACE
534 
535 #endif // FMT_COMPILE_H_
formatter< T, Char >
basic_string_view::data
constexpr auto data() const noexcept -> const Char *
Definition: core.h:457
detail::first
auto first(const T &value, const Tail &...) -> const T &
Definition: compile.h:60
remove_cvref_t
typename std::remove_cv< remove_reference_t< T > >::type remove_cvref_t
Definition: core.h:280
POS
#define POS
Definition: lobject.c:553
basic_string_view::size
constexpr auto size() const noexcept -> size_t
Definition: core.h:460
backward::ColorMode::type
type
Definition: backward.hpp:3600
basic_string_view< char_type >
s
XmlRpcServer s
detail::type::char_type
@ char_type
format.h
FMT_INLINE
#define FMT_INLINE
Definition: core.h:165
arg
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1875
nonstd::span_lite::std11::false_type
integral_constant< bool, false > false_type
Definition: span.hpp:657
detail
Definition: args.h:19
sol::load_mode::text
@ text
nonstd::span_lite::std11::true_type
integral_constant< bool, true > true_type
Definition: span.hpp:656
f
f
detail::get_arg_index_by_name
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view< Char > name) -> int
Definition: core.h:2628
sol::call_status::handler
@ handler
basic_string_view::remove_prefix
FMT_CONSTEXPR void remove_prefix(size_t n) noexcept
Definition: core.h:469
nonstd::span_lite::size
span_constexpr std::size_t size(span< T, Extent > const &spn)
Definition: span.hpp:1554
detail::is_named_arg
Definition: core.h:1197
detail::counting_iterator
Definition: format.h:2275
detail::compiled_string
Definition: compile.h:23
detail::value
Definition: core.h:1257
enable_if_t
typename std::enable_if< B, T >::type enable_if_t
Definition: core.h:271
detail::copy_str
FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, counting_iterator it) -> counting_iterator
Definition: compile.h:17
nlohmann::detail::parse_event_t::value
@ value
the parser finished reading a JSON value
format
auto format(const text_style &ts, const S &format_str, const Args &... args) -> std::basic_string< Char >
Definition: color.h:543
FMT_ASSERT
#define FMT_ASSERT(condition, message)
Definition: core.h:353
field_type
def field_type(f)
field
static void field(LexState *ls, ConsControl *cc)
Definition: lparser.c:891
nlohmann::detail::get
auto get(const nlohmann::detail::iteration_proxy_value< IteratorType > &i) -> decltype(i.key())
Definition: json.hpp:4451
mqtt_test.data
dictionary data
Definition: mqtt_test.py:22
FMT_BEGIN_NAMESPACE
#define FMT_BEGIN_NAMESPACE
Definition: core.h:177
detail::arg_id_kind::name
@ name
basic_format_context
Definition: core.h:1739
detail::to_unsigned
FMT_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned< Int >::type
Definition: core.h:391
detail::is_compiled_string
Definition: compile.h:26
detail::type
type
Definition: core.h:573
S
#define S(x)
Definition: luac.c:667
detail::parse_arg_id
FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char *begin, const Char *end, Handler &&handler) -> const Char *
Definition: core.h:2240
FMT_CONSTEXPR
#define FMT_CONSTEXPR
Definition: core.h:105
FMT_THROW
#define FMT_THROW(x)
Definition: format.h:130
udp_client.args
args
Definition: udp_client.py:12


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Nov 11 2024 03:23:43