Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-13 08:25:18

0001 // Formatting library for C++ - legacy printf implementation
0002 //
0003 // Copyright (c) 2012 - 2016, Victor Zverovich
0004 // All rights reserved.
0005 //
0006 // For the license information refer to format.h.
0007 
0008 #ifndef FMT_PRINTF_H_
0009 #define FMT_PRINTF_H_
0010 
0011 #ifndef FMT_MODULE
0012 #  include <algorithm>  // std::max
0013 #  include <limits>     // std::numeric_limits
0014 #endif
0015 
0016 #include "format.h"
0017 
0018 FMT_BEGIN_NAMESPACE
0019 FMT_BEGIN_EXPORT
0020 
0021 template <typename T> struct printf_formatter {
0022   printf_formatter() = delete;
0023 };
0024 
0025 template <typename Char> class basic_printf_context {
0026  private:
0027   basic_appender<Char> out_;
0028   basic_format_args<basic_printf_context> args_;
0029 
0030   static_assert(std::is_same<Char, char>::value ||
0031                     std::is_same<Char, wchar_t>::value,
0032                 "Unsupported code unit type.");
0033 
0034  public:
0035   using char_type = Char;
0036   using parse_context_type = parse_context<Char>;
0037   template <typename T> using formatter_type = printf_formatter<T>;
0038   enum { builtin_types = 1 };
0039 
0040   /// Constructs a `printf_context` object. References to the arguments are
0041   /// stored in the context object so make sure they have appropriate lifetimes.
0042   basic_printf_context(basic_appender<Char> out,
0043                        basic_format_args<basic_printf_context> args)
0044       : out_(out), args_(args) {}
0045 
0046   auto out() -> basic_appender<Char> { return out_; }
0047   void advance_to(basic_appender<Char>) {}
0048 
0049   auto locale() -> detail::locale_ref { return {}; }
0050 
0051   auto arg(int id) const -> basic_format_arg<basic_printf_context> {
0052     return args_.get(id);
0053   }
0054 };
0055 
0056 namespace detail {
0057 
0058 // Return the result via the out param to workaround gcc bug 77539.
0059 template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
0060 FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
0061   for (out = first; out != last; ++out) {
0062     if (*out == value) return true;
0063   }
0064   return false;
0065 }
0066 
0067 template <>
0068 inline auto find<false, char>(const char* first, const char* last, char value,
0069                               const char*& out) -> bool {
0070   out =
0071       static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
0072   return out != nullptr;
0073 }
0074 
0075 // Checks if a value fits in int - used to avoid warnings about comparing
0076 // signed and unsigned integers.
0077 template <bool IsSigned> struct int_checker {
0078   template <typename T> static auto fits_in_int(T value) -> bool {
0079     unsigned max = to_unsigned(max_value<int>());
0080     return value <= max;
0081   }
0082   inline static auto fits_in_int(bool) -> bool { return true; }
0083 };
0084 
0085 template <> struct int_checker<true> {
0086   template <typename T> static auto fits_in_int(T value) -> bool {
0087     return value >= (std::numeric_limits<int>::min)() &&
0088            value <= max_value<int>();
0089   }
0090   inline static auto fits_in_int(int) -> bool { return true; }
0091 };
0092 
0093 struct printf_precision_handler {
0094   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0095   auto operator()(T value) -> int {
0096     if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
0097       report_error("number is too big");
0098     return (std::max)(static_cast<int>(value), 0);
0099   }
0100 
0101   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0102   auto operator()(T) -> int {
0103     report_error("precision is not integer");
0104     return 0;
0105   }
0106 };
0107 
0108 // An argument visitor that returns true iff arg is a zero integer.
0109 struct is_zero_int {
0110   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0111   auto operator()(T value) -> bool {
0112     return value == 0;
0113   }
0114 
0115   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0116   auto operator()(T) -> bool {
0117     return false;
0118   }
0119 };
0120 
0121 template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
0122 
0123 template <> struct make_unsigned_or_bool<bool> {
0124   using type = bool;
0125 };
0126 
0127 template <typename T, typename Context> class arg_converter {
0128  private:
0129   using char_type = typename Context::char_type;
0130 
0131   basic_format_arg<Context>& arg_;
0132   char_type type_;
0133 
0134  public:
0135   arg_converter(basic_format_arg<Context>& arg, char_type type)
0136       : arg_(arg), type_(type) {}
0137 
0138   void operator()(bool value) {
0139     if (type_ != 's') operator()<bool>(value);
0140   }
0141 
0142   template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
0143   void operator()(U value) {
0144     bool is_signed = type_ == 'd' || type_ == 'i';
0145     using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
0146     if (const_check(sizeof(target_type) <= sizeof(int))) {
0147       // Extra casts are used to silence warnings.
0148       using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
0149       if (is_signed)
0150         arg_ = static_cast<int>(static_cast<target_type>(value));
0151       else
0152         arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
0153     } else {
0154       // glibc's printf doesn't sign extend arguments of smaller types:
0155       //   std::printf("%lld", -42);  // prints "4294967254"
0156       // but we don't have to do the same because it's a UB.
0157       if (is_signed)
0158         arg_ = static_cast<long long>(value);
0159       else
0160         arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
0161     }
0162   }
0163 
0164   template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
0165   void operator()(U) {}  // No conversion needed for non-integral types.
0166 };
0167 
0168 // Converts an integer argument to T for printf, if T is an integral type.
0169 // If T is void, the argument is converted to corresponding signed or unsigned
0170 // type depending on the type specifier: 'd' and 'i' - signed, other -
0171 // unsigned).
0172 template <typename T, typename Context, typename Char>
0173 void convert_arg(basic_format_arg<Context>& arg, Char type) {
0174   arg.visit(arg_converter<T, Context>(arg, type));
0175 }
0176 
0177 // Converts an integer argument to char for printf.
0178 template <typename Context> class char_converter {
0179  private:
0180   basic_format_arg<Context>& arg_;
0181 
0182  public:
0183   explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
0184 
0185   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0186   void operator()(T value) {
0187     arg_ = static_cast<typename Context::char_type>(value);
0188   }
0189 
0190   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0191   void operator()(T) {}  // No conversion needed for non-integral types.
0192 };
0193 
0194 // An argument visitor that return a pointer to a C string if argument is a
0195 // string or null otherwise.
0196 template <typename Char> struct get_cstring {
0197   template <typename T> auto operator()(T) -> const Char* { return nullptr; }
0198   auto operator()(const Char* s) -> const Char* { return s; }
0199 };
0200 
0201 // Checks if an argument is a valid printf width specifier and sets
0202 // left alignment if it is negative.
0203 class printf_width_handler {
0204  private:
0205   format_specs& specs_;
0206 
0207  public:
0208   inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
0209 
0210   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
0211   auto operator()(T value) -> unsigned {
0212     auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
0213     if (detail::is_negative(value)) {
0214       specs_.set_align(align::left);
0215       width = 0 - width;
0216     }
0217     unsigned int_max = to_unsigned(max_value<int>());
0218     if (width > int_max) report_error("number is too big");
0219     return static_cast<unsigned>(width);
0220   }
0221 
0222   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
0223   auto operator()(T) -> unsigned {
0224     report_error("width is not integer");
0225     return 0;
0226   }
0227 };
0228 
0229 // Workaround for a bug with the XL compiler when initializing
0230 // printf_arg_formatter's base class.
0231 template <typename Char>
0232 auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
0233     -> arg_formatter<Char> {
0234   return {iter, s, locale_ref()};
0235 }
0236 
0237 // The `printf` argument formatter.
0238 template <typename Char>
0239 class printf_arg_formatter : public arg_formatter<Char> {
0240  private:
0241   using base = arg_formatter<Char>;
0242   using context_type = basic_printf_context<Char>;
0243 
0244   context_type& context_;
0245 
0246   void write_null_pointer(bool is_string = false) {
0247     auto s = this->specs;
0248     s.set_type(presentation_type::none);
0249     write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
0250   }
0251 
0252   template <typename T> void write(T value) {
0253     detail::write<Char>(this->out, value, this->specs, this->locale);
0254   }
0255 
0256  public:
0257   printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
0258                        context_type& ctx)
0259       : base(make_arg_formatter(iter, s)), context_(ctx) {}
0260 
0261   void operator()(monostate value) { write(value); }
0262 
0263   template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
0264   void operator()(T value) {
0265     // MSVC2013 fails to compile separate overloads for bool and Char so use
0266     // std::is_same instead.
0267     if (!std::is_same<T, Char>::value) {
0268       write(value);
0269       return;
0270     }
0271     format_specs s = this->specs;
0272     if (s.type() != presentation_type::none &&
0273         s.type() != presentation_type::chr) {
0274       return (*this)(static_cast<int>(value));
0275     }
0276     s.set_sign(sign::none);
0277     s.clear_alt();
0278     s.set_fill(' ');  // Ignore '0' flag for char types.
0279     // align::numeric needs to be overwritten here since the '0' flag is
0280     // ignored for non-numeric types
0281     if (s.align() == align::none || s.align() == align::numeric)
0282       s.set_align(align::right);
0283     detail::write<Char>(this->out, static_cast<Char>(value), s);
0284   }
0285 
0286   template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
0287   void operator()(T value) {
0288     write(value);
0289   }
0290 
0291   void operator()(const char* value) {
0292     if (value)
0293       write(value);
0294     else
0295       write_null_pointer(this->specs.type() != presentation_type::pointer);
0296   }
0297 
0298   void operator()(const wchar_t* value) {
0299     if (value)
0300       write(value);
0301     else
0302       write_null_pointer(this->specs.type() != presentation_type::pointer);
0303   }
0304 
0305   void operator()(basic_string_view<Char> value) { write(value); }
0306 
0307   void operator()(const void* value) {
0308     if (value)
0309       write(value);
0310     else
0311       write_null_pointer();
0312   }
0313 
0314   void operator()(typename basic_format_arg<context_type>::handle handle) {
0315     auto parse_ctx = parse_context<Char>({});
0316     handle.format(parse_ctx, context_);
0317   }
0318 };
0319 
0320 template <typename Char>
0321 void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
0322   for (; it != end; ++it) {
0323     switch (*it) {
0324     case '-': specs.set_align(align::left); break;
0325     case '+': specs.set_sign(sign::plus); break;
0326     case '0': specs.set_fill('0'); break;
0327     case ' ':
0328       if (specs.sign() != sign::plus) specs.set_sign(sign::space);
0329       break;
0330     case '#': specs.set_alt(); break;
0331     default:  return;
0332     }
0333   }
0334 }
0335 
0336 template <typename Char, typename GetArg>
0337 auto parse_header(const Char*& it, const Char* end, format_specs& specs,
0338                   GetArg get_arg) -> int {
0339   int arg_index = -1;
0340   Char c = *it;
0341   if (c >= '0' && c <= '9') {
0342     // Parse an argument index (if followed by '$') or a width possibly
0343     // preceded with '0' flag(s).
0344     int value = parse_nonnegative_int(it, end, -1);
0345     if (it != end && *it == '$') {  // value is an argument index
0346       ++it;
0347       arg_index = value != -1 ? value : max_value<int>();
0348     } else {
0349       if (c == '0') specs.set_fill('0');
0350       if (value != 0) {
0351         // Nonzero value means that we parsed width and don't need to
0352         // parse it or flags again, so return now.
0353         if (value == -1) report_error("number is too big");
0354         specs.width = value;
0355         return arg_index;
0356       }
0357     }
0358   }
0359   parse_flags(specs, it, end);
0360   // Parse width.
0361   if (it != end) {
0362     if (*it >= '0' && *it <= '9') {
0363       specs.width = parse_nonnegative_int(it, end, -1);
0364       if (specs.width == -1) report_error("number is too big");
0365     } else if (*it == '*') {
0366       ++it;
0367       specs.width = static_cast<int>(
0368           get_arg(-1).visit(detail::printf_width_handler(specs)));
0369     }
0370   }
0371   return arg_index;
0372 }
0373 
0374 inline auto parse_printf_presentation_type(char c, type t, bool& upper)
0375     -> presentation_type {
0376   using pt = presentation_type;
0377   constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
0378   switch (c) {
0379   case 'd': return in(t, integral_set) ? pt::dec : pt::none;
0380   case 'o': return in(t, integral_set) ? pt::oct : pt::none;
0381   case 'X': upper = true; FMT_FALLTHROUGH;
0382   case 'x': return in(t, integral_set) ? pt::hex : pt::none;
0383   case 'E': upper = true; FMT_FALLTHROUGH;
0384   case 'e': return in(t, float_set) ? pt::exp : pt::none;
0385   case 'F': upper = true; FMT_FALLTHROUGH;
0386   case 'f': return in(t, float_set) ? pt::fixed : pt::none;
0387   case 'G': upper = true; FMT_FALLTHROUGH;
0388   case 'g': return in(t, float_set) ? pt::general : pt::none;
0389   case 'A': upper = true; FMT_FALLTHROUGH;
0390   case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
0391   case 'c': return in(t, integral_set) ? pt::chr : pt::none;
0392   case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
0393   case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
0394   default:  return pt::none;
0395   }
0396 }
0397 
0398 template <typename Char, typename Context>
0399 void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
0400              basic_format_args<Context> args) {
0401   using iterator = basic_appender<Char>;
0402   auto out = iterator(buf);
0403   auto context = basic_printf_context<Char>(out, args);
0404   auto parse_ctx = parse_context<Char>(format);
0405 
0406   // Returns the argument with specified index or, if arg_index is -1, the next
0407   // argument.
0408   auto get_arg = [&](int arg_index) {
0409     if (arg_index < 0)
0410       arg_index = parse_ctx.next_arg_id();
0411     else
0412       parse_ctx.check_arg_id(--arg_index);
0413     return detail::get_arg(context, arg_index);
0414   };
0415 
0416   const Char* start = parse_ctx.begin();
0417   const Char* end = parse_ctx.end();
0418   auto it = start;
0419   while (it != end) {
0420     if (!find<false, Char>(it, end, '%', it)) {
0421       it = end;  // find leaves it == nullptr if it doesn't find '%'.
0422       break;
0423     }
0424     Char c = *it++;
0425     if (it != end && *it == c) {
0426       write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
0427       start = ++it;
0428       continue;
0429     }
0430     write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
0431 
0432     auto specs = format_specs();
0433     specs.set_align(align::right);
0434 
0435     // Parse argument index, flags and width.
0436     int arg_index = parse_header(it, end, specs, get_arg);
0437     if (arg_index == 0) report_error("argument not found");
0438 
0439     // Parse precision.
0440     if (it != end && *it == '.') {
0441       ++it;
0442       c = it != end ? *it : 0;
0443       if ('0' <= c && c <= '9') {
0444         specs.precision = parse_nonnegative_int(it, end, 0);
0445       } else if (c == '*') {
0446         ++it;
0447         specs.precision =
0448             static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
0449       } else {
0450         specs.precision = 0;
0451       }
0452     }
0453 
0454     auto arg = get_arg(arg_index);
0455     // For d, i, o, u, x, and X conversion specifiers, if a precision is
0456     // specified, the '0' flag is ignored
0457     if (specs.precision >= 0 && is_integral_type(arg.type())) {
0458       // Ignore '0' for non-numeric types or if '-' present.
0459       specs.set_fill(' ');
0460     }
0461     if (specs.precision >= 0 && arg.type() == type::cstring_type) {
0462       auto str = arg.visit(get_cstring<Char>());
0463       auto str_end = str + specs.precision;
0464       auto nul = std::find(str, str_end, Char());
0465       auto sv = basic_string_view<Char>(
0466           str, to_unsigned(nul != str_end ? nul - str : specs.precision));
0467       arg = sv;
0468     }
0469     if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
0470     if (specs.fill_unit<Char>() == '0') {
0471       if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
0472         specs.set_align(align::numeric);
0473       } else {
0474         // Ignore '0' flag for non-numeric types or if '-' flag is also present.
0475         specs.set_fill(' ');
0476       }
0477     }
0478 
0479     // Parse length and convert the argument to the required type.
0480     c = it != end ? *it++ : 0;
0481     Char t = it != end ? *it : 0;
0482     switch (c) {
0483     case 'h':
0484       if (t == 'h') {
0485         ++it;
0486         t = it != end ? *it : 0;
0487         convert_arg<signed char>(arg, t);
0488       } else {
0489         convert_arg<short>(arg, t);
0490       }
0491       break;
0492     case 'l':
0493       if (t == 'l') {
0494         ++it;
0495         t = it != end ? *it : 0;
0496         convert_arg<long long>(arg, t);
0497       } else {
0498         convert_arg<long>(arg, t);
0499       }
0500       break;
0501     case 'j': convert_arg<intmax_t>(arg, t); break;
0502     case 'z': convert_arg<size_t>(arg, t); break;
0503     case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
0504     case 'L':
0505       // printf produces garbage when 'L' is omitted for long double, no
0506       // need to do the same.
0507       break;
0508     default: --it; convert_arg<void>(arg, c);
0509     }
0510 
0511     // Parse type.
0512     if (it == end) report_error("invalid format string");
0513     char type = static_cast<char>(*it++);
0514     if (is_integral_type(arg.type())) {
0515       // Normalize type.
0516       switch (type) {
0517       case 'i':
0518       case 'u': type = 'd'; break;
0519       case 'c':
0520         arg.visit(char_converter<basic_printf_context<Char>>(arg));
0521         break;
0522       }
0523     }
0524     bool upper = false;
0525     specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
0526     if (specs.type() == presentation_type::none)
0527       report_error("invalid format specifier");
0528     if (upper) specs.set_upper();
0529 
0530     start = it;
0531 
0532     // Format argument.
0533     arg.visit(printf_arg_formatter<Char>(out, specs, context));
0534   }
0535   write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
0536 }
0537 }  // namespace detail
0538 
0539 using printf_context = basic_printf_context<char>;
0540 using wprintf_context = basic_printf_context<wchar_t>;
0541 
0542 using printf_args = basic_format_args<printf_context>;
0543 using wprintf_args = basic_format_args<wprintf_context>;
0544 
0545 /// Constructs an `format_arg_store` object that contains references to
0546 /// arguments and can be implicitly converted to `printf_args`.
0547 template <typename Char = char, typename... T>
0548 inline auto make_printf_args(T&... args)
0549     -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
0550   return fmt::make_format_args<basic_printf_context<Char>>(args...);
0551 }
0552 
0553 template <typename Char> struct vprintf_args {
0554   using type = basic_format_args<basic_printf_context<Char>>;
0555 };
0556 
0557 template <typename Char>
0558 inline auto vsprintf(basic_string_view<Char> fmt,
0559                      typename vprintf_args<Char>::type args)
0560     -> std::basic_string<Char> {
0561   auto buf = basic_memory_buffer<Char>();
0562   detail::vprintf(buf, fmt, args);
0563   return {buf.data(), buf.size()};
0564 }
0565 
0566 /**
0567  * Formats `args` according to specifications in `fmt` and returns the result
0568  * as as string.
0569  *
0570  * **Example**:
0571  *
0572  *     std::string message = fmt::sprintf("The answer is %d", 42);
0573  */
0574 template <typename S, typename... T, typename Char = detail::char_t<S>>
0575 inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
0576   return vsprintf(detail::to_string_view(fmt),
0577                   fmt::make_format_args<basic_printf_context<Char>>(args...));
0578 }
0579 
0580 template <typename Char>
0581 inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
0582                      typename vprintf_args<Char>::type args) -> int {
0583   auto buf = basic_memory_buffer<Char>();
0584   detail::vprintf(buf, fmt, args);
0585   size_t size = buf.size();
0586   return std::fwrite(buf.data(), sizeof(Char), size, f) < size
0587              ? -1
0588              : static_cast<int>(size);
0589 }
0590 
0591 /**
0592  * Formats `args` according to specifications in `fmt` and writes the output
0593  * to `f`.
0594  *
0595  * **Example**:
0596  *
0597  *     fmt::fprintf(stderr, "Don't %s!", "panic");
0598  */
0599 template <typename S, typename... T, typename Char = detail::char_t<S>>
0600 inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
0601   return vfprintf(f, detail::to_string_view(fmt),
0602                   make_printf_args<Char>(args...));
0603 }
0604 
0605 template <typename Char>
0606 FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
0607                                    typename vprintf_args<Char>::type args)
0608     -> int {
0609   return vfprintf(stdout, fmt, args);
0610 }
0611 
0612 /**
0613  * Formats `args` according to specifications in `fmt` and writes the output
0614  * to `stdout`.
0615  *
0616  * **Example**:
0617  *
0618  *   fmt::printf("Elapsed time: %.2f seconds", 1.23);
0619  */
0620 template <typename... T>
0621 inline auto printf(string_view fmt, const T&... args) -> int {
0622   return vfprintf(stdout, fmt, make_printf_args(args...));
0623 }
0624 template <typename... T>
0625 FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
0626                                   const T&... args) -> int {
0627   return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
0628 }
0629 
0630 FMT_END_EXPORT
0631 FMT_END_NAMESPACE
0632 
0633 #endif  // FMT_PRINTF_H_