STIR  6.3.0
printf.h
1 // Formatting library for C++ - legacy printf implementation
2 //
3 // Copyright (c) 2012 - 2016, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7 
8 #ifndef FMT_PRINTF_H_
9 #define FMT_PRINTF_H_
10 
11 #ifndef FMT_MODULE
12 # include <algorithm> // std::find
13 # include <limits> // std::numeric_limits
14 #endif
15 
16 #include "format.h"
17 
18 FMT_BEGIN_NAMESPACE
19 FMT_BEGIN_EXPORT
20 
21 template <typename Char> class basic_printf_context {
22  private:
23  basic_appender<Char> out_;
25 
26  static_assert(std::is_same<Char, char>::value ||
27  std::is_same<Char, wchar_t>::value,
28  "Unsupported code unit type.");
29 
30  public:
31  using char_type = Char;
32  enum { builtin_types = 1 };
33 
36  basic_printf_context(basic_appender<Char> out,
38  : out_(out), args_(args) {}
39 
40  auto out() -> basic_appender<Char> { return out_; }
41  void advance_to(basic_appender<Char>) {}
42 
43  auto locale() -> locale_ref { return {}; }
44 
45  auto arg(int id) const -> basic_format_arg<basic_printf_context> {
46  return args_.get(id);
47  }
48 };
49 
50 namespace detail {
51 
52 // Return the result via the out param to workaround gcc bug 77539.
53 template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
54 FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
55  for (out = first; out != last; ++out) {
56  if (*out == value) return true;
57  }
58  return false;
59 }
60 
61 template <>
62 inline auto find<false, char>(const char* first, const char* last, char value,
63  const char*& out) -> bool {
64  out =
65  static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
66  return out != nullptr;
67 }
68 
69 // Checks if a value fits in int - used to avoid warnings about comparing
70 // signed and unsigned integers.
71 template <bool IS_SIGNED> struct int_checker {
72  template <typename T> static auto fits_in_int(T value) -> bool {
73  return value <= to_unsigned(max_value<int>());
74  }
75  inline static auto fits_in_int(bool) -> bool { return true; }
76 };
77 
78 template <> struct int_checker<true> {
79  template <typename T> static auto fits_in_int(T value) -> bool {
80  return value >= (std::numeric_limits<int>::min)() &&
81  value <= max_value<int>();
82  }
83  inline static auto fits_in_int(int) -> bool { return true; }
84 };
85 
86 struct printf_precision_handler {
87  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
88  auto operator()(T value) -> int {
89  if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
90  report_error("number is too big");
91  return max_of(static_cast<int>(value), 0);
92  }
93 
94  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
95  auto operator()(T) -> int {
96  report_error("precision is not integer");
97  return 0;
98  }
99 };
100 
101 // An argument visitor that returns true iff arg is a zero integer.
102 struct is_zero_int {
103  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
104  auto operator()(T value) -> bool {
105  return value == 0;
106  }
107 
108  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
109  auto operator()(T) -> bool {
110  return false;
111  }
112 };
113 
114 template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
115 
116 template <> struct make_unsigned_or_bool<bool> {
117  using type = bool;
118 };
119 
120 template <typename T, typename Context> class arg_converter {
121  private:
122  using char_type = typename Context::char_type;
123 
124  basic_format_arg<Context>& arg_;
125  char_type type_;
126 
127  public:
128  arg_converter(basic_format_arg<Context>& arg, char_type type)
129  : arg_(arg), type_(type) {}
130 
131  void operator()(bool value) {
132  if (type_ != 's') operator()<bool>(value);
133  }
134 
135  template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
136  void operator()(U value) {
137  bool is_signed = type_ == 'd' || type_ == 'i';
138  using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
139  if (const_check(sizeof(target_type) <= sizeof(int))) {
140  // Extra casts are used to silence warnings.
141  using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
142  if (is_signed)
143  arg_ = static_cast<int>(static_cast<target_type>(value));
144  else
145  arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
146  } else {
147  // glibc's printf doesn't sign extend arguments of smaller types:
148  // std::printf("%lld", -42); // prints "4294967254"
149  // but we don't have to do the same because it's a UB.
150  if (is_signed)
151  arg_ = static_cast<long long>(value);
152  else
153  arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
154  }
155  }
156 
157  template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
158  void operator()(U) {} // No conversion needed for non-integral types.
159 };
160 
161 // Converts an integer argument to T for printf, if T is an integral type.
162 // If T is void, the argument is converted to corresponding signed or unsigned
163 // type depending on the type specifier: 'd' and 'i' - signed, other -
164 // unsigned).
165 template <typename T, typename Context, typename Char>
166 void convert_arg(basic_format_arg<Context>& arg, Char type) {
167  arg.visit(arg_converter<T, Context>(arg, type));
168 }
169 
170 // Converts an integer argument to char for printf.
171 template <typename Context> class char_converter {
172  private:
173  basic_format_arg<Context>& arg_;
174 
175  public:
176  explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
177 
178  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
179  void operator()(T value) {
180  arg_ = static_cast<typename Context::char_type>(value);
181  }
182 
183  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
184  void operator()(T) {} // No conversion needed for non-integral types.
185 };
186 
187 // An argument visitor that return a pointer to a C string if argument is a
188 // string or null otherwise.
189 template <typename Char> struct get_cstring {
190  template <typename T> auto operator()(T) -> const Char* { return nullptr; }
191  auto operator()(const Char* s) -> const Char* { return s; }
192 };
193 
194 // Checks if an argument is a valid printf width specifier and sets
195 // left alignment if it is negative.
196 class printf_width_handler {
197  private:
198  format_specs& specs_;
199 
200  public:
201  inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
202 
203  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
204  auto operator()(T value) -> unsigned {
205  auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
206  if (detail::is_negative(value)) {
207  specs_.set_align(align::left);
208  width = 0 - width;
209  }
210  unsigned int_max = to_unsigned(max_value<int>());
211  if (width > int_max) report_error("number is too big");
212  return static_cast<unsigned>(width);
213  }
214 
215  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
216  auto operator()(T) -> unsigned {
217  report_error("width is not integer");
218  return 0;
219  }
220 };
221 
222 // Workaround for a bug with the XL compiler when initializing
223 // printf_arg_formatter's base class.
224 template <typename Char>
225 auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
226  -> arg_formatter<Char> {
227  return {iter, s, locale_ref()};
228 }
229 
230 // The `printf` argument formatter.
231 template <typename Char>
232 class printf_arg_formatter : public arg_formatter<Char> {
233  private:
234  using base = arg_formatter<Char>;
235  using context_type = basic_printf_context<Char>;
236 
237  context_type& context_;
238 
239  void write_null_pointer(bool is_string = false) {
240  auto s = this->specs;
241  s.set_type(presentation_type::none);
242  write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
243  }
244 
245  template <typename T> void write(T value) {
246  detail::write<Char>(this->out, value, this->specs, this->locale);
247  }
248 
249  public:
250  printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
251  context_type& ctx)
252  : base(make_arg_formatter(iter, s)), context_(ctx) {}
253 
254  void operator()(monostate value) { write(value); }
255 
256  template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
257  void operator()(T value) {
258  // MSVC2013 fails to compile separate overloads for bool and Char so use
259  // std::is_same instead.
260  if (!std::is_same<T, Char>::value) {
261  write(value);
262  return;
263  }
264  format_specs s = this->specs;
265  if (s.type() != presentation_type::none &&
266  s.type() != presentation_type::chr) {
267  return (*this)(static_cast<int>(value));
268  }
269  s.set_sign(sign::none);
270  s.clear_alt();
271  s.set_fill(' '); // Ignore '0' flag for char types.
272  // align::numeric needs to be overwritten here since the '0' flag is
273  // ignored for non-numeric types
274  if (s.align() == align::none || s.align() == align::numeric)
275  s.set_align(align::right);
276  detail::write<Char>(this->out, static_cast<Char>(value), s);
277  }
278 
279  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
280  void operator()(T value) {
281  write(value);
282  }
283 
284  void operator()(const char* value) {
285  if (value)
286  write(value);
287  else
288  write_null_pointer(this->specs.type() != presentation_type::pointer);
289  }
290 
291  void operator()(const wchar_t* value) {
292  if (value)
293  write(value);
294  else
295  write_null_pointer(this->specs.type() != presentation_type::pointer);
296  }
297 
298  void operator()(basic_string_view<Char> value) { write(value); }
299 
300  void operator()(const void* value) {
301  if (value)
302  write(value);
303  else
304  write_null_pointer();
305  }
306 
307  void operator()(typename basic_format_arg<context_type>::handle handle) {
308  auto parse_ctx = parse_context<Char>({});
309  handle.format(parse_ctx, context_);
310  }
311 };
312 
313 template <typename Char>
314 void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
315  for (; it != end; ++it) {
316  switch (*it) {
317  case '-': specs.set_align(align::left); break;
318  case '+': specs.set_sign(sign::plus); break;
319  case '0': specs.set_fill('0'); break;
320  case ' ':
321  if (specs.sign() != sign::plus) specs.set_sign(sign::space);
322  break;
323  case '#': specs.set_alt(); break;
324  default: return;
325  }
326  }
327 }
328 
329 template <typename Char, typename GetArg>
330 auto parse_header(const Char*& it, const Char* end, format_specs& specs,
331  GetArg get_arg) -> int {
332  int arg_index = -1;
333  Char c = *it;
334  if (c >= '0' && c <= '9') {
335  // Parse an argument index (if followed by '$') or a width possibly
336  // preceded with '0' flag(s).
337  int value = parse_nonnegative_int(it, end, -1);
338  if (it != end && *it == '$') { // value is an argument index
339  ++it;
340  arg_index = value != -1 ? value : max_value<int>();
341  } else {
342  if (c == '0') specs.set_fill('0');
343  if (value != 0) {
344  // Nonzero value means that we parsed width and don't need to
345  // parse it or flags again, so return now.
346  if (value == -1) report_error("number is too big");
347  specs.width = value;
348  return arg_index;
349  }
350  }
351  }
352  parse_flags(specs, it, end);
353  // Parse width.
354  if (it != end) {
355  if (*it >= '0' && *it <= '9') {
356  specs.width = parse_nonnegative_int(it, end, -1);
357  if (specs.width == -1) report_error("number is too big");
358  } else if (*it == '*') {
359  ++it;
360  specs.width = static_cast<int>(
361  get_arg(-1).visit(detail::printf_width_handler(specs)));
362  }
363  }
364  return arg_index;
365 }
366 
367 inline auto parse_printf_presentation_type(char c, type t, bool& upper)
368  -> presentation_type {
369  using pt = presentation_type;
370  constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
371  switch (c) {
372  case 'd': return in(t, integral_set) ? pt::dec : pt::none;
373  case 'o': return in(t, integral_set) ? pt::oct : pt::none;
374  case 'X': upper = true; FMT_FALLTHROUGH;
375  case 'x': return in(t, integral_set) ? pt::hex : pt::none;
376  case 'E': upper = true; FMT_FALLTHROUGH;
377  case 'e': return in(t, float_set) ? pt::exp : pt::none;
378  case 'F': upper = true; FMT_FALLTHROUGH;
379  case 'f': return in(t, float_set) ? pt::fixed : pt::none;
380  case 'G': upper = true; FMT_FALLTHROUGH;
381  case 'g': return in(t, float_set) ? pt::general : pt::none;
382  case 'A': upper = true; FMT_FALLTHROUGH;
383  case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
384  case 'c': return in(t, integral_set) ? pt::chr : pt::none;
385  case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
386  case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
387  default: return pt::none;
388  }
389 }
390 
391 template <typename Char, typename Context>
392 void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
394  using iterator = basic_appender<Char>;
395  auto out = iterator(buf);
396  auto context = basic_printf_context<Char>(out, args);
397  auto parse_ctx = parse_context<Char>(format);
398 
399  // Returns the argument with specified index or, if arg_index is -1, the next
400  // argument.
401  auto get_arg = [&](int arg_index) {
402  if (arg_index < 0)
403  arg_index = parse_ctx.next_arg_id();
404  else
405  parse_ctx.check_arg_id(--arg_index);
406  auto arg = context.arg(arg_index);
407  if (!arg) report_error("argument not found");
408  return arg;
409  };
410 
411  const Char* start = parse_ctx.begin();
412  const Char* end = parse_ctx.end();
413  auto it = start;
414  while (it != end) {
415  if (!find<false, Char>(it, end, '%', it)) {
416  it = end; // find leaves it == nullptr if it doesn't find '%'.
417  break;
418  }
419  Char c = *it++;
420  if (it != end && *it == c) {
421  write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
422  start = ++it;
423  continue;
424  }
425  write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
426 
427  auto specs = format_specs();
428  specs.set_align(align::right);
429 
430  // Parse argument index, flags and width.
431  int arg_index = parse_header(it, end, specs, get_arg);
432  if (arg_index == 0) report_error("argument not found");
433 
434  // Parse precision.
435  if (it != end && *it == '.') {
436  ++it;
437  c = it != end ? *it : 0;
438  if ('0' <= c && c <= '9') {
439  specs.precision = parse_nonnegative_int(it, end, 0);
440  } else if (c == '*') {
441  ++it;
442  specs.precision =
443  static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
444  } else {
445  specs.precision = 0;
446  }
447  }
448 
449  auto arg = get_arg(arg_index);
450  // For d, i, o, u, x, and X conversion specifiers, if a precision is
451  // specified, the '0' flag is ignored
452  if (specs.precision >= 0 && is_integral_type(arg.type())) {
453  // Ignore '0' for non-numeric types or if '-' present.
454  specs.set_fill(' ');
455  }
456  if (specs.precision >= 0 && arg.type() == type::cstring_type) {
457  auto str = arg.visit(get_cstring<Char>());
458  auto str_end = str + specs.precision;
459  auto nul = std::find(str, str_end, Char());
460  auto sv = basic_string_view<Char>(
461  str, to_unsigned(nul != str_end ? nul - str : specs.precision));
462  arg = sv;
463  }
464  if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
465  if (specs.fill_unit<Char>() == '0') {
466  if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
467  specs.set_align(align::numeric);
468  } else {
469  // Ignore '0' flag for non-numeric types or if '-' flag is also present.
470  specs.set_fill(' ');
471  }
472  }
473 
474  // Parse length and convert the argument to the required type.
475  c = it != end ? *it++ : 0;
476  Char t = it != end ? *it : 0;
477  switch (c) {
478  case 'h':
479  if (t == 'h') {
480  ++it;
481  t = it != end ? *it : 0;
482  convert_arg<signed char>(arg, t);
483  } else {
484  convert_arg<short>(arg, t);
485  }
486  break;
487  case 'l':
488  if (t == 'l') {
489  ++it;
490  t = it != end ? *it : 0;
491  convert_arg<long long>(arg, t);
492  } else {
493  convert_arg<long>(arg, t);
494  }
495  break;
496  case 'j': convert_arg<intmax_t>(arg, t); break;
497  case 'z': convert_arg<size_t>(arg, t); break;
498  case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
499  case 'L':
500  // printf produces garbage when 'L' is omitted for long double, no
501  // need to do the same.
502  break;
503  default: --it; convert_arg<void>(arg, c);
504  }
505 
506  // Parse type.
507  if (it == end) report_error("invalid format string");
508  char type = static_cast<char>(*it++);
509  if (is_integral_type(arg.type())) {
510  // Normalize type.
511  switch (type) {
512  case 'i':
513  case 'u': type = 'd'; break;
514  case 'c':
515  arg.visit(char_converter<basic_printf_context<Char>>(arg));
516  break;
517  }
518  }
519  bool upper = false;
520  specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
521  if (specs.type() == presentation_type::none)
522  report_error("invalid format specifier");
523  if (upper) specs.set_upper();
524 
525  start = it;
526 
527  // Format argument.
528  arg.visit(printf_arg_formatter<Char>(out, specs, context));
529  }
530  write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
531 }
532 } // namespace detail
533 
534 using printf_context = basic_printf_context<char>;
535 using wprintf_context = basic_printf_context<wchar_t>;
536 
539 
542 template <typename Char = char, typename... T>
543 inline auto make_printf_args(T&... args)
544  -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
545  return fmt::make_format_args<basic_printf_context<Char>>(args...);
546 }
547 
548 template <typename Char> struct vprintf_args {
550 };
551 
552 template <typename Char>
553 inline auto vsprintf(basic_string_view<Char> fmt,
554  typename vprintf_args<Char>::type args)
555  -> std::basic_string<Char> {
556  auto buf = basic_memory_buffer<Char>();
557  detail::vprintf(buf, fmt, args);
558  return {buf.data(), buf.size()};
559 }
560 
569 template <typename... T>
570 inline auto sprintf(string_view fmt, const T&... args) -> std::string {
571  return vsprintf(fmt, make_printf_args(args...));
572 }
573 template <typename... T>
574 FMT_DEPRECATED auto sprintf(basic_string_view<wchar_t> fmt, const T&... args)
575  -> std::wstring {
576  return vsprintf(fmt, make_printf_args<wchar_t>(args...));
577 }
578 
579 template <typename Char>
580 auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
581  typename vprintf_args<Char>::type args) -> int {
582  auto buf = basic_memory_buffer<Char>();
583  detail::vprintf(buf, fmt, args);
584  size_t size = buf.size();
585  return std::fwrite(buf.data(), sizeof(Char), size, f) < size
586  ? -1
587  : static_cast<int>(size);
588 }
589 
598 template <typename... T>
599 inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
600  return vfprintf(f, fmt, make_printf_args(args...));
601 }
602 template <typename... T>
603 FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
604  const T&... args) -> int {
605  return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
606 }
607 
616 template <typename... T>
617 inline auto printf(string_view fmt, const T&... args) -> int {
618  return vfprintf(stdout, fmt, make_printf_args(args...));
619 }
620 
621 FMT_END_EXPORT
622 FMT_END_NAMESPACE
623 
624 #endif // FMT_PRINTF_H_
A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:785
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:623
FMT_CONSTEXPR auto get(int id) const -> format_arg
Returns the argument with the specified id.
Definition: base.h:2631
Converts a string literal into a format string that will be parsed at compile time and converted into...
Definition: args.h:20
An implementation of std::basic_string_view for pre-C++17.
Definition: base.h:515