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