RavEngine
Loading...
Searching...
No Matches
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_IMPORT_STD
12# include <algorithm> // std::max
13# include <limits> // std::numeric_limits
14#endif
15
16#include "format.h"
17
18FMT_BEGIN_NAMESPACE
19FMT_BEGIN_EXPORT
20
21template <typename T> struct printf_formatter {
22 printf_formatter() = delete;
23};
24
25template <typename Char> class basic_printf_context {
26 private:
29
30 static_assert(std::is_same<Char, char>::value ||
31 std::is_same<Char, wchar_t>::value,
32 "Unsupported code unit type.");
33
34 public:
35 using char_type = Char;
37 template <typename T> using formatter_type = printf_formatter<T>;
38
48
49 auto out() -> basic_appender<Char> { return out_; }
50 void advance_to(basic_appender<Char>) {}
51
52 auto locale() -> detail::locale_ref { return {}; }
53
54 auto arg(int id) const -> basic_format_arg<basic_printf_context> {
55 return args_.get(id);
56 }
57};
58
59namespace detail {
60
61// Checks if a value fits in int - used to avoid warnings about comparing
62// signed and unsigned integers.
63template <bool IsSigned> struct int_checker {
64 template <typename T> static auto fits_in_int(T value) -> bool {
65 unsigned max = to_unsigned(max_value<int>());
66 return value <= max;
67 }
68 static auto fits_in_int(bool) -> bool { return true; }
69};
70
71template <> struct int_checker<true> {
72 template <typename T> static auto fits_in_int(T value) -> bool {
73 return value >= (std::numeric_limits<int>::min)() &&
74 value <= max_value<int>();
75 }
76 static auto fits_in_int(int) -> bool { return true; }
77};
78
80 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
81 auto operator()(T value) -> int {
82 if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
83 report_error("number is too big");
84 return (std::max)(static_cast<int>(value), 0);
85 }
86
87 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
88 auto operator()(T) -> int {
89 report_error("precision is not integer");
90 return 0;
91 }
92};
93
94// An argument visitor that returns true iff arg is a zero integer.
96 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
97 auto operator()(T value) -> bool {
98 return value == 0;
99 }
100
101 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
102 auto operator()(T) -> bool {
103 return false;
104 }
105};
106
107template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
108
109template <> struct make_unsigned_or_bool<bool> {
110 using type = bool;
111};
112
113template <typename T, typename Context> class arg_converter {
114 private:
115 using char_type = typename Context::char_type;
116
118 char_type type_;
119
120 public:
121 arg_converter(basic_format_arg<Context>& arg, char_type type)
122 : arg_(arg), type_(type) {}
123
124 void operator()(bool value) {
125 if (type_ != 's') operator()<bool>(value);
126 }
127
128 template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
129 void operator()(U value) {
130 bool is_signed = type_ == 'd' || type_ == 'i';
131 using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
132 if (const_check(sizeof(target_type) <= sizeof(int))) {
133 // Extra casts are used to silence warnings.
134 if (is_signed) {
135 auto n = static_cast<int>(static_cast<target_type>(value));
136 arg_ = detail::make_arg<Context>(n);
137 } else {
138 using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
139 auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
140 arg_ = detail::make_arg<Context>(n);
141 }
142 } else {
143 if (is_signed) {
144 // glibc's printf doesn't sign extend arguments of smaller types:
145 // std::printf("%lld", -42); // prints "4294967254"
146 // but we don't have to do the same because it's a UB.
147 auto n = static_cast<long long>(value);
148 arg_ = detail::make_arg<Context>(n);
149 } else {
150 auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
151 arg_ = detail::make_arg<Context>(n);
152 }
153 }
154 }
155
156 template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
157 void operator()(U) {} // No conversion needed for non-integral types.
158};
159
160// Converts an integer argument to T for printf, if T is an integral type.
161// If T is void, the argument is converted to corresponding signed or unsigned
162// type depending on the type specifier: 'd' and 'i' - signed, other -
163// unsigned).
164template <typename T, typename Context, typename Char>
165void convert_arg(basic_format_arg<Context>& arg, Char type) {
166 arg.visit(arg_converter<T, Context>(arg, type));
167}
168
169// Converts an integer argument to char for printf.
170template <typename Context> class char_converter {
171 private:
173
174 public:
175 explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
176
177 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
178 void operator()(T value) {
179 auto c = static_cast<typename Context::char_type>(value);
180 arg_ = detail::make_arg<Context>(c);
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.
189template <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.
197 private:
198 format_specs& specs_;
199
200 public:
201 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_.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.
224template <typename Char>
225auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
227 return {iter, s, locale_ref()};
228}
229
230// The ``printf`` argument formatter.
231template <typename Char>
233 private:
236
237 context_type& context_;
238
239 void write_null_pointer(bool is_string = false) {
240 auto s = this->specs;
241 s.type = presentation_type::none;
242 write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
243 }
244
245 public:
247 context_type& ctx)
248 : base(make_arg_formatter(iter, s)), context_(ctx) {}
249
250 void operator()(monostate value) { base::operator()(value); }
251
252 template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
253 void operator()(T value) {
254 // MSVC2013 fails to compile separate overloads for bool and Char so use
255 // std::is_same instead.
256 if (!std::is_same<T, Char>::value) {
257 base::operator()(value);
258 return;
259 }
260 format_specs s = this->specs;
261 if (s.type != presentation_type::none && s.type != presentation_type::chr) {
262 return (*this)(static_cast<int>(value));
263 }
264 s.sign = sign::none;
265 s.alt = false;
266 s.fill = ' '; // Ignore '0' flag for char types.
267 // align::numeric needs to be overwritten here since the '0' flag is
268 // ignored for non-numeric types
269 if (s.align == align::none || s.align == align::numeric)
270 s.align = align::right;
271 write<Char>(this->out, static_cast<Char>(value), s);
272 }
273
274 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
275 void operator()(T value) {
276 base::operator()(value);
277 }
278
280 void operator()(const char* value) {
281 if (value)
282 base::operator()(value);
283 else
284 write_null_pointer(this->specs.type != presentation_type::pointer);
285 }
286
288 void operator()(const wchar_t* value) {
289 if (value)
290 base::operator()(value);
291 else
292 write_null_pointer(this->specs.type != presentation_type::pointer);
293 }
294
295 void operator()(basic_string_view<Char> value) { base::operator()(value); }
296
298 void operator()(const void* value) {
299 if (value)
300 base::operator()(value);
301 else
302 write_null_pointer();
303 }
304
307 auto parse_ctx = basic_format_parse_context<Char>({});
308 handle.format(parse_ctx, context_);
309 }
310};
311
312template <typename Char>
313void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
314 for (; it != end; ++it) {
315 switch (*it) {
316 case '-':
317 specs.align = align::left;
318 break;
319 case '+':
320 specs.sign = sign::plus;
321 break;
322 case '0':
323 specs.fill = '0';
324 break;
325 case ' ':
326 if (specs.sign != sign::plus) specs.sign = sign::space;
327 break;
328 case '#':
329 specs.alt = true;
330 break;
331 default:
332 return;
333 }
334 }
335}
336
337template <typename Char, typename GetArg>
338auto parse_header(const Char*& it, const Char* end, format_specs& specs,
339 GetArg get_arg) -> int {
340 int arg_index = -1;
341 Char c = *it;
342 if (c >= '0' && c <= '9') {
343 // Parse an argument index (if followed by '$') or a width possibly
344 // preceded with '0' flag(s).
345 int value = parse_nonnegative_int(it, end, -1);
346 if (it != end && *it == '$') { // value is an argument index
347 ++it;
348 arg_index = value != -1 ? value : max_value<int>();
349 } else {
350 if (c == '0') specs.fill = '0';
351 if (value != 0) {
352 // Nonzero value means that we parsed width and don't need to
353 // parse it or flags again, so return now.
354 if (value == -1) report_error("number is too big");
355 specs.width = value;
356 return arg_index;
357 }
358 }
359 }
360 parse_flags(specs, it, end);
361 // Parse width.
362 if (it != end) {
363 if (*it >= '0' && *it <= '9') {
364 specs.width = parse_nonnegative_int(it, end, -1);
365 if (specs.width == -1) report_error("number is too big");
366 } else if (*it == '*') {
367 ++it;
368 specs.width = static_cast<int>(
369 get_arg(-1).visit(detail::printf_width_handler(specs)));
370 }
371 }
372 return arg_index;
373}
374
375inline auto parse_printf_presentation_type(char c, type t, bool& upper)
376 -> presentation_type {
377 using pt = presentation_type;
378 constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
379 switch (c) {
380 case 'd':
381 return in(t, integral_set) ? pt::dec : pt::none;
382 case 'o':
383 return in(t, integral_set) ? pt::oct : pt::none;
384 case 'X':
385 upper = true;
386 FMT_FALLTHROUGH;
387 case 'x':
388 return in(t, integral_set) ? pt::hex : pt::none;
389 case 'E':
390 upper = true;
391 FMT_FALLTHROUGH;
392 case 'e':
393 return in(t, float_set) ? pt::exp : pt::none;
394 case 'F':
395 upper = true;
396 FMT_FALLTHROUGH;
397 case 'f':
398 return in(t, float_set) ? pt::fixed : pt::none;
399 case 'G':
400 upper = true;
401 FMT_FALLTHROUGH;
402 case 'g':
403 return in(t, float_set) ? pt::general : pt::none;
404 case 'A':
405 upper = true;
406 FMT_FALLTHROUGH;
407 case 'a':
408 return in(t, float_set) ? pt::hexfloat : pt::none;
409 case 'c':
410 return in(t, integral_set) ? pt::chr : pt::none;
411 case 's':
412 return in(t, string_set | cstring_set) ? pt::string : pt::none;
413 case 'p':
414 return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
415 default:
416 return pt::none;
417 }
418}
419
420template <typename Char, typename Context>
421void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
423 using iterator = basic_appender<Char>;
424 auto out = iterator(buf);
425 auto context = basic_printf_context<Char>(out, args);
426 auto parse_ctx = basic_format_parse_context<Char>(format);
427
428 // Returns the argument with specified index or, if arg_index is -1, the next
429 // argument.
430 auto get_arg = [&](int arg_index) {
431 if (arg_index < 0)
432 arg_index = parse_ctx.next_arg_id();
433 else
434 parse_ctx.check_arg_id(--arg_index);
435 return detail::get_arg(context, arg_index);
436 };
437
438 const Char* start = parse_ctx.begin();
439 const Char* end = parse_ctx.end();
440 auto it = start;
441 while (it != end) {
442 if (!find<false, Char>(it, end, '%', it)) {
443 it = end; // find leaves it == nullptr if it doesn't find '%'.
444 break;
445 }
446 Char c = *it++;
447 if (it != end && *it == c) {
448 write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
449 start = ++it;
450 continue;
451 }
452 write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
453
454 auto specs = format_specs();
455 specs.align = align::right;
456
457 // Parse argument index, flags and width.
458 int arg_index = parse_header(it, end, specs, get_arg);
459 if (arg_index == 0) report_error("argument not found");
460
461 // Parse precision.
462 if (it != end && *it == '.') {
463 ++it;
464 c = it != end ? *it : 0;
465 if ('0' <= c && c <= '9') {
466 specs.precision = parse_nonnegative_int(it, end, 0);
467 } else if (c == '*') {
468 ++it;
469 specs.precision =
470 static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
471 } else {
472 specs.precision = 0;
473 }
474 }
475
476 auto arg = get_arg(arg_index);
477 // For d, i, o, u, x, and X conversion specifiers, if a precision is
478 // specified, the '0' flag is ignored
479 if (specs.precision >= 0 && arg.is_integral()) {
480 // Ignore '0' for non-numeric types or if '-' present.
481 specs.fill = ' ';
482 }
483 if (specs.precision >= 0 && arg.type() == type::cstring_type) {
484 auto str = arg.visit(get_cstring<Char>());
485 auto str_end = str + specs.precision;
486 auto nul = std::find(str, str_end, Char());
487 auto sv = basic_string_view<Char>(
488 str, to_unsigned(nul != str_end ? nul - str : specs.precision));
489 arg = make_arg<basic_printf_context<Char>>(sv);
490 }
491 if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
492 if (specs.fill.template get<Char>() == '0') {
493 if (arg.is_arithmetic() && specs.align != align::left)
494 specs.align = align::numeric;
495 else
496 specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
497 // flag is also present.
498 }
499
500 // Parse length and convert the argument to the required type.
501 c = it != end ? *it++ : 0;
502 Char t = it != end ? *it : 0;
503 switch (c) {
504 case 'h':
505 if (t == 'h') {
506 ++it;
507 t = it != end ? *it : 0;
508 convert_arg<signed char>(arg, t);
509 } else {
510 convert_arg<short>(arg, t);
511 }
512 break;
513 case 'l':
514 if (t == 'l') {
515 ++it;
516 t = it != end ? *it : 0;
517 convert_arg<long long>(arg, t);
518 } else {
519 convert_arg<long>(arg, t);
520 }
521 break;
522 case 'j':
523 convert_arg<intmax_t>(arg, t);
524 break;
525 case 'z':
526 convert_arg<size_t>(arg, t);
527 break;
528 case 't':
529 convert_arg<std::ptrdiff_t>(arg, t);
530 break;
531 case 'L':
532 // printf produces garbage when 'L' is omitted for long double, no
533 // need to do the same.
534 break;
535 default:
536 --it;
537 convert_arg<void>(arg, c);
538 }
539
540 // Parse type.
541 if (it == end) report_error("invalid format string");
542 char type = static_cast<char>(*it++);
543 if (arg.is_integral()) {
544 // Normalize type.
545 switch (type) {
546 case 'i':
547 case 'u':
548 type = 'd';
549 break;
550 case 'c':
551 arg.visit(char_converter<basic_printf_context<Char>>(arg));
552 break;
553 }
554 }
555 bool upper = false;
556 specs.type = parse_printf_presentation_type(type, arg.type(), upper);
557 if (specs.type == presentation_type::none)
558 report_error("invalid format specifier");
559 specs.upper = upper;
560
561 start = it;
562
563 // Format argument.
564 arg.visit(printf_arg_formatter<Char>(out, specs, context));
565 }
566 write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
567}
568} // namespace detail
569
572
575
582template <typename Char = char, typename... T>
583inline auto make_printf_args(T&... args)
584 -> decltype(make_format_args<basic_printf_context<Char>>(args...)) {
585 return make_format_args<basic_printf_context<Char>>(args...);
586}
587
588template <typename Char> struct vprintf_args {
590};
591
592template <typename Char>
593inline auto vsprintf(basic_string_view<Char> fmt,
594 typename vprintf_args<Char>::type args)
595 -> std::basic_string<Char> {
596 auto buf = basic_memory_buffer<Char>();
597 detail::vprintf(buf, fmt, args);
598 return to_string(buf);
599}
600
610template <typename S, typename... T, typename Char = char_t<S>>
611inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
612 return vsprintf(detail::to_string_view(fmt),
613 fmt::make_format_args<basic_printf_context<Char>>(args...));
614}
615
616template <typename Char>
617inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
618 typename vprintf_args<Char>::type args) -> int {
619 auto buf = basic_memory_buffer<Char>();
620 detail::vprintf(buf, fmt, args);
621 size_t size = buf.size();
622 return std::fwrite(buf.data(), sizeof(Char), size, f) < size
623 ? -1
624 : static_cast<int>(size);
625}
626
636template <typename S, typename... T, typename Char = char_t<S>>
637inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
638 return vfprintf(f, detail::to_string_view(fmt),
639 make_printf_args<Char>(args...));
640}
641
642template <typename Char>
643FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
644 typename vprintf_args<Char>::type args)
645 -> int {
646 return vfprintf(stdout, fmt, args);
647}
648
658template <typename... T>
659inline auto printf(string_view fmt, const T&... args) -> int {
660 return vfprintf(stdout, fmt, make_printf_args(args...));
661}
662template <typename... T>
663FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
664 const T&... args) -> int {
665 return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
666}
667
668FMT_END_EXPORT
669FMT_END_NAMESPACE
670
671#endif // FMT_PRINTF_H_
Definition base.h:1143
Definition base.h:1728
Definition base.h:1707
Definition base.h:1827
FMT_CONSTEXPR auto get(int id) const -> format_arg
Definition base.h:1902
Definition base.h:727
Definition format.h:838
Definition printf.h:25
basic_printf_context(basic_appender< Char > out, basic_format_args< basic_printf_context > args)
Definition printf.h:45
Definition base.h:484
Definition base.h:1940
Definition printf.h:113
Definition printf.h:170
Definition base.h:1561
Definition printf.h:232
void operator()(typename basic_format_arg< context_type >::handle handle)
Definition printf.h:306
void operator()(const void *value)
Definition printf.h:298
void operator()(const char *value)
Definition printf.h:280
void operator()(const wchar_t *value)
Definition printf.h:288
Definition printf.h:196
Definition base.h:1310
Definition format.h:3754
Definition printf.h:189
Definition printf.h:63
Definition printf.h:95
Definition printf.h:107
Definition printf.h:79
Definition base.h:2152
Definition base.h:323
Definition printf.h:21
Definition printf.h:588