File indexing completed on 2025-07-03 08:36:29
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef FMT_PRINTF_H_
0009 #define FMT_PRINTF_H_
0010
0011 #include <algorithm> // std::max
0012 #include <limits> // std::numeric_limits
0013
0014 #include "format.h"
0015
0016 FMT_BEGIN_NAMESPACE
0017 FMT_BEGIN_EXPORT
0018
0019 template <typename T> struct printf_formatter {
0020 printf_formatter() = delete;
0021 };
0022
0023 template <typename Char> class basic_printf_context {
0024 private:
0025 detail::buffer_appender<Char> out_;
0026 basic_format_args<basic_printf_context> args_;
0027
0028 static_assert(std::is_same<Char, char>::value ||
0029 std::is_same<Char, wchar_t>::value,
0030 "Unsupported code unit type.");
0031
0032 public:
0033 using char_type = Char;
0034 using parse_context_type = basic_format_parse_context<Char>;
0035 template <typename T> using formatter_type = printf_formatter<T>;
0036
0037
0038
0039
0040
0041
0042
0043 basic_printf_context(detail::buffer_appender<Char> out,
0044 basic_format_args<basic_printf_context> args)
0045 : out_(out), args_(args) {}
0046
0047 auto out() -> detail::buffer_appender<Char> { return out_; }
0048 void advance_to(detail::buffer_appender<Char>) {}
0049
0050 auto locale() -> detail::locale_ref { return {}; }
0051
0052 auto arg(int id) const -> basic_format_arg<basic_printf_context> {
0053 return args_.get(id);
0054 }
0055
0056 FMT_CONSTEXPR void on_error(const char* message) {
0057 detail::error_handler().on_error(message);
0058 }
0059 };
0060
0061 namespace detail {
0062
0063
0064
0065 template <bool IsSigned> struct int_checker {
0066 template <typename T> static auto fits_in_int(T value) -> bool {
0067 unsigned max = max_value<int>();
0068 return value <= max;
0069 }
0070 static auto fits_in_int(bool) -> bool { return true; }
0071 };
0072
0073 template <> struct int_checker<true> {
0074 template <typename T> static auto fits_in_int(T value) -> bool {
0075 return value >= (std::numeric_limits<int>::min)() &&
0076 value <= max_value<int>();
0077 }
0078 static auto fits_in_int(int) -> bool { return true; }
0079 };
0080
0081 struct printf_precision_handler {
0082 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0083 auto operator()(T value) -> int {
0084 if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
0085 throw_format_error("number is too big");
0086 return (std::max)(static_cast<int>(value), 0);
0087 }
0088
0089 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0090 auto operator()(T) -> int {
0091 throw_format_error("precision is not integer");
0092 return 0;
0093 }
0094 };
0095
0096
0097 struct is_zero_int {
0098 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0099 auto operator()(T value) -> bool {
0100 return value == 0;
0101 }
0102
0103 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0104 auto operator()(T) -> bool {
0105 return false;
0106 }
0107 };
0108
0109 template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
0110
0111 template <> struct make_unsigned_or_bool<bool> {
0112 using type = bool;
0113 };
0114
0115 template <typename T, typename Context> class arg_converter {
0116 private:
0117 using char_type = typename Context::char_type;
0118
0119 basic_format_arg<Context>& arg_;
0120 char_type type_;
0121
0122 public:
0123 arg_converter(basic_format_arg<Context>& arg, char_type type)
0124 : arg_(arg), type_(type) {}
0125
0126 void operator()(bool value) {
0127 if (type_ != 's') operator()<bool>(value);
0128 }
0129
0130 template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
0131 void operator()(U value) {
0132 bool is_signed = type_ == 'd' || type_ == 'i';
0133 using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
0134 if (const_check(sizeof(target_type) <= sizeof(int))) {
0135
0136 if (is_signed) {
0137 auto n = static_cast<int>(static_cast<target_type>(value));
0138 arg_ = detail::make_arg<Context>(n);
0139 } else {
0140 using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
0141 auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
0142 arg_ = detail::make_arg<Context>(n);
0143 }
0144 } else {
0145 if (is_signed) {
0146
0147
0148
0149 auto n = static_cast<long long>(value);
0150 arg_ = detail::make_arg<Context>(n);
0151 } else {
0152 auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
0153 arg_ = detail::make_arg<Context>(n);
0154 }
0155 }
0156 }
0157
0158 template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
0159 void operator()(U) {}
0160 };
0161
0162
0163
0164
0165
0166 template <typename T, typename Context, typename Char>
0167 void convert_arg(basic_format_arg<Context>& arg, Char type) {
0168 visit_format_arg(arg_converter<T, Context>(arg, type), arg);
0169 }
0170
0171
0172 template <typename Context> class char_converter {
0173 private:
0174 basic_format_arg<Context>& arg_;
0175
0176 public:
0177 explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
0178
0179 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0180 void operator()(T value) {
0181 auto c = static_cast<typename Context::char_type>(value);
0182 arg_ = detail::make_arg<Context>(c);
0183 }
0184
0185 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0186 void operator()(T) {}
0187 };
0188
0189
0190
0191 template <typename Char> struct get_cstring {
0192 template <typename T> auto operator()(T) -> const Char* { return nullptr; }
0193 auto operator()(const Char* s) -> const Char* { return s; }
0194 };
0195
0196
0197
0198 template <typename Char> class printf_width_handler {
0199 private:
0200 format_specs<Char>& specs_;
0201
0202 public:
0203 explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
0204
0205 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0206 auto operator()(T value) -> unsigned {
0207 auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
0208 if (detail::is_negative(value)) {
0209 specs_.align = align::left;
0210 width = 0 - width;
0211 }
0212 unsigned int_max = max_value<int>();
0213 if (width > int_max) throw_format_error("number is too big");
0214 return static_cast<unsigned>(width);
0215 }
0216
0217 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0218 auto operator()(T) -> unsigned {
0219 throw_format_error("width is not integer");
0220 return 0;
0221 }
0222 };
0223
0224
0225
0226 template <typename Char>
0227 auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
0228 -> arg_formatter<Char> {
0229 return {iter, s, locale_ref()};
0230 }
0231
0232
0233 template <typename Char>
0234 class printf_arg_formatter : public arg_formatter<Char> {
0235 private:
0236 using base = arg_formatter<Char>;
0237 using context_type = basic_printf_context<Char>;
0238
0239 context_type& context_;
0240
0241 void write_null_pointer(bool is_string = false) {
0242 auto s = this->specs;
0243 s.type = presentation_type::none;
0244 write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
0245 }
0246
0247 public:
0248 printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
0249 context_type& ctx)
0250 : base(make_arg_formatter(iter, s)), context_(ctx) {}
0251
0252 void operator()(monostate value) { base::operator()(value); }
0253
0254 template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
0255 void operator()(T value) {
0256
0257
0258 if (!std::is_same<T, Char>::value) {
0259 base::operator()(value);
0260 return;
0261 }
0262 format_specs<Char> fmt_specs = this->specs;
0263 if (fmt_specs.type != presentation_type::none &&
0264 fmt_specs.type != presentation_type::chr) {
0265 return (*this)(static_cast<int>(value));
0266 }
0267 fmt_specs.sign = sign::none;
0268 fmt_specs.alt = false;
0269 fmt_specs.fill[0] = ' ';
0270
0271
0272 if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
0273 fmt_specs.align = align::right;
0274 write<Char>(this->out, static_cast<Char>(value), fmt_specs);
0275 }
0276
0277 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
0278 void operator()(T value) {
0279 base::operator()(value);
0280 }
0281
0282
0283 void operator()(const char* value) {
0284 if (value)
0285 base::operator()(value);
0286 else
0287 write_null_pointer(this->specs.type != presentation_type::pointer);
0288 }
0289
0290
0291 void operator()(const wchar_t* value) {
0292 if (value)
0293 base::operator()(value);
0294 else
0295 write_null_pointer(this->specs.type != presentation_type::pointer);
0296 }
0297
0298 void operator()(basic_string_view<Char> value) { base::operator()(value); }
0299
0300
0301 void operator()(const void* value) {
0302 if (value)
0303 base::operator()(value);
0304 else
0305 write_null_pointer();
0306 }
0307
0308
0309 void operator()(typename basic_format_arg<context_type>::handle handle) {
0310 auto parse_ctx = basic_format_parse_context<Char>({});
0311 handle.format(parse_ctx, context_);
0312 }
0313 };
0314
0315 template <typename Char>
0316 void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
0317 for (; it != end; ++it) {
0318 switch (*it) {
0319 case '-':
0320 specs.align = align::left;
0321 break;
0322 case '+':
0323 specs.sign = sign::plus;
0324 break;
0325 case '0':
0326 specs.fill[0] = '0';
0327 break;
0328 case ' ':
0329 if (specs.sign != sign::plus) specs.sign = sign::space;
0330 break;
0331 case '#':
0332 specs.alt = true;
0333 break;
0334 default:
0335 return;
0336 }
0337 }
0338 }
0339
0340 template <typename Char, typename GetArg>
0341 auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
0342 GetArg get_arg) -> int {
0343 int arg_index = -1;
0344 Char c = *it;
0345 if (c >= '0' && c <= '9') {
0346
0347
0348 int value = parse_nonnegative_int(it, end, -1);
0349 if (it != end && *it == '$') {
0350 ++it;
0351 arg_index = value != -1 ? value : max_value<int>();
0352 } else {
0353 if (c == '0') specs.fill[0] = '0';
0354 if (value != 0) {
0355
0356
0357 if (value == -1) throw_format_error("number is too big");
0358 specs.width = value;
0359 return arg_index;
0360 }
0361 }
0362 }
0363 parse_flags(specs, it, end);
0364
0365 if (it != end) {
0366 if (*it >= '0' && *it <= '9') {
0367 specs.width = parse_nonnegative_int(it, end, -1);
0368 if (specs.width == -1) throw_format_error("number is too big");
0369 } else if (*it == '*') {
0370 ++it;
0371 specs.width = static_cast<int>(visit_format_arg(
0372 detail::printf_width_handler<Char>(specs), get_arg(-1)));
0373 }
0374 }
0375 return arg_index;
0376 }
0377
0378 inline auto parse_printf_presentation_type(char c, type t)
0379 -> presentation_type {
0380 using pt = presentation_type;
0381 constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
0382 switch (c) {
0383 case 'd':
0384 return in(t, integral_set) ? pt::dec : pt::none;
0385 case 'o':
0386 return in(t, integral_set) ? pt::oct : pt::none;
0387 case 'x':
0388 return in(t, integral_set) ? pt::hex_lower : pt::none;
0389 case 'X':
0390 return in(t, integral_set) ? pt::hex_upper : pt::none;
0391 case 'a':
0392 return in(t, float_set) ? pt::hexfloat_lower : pt::none;
0393 case 'A':
0394 return in(t, float_set) ? pt::hexfloat_upper : pt::none;
0395 case 'e':
0396 return in(t, float_set) ? pt::exp_lower : pt::none;
0397 case 'E':
0398 return in(t, float_set) ? pt::exp_upper : pt::none;
0399 case 'f':
0400 return in(t, float_set) ? pt::fixed_lower : pt::none;
0401 case 'F':
0402 return in(t, float_set) ? pt::fixed_upper : pt::none;
0403 case 'g':
0404 return in(t, float_set) ? pt::general_lower : pt::none;
0405 case 'G':
0406 return in(t, float_set) ? pt::general_upper : pt::none;
0407 case 'c':
0408 return in(t, integral_set) ? pt::chr : pt::none;
0409 case 's':
0410 return in(t, string_set | cstring_set) ? pt::string : pt::none;
0411 case 'p':
0412 return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
0413 default:
0414 return pt::none;
0415 }
0416 }
0417
0418 template <typename Char, typename Context>
0419 void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
0420 basic_format_args<Context> args) {
0421 using iterator = buffer_appender<Char>;
0422 auto out = iterator(buf);
0423 auto context = basic_printf_context<Char>(out, args);
0424 auto parse_ctx = basic_format_parse_context<Char>(format);
0425
0426
0427
0428 auto get_arg = [&](int arg_index) {
0429 if (arg_index < 0)
0430 arg_index = parse_ctx.next_arg_id();
0431 else
0432 parse_ctx.check_arg_id(--arg_index);
0433 return detail::get_arg(context, arg_index);
0434 };
0435
0436 const Char* start = parse_ctx.begin();
0437 const Char* end = parse_ctx.end();
0438 auto it = start;
0439 while (it != end) {
0440 if (!find<false, Char>(it, end, '%', it)) {
0441 it = end;
0442 break;
0443 }
0444 Char c = *it++;
0445 if (it != end && *it == c) {
0446 write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
0447 start = ++it;
0448 continue;
0449 }
0450 write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
0451
0452 auto specs = format_specs<Char>();
0453 specs.align = align::right;
0454
0455
0456 int arg_index = parse_header(it, end, specs, get_arg);
0457 if (arg_index == 0) throw_format_error("argument not found");
0458
0459
0460 if (it != end && *it == '.') {
0461 ++it;
0462 c = it != end ? *it : 0;
0463 if ('0' <= c && c <= '9') {
0464 specs.precision = parse_nonnegative_int(it, end, 0);
0465 } else if (c == '*') {
0466 ++it;
0467 specs.precision = static_cast<int>(
0468 visit_format_arg(printf_precision_handler(), get_arg(-1)));
0469 } else {
0470 specs.precision = 0;
0471 }
0472 }
0473
0474 auto arg = get_arg(arg_index);
0475
0476
0477 if (specs.precision >= 0 && arg.is_integral()) {
0478
0479 specs.fill[0] = ' ';
0480 }
0481 if (specs.precision >= 0 && arg.type() == type::cstring_type) {
0482 auto str = visit_format_arg(get_cstring<Char>(), arg);
0483 auto str_end = str + specs.precision;
0484 auto nul = std::find(str, str_end, Char());
0485 auto sv = basic_string_view<Char>(
0486 str, to_unsigned(nul != str_end ? nul - str : specs.precision));
0487 arg = make_arg<basic_printf_context<Char>>(sv);
0488 }
0489 if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
0490 if (specs.fill[0] == '0') {
0491 if (arg.is_arithmetic() && specs.align != align::left)
0492 specs.align = align::numeric;
0493 else
0494 specs.fill[0] = ' ';
0495
0496 }
0497
0498
0499 c = it != end ? *it++ : 0;
0500 Char t = it != end ? *it : 0;
0501 switch (c) {
0502 case 'h':
0503 if (t == 'h') {
0504 ++it;
0505 t = it != end ? *it : 0;
0506 convert_arg<signed char>(arg, t);
0507 } else {
0508 convert_arg<short>(arg, t);
0509 }
0510 break;
0511 case 'l':
0512 if (t == 'l') {
0513 ++it;
0514 t = it != end ? *it : 0;
0515 convert_arg<long long>(arg, t);
0516 } else {
0517 convert_arg<long>(arg, t);
0518 }
0519 break;
0520 case 'j':
0521 convert_arg<intmax_t>(arg, t);
0522 break;
0523 case 'z':
0524 convert_arg<size_t>(arg, t);
0525 break;
0526 case 't':
0527 convert_arg<std::ptrdiff_t>(arg, t);
0528 break;
0529 case 'L':
0530
0531
0532 break;
0533 default:
0534 --it;
0535 convert_arg<void>(arg, c);
0536 }
0537
0538
0539 if (it == end) throw_format_error("invalid format string");
0540 char type = static_cast<char>(*it++);
0541 if (arg.is_integral()) {
0542
0543 switch (type) {
0544 case 'i':
0545 case 'u':
0546 type = 'd';
0547 break;
0548 case 'c':
0549 visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
0550 break;
0551 }
0552 }
0553 specs.type = parse_printf_presentation_type(type, arg.type());
0554 if (specs.type == presentation_type::none)
0555 throw_format_error("invalid format specifier");
0556
0557 start = it;
0558
0559
0560 visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
0561 }
0562 write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
0563 }
0564 }
0565
0566 using printf_context = basic_printf_context<char>;
0567 using wprintf_context = basic_printf_context<wchar_t>;
0568
0569 using printf_args = basic_format_args<printf_context>;
0570 using wprintf_args = basic_format_args<wprintf_context>;
0571
0572
0573
0574
0575
0576
0577
0578 template <typename... T>
0579 inline auto make_printf_args(const T&... args)
0580 -> format_arg_store<printf_context, T...> {
0581 return {args...};
0582 }
0583
0584
0585 template <typename... T>
0586 inline auto make_wprintf_args(const T&... args)
0587 -> format_arg_store<wprintf_context, T...> {
0588 return {args...};
0589 }
0590
0591 template <typename Char>
0592 inline auto vsprintf(
0593 basic_string_view<Char> fmt,
0594 basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
0595 -> std::basic_string<Char> {
0596 auto buf = basic_memory_buffer<Char>();
0597 detail::vprintf(buf, fmt, args);
0598 return to_string(buf);
0599 }
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610 template <typename S, typename... T,
0611 typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
0612 inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
0613 return vsprintf(detail::to_string_view(fmt),
0614 fmt::make_format_args<basic_printf_context<Char>>(args...));
0615 }
0616
0617 template <typename Char>
0618 inline auto vfprintf(
0619 std::FILE* f, basic_string_view<Char> fmt,
0620 basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
0621 -> int {
0622 auto buf = basic_memory_buffer<Char>();
0623 detail::vprintf(buf, fmt, args);
0624 size_t size = buf.size();
0625 return std::fwrite(buf.data(), sizeof(Char), size, f) < size
0626 ? -1
0627 : static_cast<int>(size);
0628 }
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638
0639 template <typename S, typename... T, typename Char = char_t<S>>
0640 inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
0641 return vfprintf(f, detail::to_string_view(fmt),
0642 fmt::make_format_args<basic_printf_context<Char>>(args...));
0643 }
0644
0645 template <typename Char>
0646 FMT_DEPRECATED inline auto vprintf(
0647 basic_string_view<Char> fmt,
0648 basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
0649 -> int {
0650 return vfprintf(stdout, fmt, args);
0651 }
0652
0653
0654
0655
0656
0657
0658
0659
0660
0661
0662 template <typename... T>
0663 inline auto printf(string_view fmt, const T&... args) -> int {
0664 return vfprintf(stdout, fmt, make_printf_args(args...));
0665 }
0666 template <typename... T>
0667 FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
0668 const T&... args) -> int {
0669 return vfprintf(stdout, fmt, make_wprintf_args(args...));
0670 }
0671
0672 FMT_END_EXPORT
0673 FMT_END_NAMESPACE
0674
0675 #endif