Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-03 08:36:29

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 #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     \rst
0039     Constructs a ``printf_context`` object. References to the arguments are
0040     stored in the context object so make sure they have appropriate lifetimes.
0041     \endrst
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 // Checks if a value fits in int - used to avoid warnings about comparing
0064 // signed and unsigned integers.
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 // An argument visitor that returns true iff arg is a zero integer.
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       // Extra casts are used to silence warnings.
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         // glibc's printf doesn't sign extend arguments of smaller types:
0147         //   std::printf("%lld", -42);  // prints "4294967254"
0148         // but we don't have to do the same because it's a UB.
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) {}  // No conversion needed for non-integral types.
0160 };
0161 
0162 // Converts an integer argument to T for printf, if T is an integral type.
0163 // If T is void, the argument is converted to corresponding signed or unsigned
0164 // type depending on the type specifier: 'd' and 'i' - signed, other -
0165 // unsigned).
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 // Converts an integer argument to char for printf.
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) {}  // No conversion needed for non-integral types.
0187 };
0188 
0189 // An argument visitor that return a pointer to a C string if argument is a
0190 // string or null otherwise.
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 // Checks if an argument is a valid printf width specifier and sets
0197 // left alignment if it is negative.
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 // Workaround for a bug with the XL compiler when initializing
0225 // printf_arg_formatter's base class.
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 // The ``printf`` argument formatter.
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     // MSVC2013 fails to compile separate overloads for bool and Char so use
0257     // std::is_same instead.
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] = ' ';  // Ignore '0' flag for char types.
0270     // align::numeric needs to be overwritten here since the '0' flag is
0271     // ignored for non-numeric types
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   /** Formats a null-terminated C string. */
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   /** Formats a null-terminated wide C string. */
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   /** Formats a pointer. */
0301   void operator()(const void* value) {
0302     if (value)
0303       base::operator()(value);
0304     else
0305       write_null_pointer();
0306   }
0307 
0308   /** Formats an argument of a custom (user-defined) type. */
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     // Parse an argument index (if followed by '$') or a width possibly
0347     // preceded with '0' flag(s).
0348     int value = parse_nonnegative_int(it, end, -1);
0349     if (it != end && *it == '$') {  // value is an argument index
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         // Nonzero value means that we parsed width and don't need to
0356         // parse it or flags again, so return now.
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   // Parse width.
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   // Returns the argument with specified index or, if arg_index is -1, the next
0427   // argument.
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;  // find leaves it == nullptr if it doesn't find '%'.
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     // Parse argument index, flags and width.
0456     int arg_index = parse_header(it, end, specs, get_arg);
0457     if (arg_index == 0) throw_format_error("argument not found");
0458 
0459     // Parse precision.
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     // For d, i, o, u, x, and X conversion specifiers, if a precision is
0476     // specified, the '0' flag is ignored
0477     if (specs.precision >= 0 && arg.is_integral()) {
0478       // Ignore '0' for non-numeric types or if '-' present.
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] = ' ';  // Ignore '0' flag for non-numeric types or if '-'
0495                               // flag is also present.
0496     }
0497 
0498     // Parse length and convert the argument to the required type.
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       // printf produces garbage when 'L' is omitted for long double, no
0531       // need to do the same.
0532       break;
0533     default:
0534       --it;
0535       convert_arg<void>(arg, c);
0536     }
0537 
0538     // Parse type.
0539     if (it == end) throw_format_error("invalid format string");
0540     char type = static_cast<char>(*it++);
0541     if (arg.is_integral()) {
0542       // Normalize type.
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     // Format argument.
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 }  // namespace detail
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   \rst
0574   Constructs an `~fmt::format_arg_store` object that contains references to
0575   arguments and can be implicitly converted to `~fmt::printf_args`.
0576   \endrst
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 // DEPRECATED!
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   \rst
0603   Formats arguments and returns the result as a string.
0604 
0605   **Example**::
0606 
0607     std::string message = fmt::sprintf("The answer is %d", 42);
0608   \endrst
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   \rst
0632   Prints formatted data to the file *f*.
0633 
0634   **Example**::
0635 
0636     fmt::fprintf(stderr, "Don't %s!", "panic");
0637   \endrst
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   \rst
0655   Prints formatted data to ``stdout``.
0656 
0657   **Example**::
0658 
0659     fmt::printf("Elapsed time: %.2f seconds", 1.23);
0660   \endrst
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  // FMT_PRINTF_H_