printf.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  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. #ifndef FMT_PRINTF_H_
  8. #define FMT_PRINTF_H_
  9. #include <algorithm> // std::max
  10. #include <limits> // std::numeric_limits
  11. #include "format.h"
  12. FMT_BEGIN_NAMESPACE
  13. FMT_BEGIN_EXPORT
  14. template <typename T> struct printf_formatter {
  15. printf_formatter() = delete;
  16. };
  17. template <typename Char> class basic_printf_context {
  18. private:
  19. detail::buffer_appender<Char> out_;
  20. basic_format_args<basic_printf_context> args_;
  21. static_assert(std::is_same<Char, char>::value ||
  22. std::is_same<Char, wchar_t>::value,
  23. "Unsupported code unit type.");
  24. public:
  25. using char_type = Char;
  26. using parse_context_type = basic_format_parse_context<Char>;
  27. template <typename T> using formatter_type = printf_formatter<T>;
  28. /**
  29. \rst
  30. Constructs a ``printf_context`` object. References to the arguments are
  31. stored in the context object so make sure they have appropriate lifetimes.
  32. \endrst
  33. */
  34. basic_printf_context(detail::buffer_appender<Char> out,
  35. basic_format_args<basic_printf_context> args)
  36. : out_(out), args_(args) {}
  37. auto out() -> detail::buffer_appender<Char> { return out_; }
  38. void advance_to(detail::buffer_appender<Char>) {}
  39. auto locale() -> detail::locale_ref { return {}; }
  40. auto arg(int id) const -> basic_format_arg<basic_printf_context> {
  41. return args_.get(id);
  42. }
  43. FMT_CONSTEXPR void on_error(const char* message) {
  44. detail::error_handler().on_error(message);
  45. }
  46. };
  47. namespace detail {
  48. // Checks if a value fits in int - used to avoid warnings about comparing
  49. // signed and unsigned integers.
  50. template <bool IsSigned> struct int_checker {
  51. template <typename T> static auto fits_in_int(T value) -> bool {
  52. unsigned max = max_value<int>();
  53. return value <= max;
  54. }
  55. static auto fits_in_int(bool) -> bool { return true; }
  56. };
  57. template <> struct int_checker<true> {
  58. template <typename T> static auto fits_in_int(T value) -> bool {
  59. return value >= (std::numeric_limits<int>::min)() &&
  60. value <= max_value<int>();
  61. }
  62. static auto fits_in_int(int) -> bool { return true; }
  63. };
  64. struct printf_precision_handler {
  65. template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
  66. auto operator()(T value) -> int {
  67. if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
  68. throw_format_error("number is too big");
  69. return (std::max)(static_cast<int>(value), 0);
  70. }
  71. template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
  72. auto operator()(T) -> int {
  73. throw_format_error("precision is not integer");
  74. return 0;
  75. }
  76. };
  77. // An argument visitor that returns true iff arg is a zero integer.
  78. struct is_zero_int {
  79. template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
  80. auto operator()(T value) -> bool {
  81. return value == 0;
  82. }
  83. template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
  84. auto operator()(T) -> bool {
  85. return false;
  86. }
  87. };
  88. template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
  89. template <> struct make_unsigned_or_bool<bool> {
  90. using type = bool;
  91. };
  92. template <typename T, typename Context> class arg_converter {
  93. private:
  94. using char_type = typename Context::char_type;
  95. basic_format_arg<Context>& arg_;
  96. char_type type_;
  97. public:
  98. arg_converter(basic_format_arg<Context>& arg, char_type type)
  99. : arg_(arg), type_(type) {}
  100. void operator()(bool value) {
  101. if (type_ != 's') operator()<bool>(value);
  102. }
  103. template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
  104. void operator()(U value) {
  105. bool is_signed = type_ == 'd' || type_ == 'i';
  106. using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
  107. if (const_check(sizeof(target_type) <= sizeof(int))) {
  108. // Extra casts are used to silence warnings.
  109. if (is_signed) {
  110. auto n = static_cast<int>(static_cast<target_type>(value));
  111. arg_ = detail::make_arg<Context>(n);
  112. } else {
  113. using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
  114. auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
  115. arg_ = detail::make_arg<Context>(n);
  116. }
  117. } else {
  118. if (is_signed) {
  119. // glibc's printf doesn't sign extend arguments of smaller types:
  120. // std::printf("%lld", -42); // prints "4294967254"
  121. // but we don't have to do the same because it's a UB.
  122. auto n = static_cast<long long>(value);
  123. arg_ = detail::make_arg<Context>(n);
  124. } else {
  125. auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
  126. arg_ = detail::make_arg<Context>(n);
  127. }
  128. }
  129. }
  130. template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
  131. void operator()(U) {} // No conversion needed for non-integral types.
  132. };
  133. // Converts an integer argument to T for printf, if T is an integral type.
  134. // If T is void, the argument is converted to corresponding signed or unsigned
  135. // type depending on the type specifier: 'd' and 'i' - signed, other -
  136. // unsigned).
  137. template <typename T, typename Context, typename Char>
  138. void convert_arg(basic_format_arg<Context>& arg, Char type) {
  139. visit_format_arg(arg_converter<T, Context>(arg, type), arg);
  140. }
  141. // Converts an integer argument to char for printf.
  142. template <typename Context> class char_converter {
  143. private:
  144. basic_format_arg<Context>& arg_;
  145. public:
  146. explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
  147. template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
  148. void operator()(T value) {
  149. auto c = static_cast<typename Context::char_type>(value);
  150. arg_ = detail::make_arg<Context>(c);
  151. }
  152. template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
  153. void operator()(T) {} // No conversion needed for non-integral types.
  154. };
  155. // An argument visitor that return a pointer to a C string if argument is a
  156. // string or null otherwise.
  157. template <typename Char> struct get_cstring {
  158. template <typename T> auto operator()(T) -> const Char* { return nullptr; }
  159. auto operator()(const Char* s) -> const Char* { return s; }
  160. };
  161. // Checks if an argument is a valid printf width specifier and sets
  162. // left alignment if it is negative.
  163. template <typename Char> class printf_width_handler {
  164. private:
  165. format_specs<Char>& specs_;
  166. public:
  167. explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
  168. template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
  169. auto operator()(T value) -> unsigned {
  170. auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
  171. if (detail::is_negative(value)) {
  172. specs_.align = align::left;
  173. width = 0 - width;
  174. }
  175. unsigned int_max = max_value<int>();
  176. if (width > int_max) throw_format_error("number is too big");
  177. return static_cast<unsigned>(width);
  178. }
  179. template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
  180. auto operator()(T) -> unsigned {
  181. throw_format_error("width is not integer");
  182. return 0;
  183. }
  184. };
  185. // Workaround for a bug with the XL compiler when initializing
  186. // printf_arg_formatter's base class.
  187. template <typename Char>
  188. auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
  189. -> arg_formatter<Char> {
  190. return {iter, s, locale_ref()};
  191. }
  192. // The ``printf`` argument formatter.
  193. template <typename Char>
  194. class printf_arg_formatter : public arg_formatter<Char> {
  195. private:
  196. using base = arg_formatter<Char>;
  197. using context_type = basic_printf_context<Char>;
  198. context_type& context_;
  199. void write_null_pointer(bool is_string = false) {
  200. auto s = this->specs;
  201. s.type = presentation_type::none;
  202. write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
  203. }
  204. public:
  205. printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
  206. context_type& ctx)
  207. : base(make_arg_formatter(iter, s)), context_(ctx) {}
  208. void operator()(monostate value) { base::operator()(value); }
  209. template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
  210. void operator()(T value) {
  211. // MSVC2013 fails to compile separate overloads for bool and Char so use
  212. // std::is_same instead.
  213. if (!std::is_same<T, Char>::value) {
  214. base::operator()(value);
  215. return;
  216. }
  217. format_specs<Char> fmt_specs = this->specs;
  218. if (fmt_specs.type != presentation_type::none &&
  219. fmt_specs.type != presentation_type::chr) {
  220. return (*this)(static_cast<int>(value));
  221. }
  222. fmt_specs.sign = sign::none;
  223. fmt_specs.alt = false;
  224. fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
  225. // align::numeric needs to be overwritten here since the '0' flag is
  226. // ignored for non-numeric types
  227. if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
  228. fmt_specs.align = align::right;
  229. write<Char>(this->out, static_cast<Char>(value), fmt_specs);
  230. }
  231. template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
  232. void operator()(T value) {
  233. base::operator()(value);
  234. }
  235. /** Formats a null-terminated C string. */
  236. void operator()(const char* value) {
  237. if (value)
  238. base::operator()(value);
  239. else
  240. write_null_pointer(this->specs.type != presentation_type::pointer);
  241. }
  242. /** Formats a null-terminated wide C string. */
  243. void operator()(const wchar_t* value) {
  244. if (value)
  245. base::operator()(value);
  246. else
  247. write_null_pointer(this->specs.type != presentation_type::pointer);
  248. }
  249. void operator()(basic_string_view<Char> value) { base::operator()(value); }
  250. /** Formats a pointer. */
  251. void operator()(const void* value) {
  252. if (value)
  253. base::operator()(value);
  254. else
  255. write_null_pointer();
  256. }
  257. /** Formats an argument of a custom (user-defined) type. */
  258. void operator()(typename basic_format_arg<context_type>::handle handle) {
  259. auto parse_ctx = basic_format_parse_context<Char>({});
  260. handle.format(parse_ctx, context_);
  261. }
  262. };
  263. template <typename Char>
  264. void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
  265. for (; it != end; ++it) {
  266. switch (*it) {
  267. case '-':
  268. specs.align = align::left;
  269. break;
  270. case '+':
  271. specs.sign = sign::plus;
  272. break;
  273. case '0':
  274. specs.fill[0] = '0';
  275. break;
  276. case ' ':
  277. if (specs.sign != sign::plus) specs.sign = sign::space;
  278. break;
  279. case '#':
  280. specs.alt = true;
  281. break;
  282. default:
  283. return;
  284. }
  285. }
  286. }
  287. template <typename Char, typename GetArg>
  288. auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
  289. GetArg get_arg) -> int {
  290. int arg_index = -1;
  291. Char c = *it;
  292. if (c >= '0' && c <= '9') {
  293. // Parse an argument index (if followed by '$') or a width possibly
  294. // preceded with '0' flag(s).
  295. int value = parse_nonnegative_int(it, end, -1);
  296. if (it != end && *it == '$') { // value is an argument index
  297. ++it;
  298. arg_index = value != -1 ? value : max_value<int>();
  299. } else {
  300. if (c == '0') specs.fill[0] = '0';
  301. if (value != 0) {
  302. // Nonzero value means that we parsed width and don't need to
  303. // parse it or flags again, so return now.
  304. if (value == -1) throw_format_error("number is too big");
  305. specs.width = value;
  306. return arg_index;
  307. }
  308. }
  309. }
  310. parse_flags(specs, it, end);
  311. // Parse width.
  312. if (it != end) {
  313. if (*it >= '0' && *it <= '9') {
  314. specs.width = parse_nonnegative_int(it, end, -1);
  315. if (specs.width == -1) throw_format_error("number is too big");
  316. } else if (*it == '*') {
  317. ++it;
  318. specs.width = static_cast<int>(visit_format_arg(
  319. detail::printf_width_handler<Char>(specs), get_arg(-1)));
  320. }
  321. }
  322. return arg_index;
  323. }
  324. inline auto parse_printf_presentation_type(char c, type t)
  325. -> presentation_type {
  326. using pt = presentation_type;
  327. constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
  328. switch (c) {
  329. case 'd':
  330. return in(t, integral_set) ? pt::dec : pt::none;
  331. case 'o':
  332. return in(t, integral_set) ? pt::oct : pt::none;
  333. case 'x':
  334. return in(t, integral_set) ? pt::hex_lower : pt::none;
  335. case 'X':
  336. return in(t, integral_set) ? pt::hex_upper : pt::none;
  337. case 'a':
  338. return in(t, float_set) ? pt::hexfloat_lower : pt::none;
  339. case 'A':
  340. return in(t, float_set) ? pt::hexfloat_upper : pt::none;
  341. case 'e':
  342. return in(t, float_set) ? pt::exp_lower : pt::none;
  343. case 'E':
  344. return in(t, float_set) ? pt::exp_upper : pt::none;
  345. case 'f':
  346. return in(t, float_set) ? pt::fixed_lower : pt::none;
  347. case 'F':
  348. return in(t, float_set) ? pt::fixed_upper : pt::none;
  349. case 'g':
  350. return in(t, float_set) ? pt::general_lower : pt::none;
  351. case 'G':
  352. return in(t, float_set) ? pt::general_upper : pt::none;
  353. case 'c':
  354. return in(t, integral_set) ? pt::chr : pt::none;
  355. case 's':
  356. return in(t, string_set | cstring_set) ? pt::string : pt::none;
  357. case 'p':
  358. return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
  359. default:
  360. return pt::none;
  361. }
  362. }
  363. template <typename Char, typename Context>
  364. void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
  365. basic_format_args<Context> args) {
  366. using iterator = buffer_appender<Char>;
  367. auto out = iterator(buf);
  368. auto context = basic_printf_context<Char>(out, args);
  369. auto parse_ctx = basic_format_parse_context<Char>(format);
  370. // Returns the argument with specified index or, if arg_index is -1, the next
  371. // argument.
  372. auto get_arg = [&](int arg_index) {
  373. if (arg_index < 0)
  374. arg_index = parse_ctx.next_arg_id();
  375. else
  376. parse_ctx.check_arg_id(--arg_index);
  377. return detail::get_arg(context, arg_index);
  378. };
  379. const Char* start = parse_ctx.begin();
  380. const Char* end = parse_ctx.end();
  381. auto it = start;
  382. while (it != end) {
  383. if (!find<false, Char>(it, end, '%', it)) {
  384. it = end; // find leaves it == nullptr if it doesn't find '%'.
  385. break;
  386. }
  387. Char c = *it++;
  388. if (it != end && *it == c) {
  389. write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
  390. start = ++it;
  391. continue;
  392. }
  393. write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
  394. auto specs = format_specs<Char>();
  395. specs.align = align::right;
  396. // Parse argument index, flags and width.
  397. int arg_index = parse_header(it, end, specs, get_arg);
  398. if (arg_index == 0) throw_format_error("argument not found");
  399. // Parse precision.
  400. if (it != end && *it == '.') {
  401. ++it;
  402. c = it != end ? *it : 0;
  403. if ('0' <= c && c <= '9') {
  404. specs.precision = parse_nonnegative_int(it, end, 0);
  405. } else if (c == '*') {
  406. ++it;
  407. specs.precision = static_cast<int>(
  408. visit_format_arg(printf_precision_handler(), get_arg(-1)));
  409. } else {
  410. specs.precision = 0;
  411. }
  412. }
  413. auto arg = get_arg(arg_index);
  414. // For d, i, o, u, x, and X conversion specifiers, if a precision is
  415. // specified, the '0' flag is ignored
  416. if (specs.precision >= 0 && arg.is_integral()) {
  417. // Ignore '0' for non-numeric types or if '-' present.
  418. specs.fill[0] = ' ';
  419. }
  420. if (specs.precision >= 0 && arg.type() == type::cstring_type) {
  421. auto str = visit_format_arg(get_cstring<Char>(), arg);
  422. auto str_end = str + specs.precision;
  423. auto nul = std::find(str, str_end, Char());
  424. auto sv = basic_string_view<Char>(
  425. str, to_unsigned(nul != str_end ? nul - str : specs.precision));
  426. arg = make_arg<basic_printf_context<Char>>(sv);
  427. }
  428. if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
  429. if (specs.fill[0] == '0') {
  430. if (arg.is_arithmetic() && specs.align != align::left)
  431. specs.align = align::numeric;
  432. else
  433. specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
  434. // flag is also present.
  435. }
  436. // Parse length and convert the argument to the required type.
  437. c = it != end ? *it++ : 0;
  438. Char t = it != end ? *it : 0;
  439. switch (c) {
  440. case 'h':
  441. if (t == 'h') {
  442. ++it;
  443. t = it != end ? *it : 0;
  444. convert_arg<signed char>(arg, t);
  445. } else {
  446. convert_arg<short>(arg, t);
  447. }
  448. break;
  449. case 'l':
  450. if (t == 'l') {
  451. ++it;
  452. t = it != end ? *it : 0;
  453. convert_arg<long long>(arg, t);
  454. } else {
  455. convert_arg<long>(arg, t);
  456. }
  457. break;
  458. case 'j':
  459. convert_arg<intmax_t>(arg, t);
  460. break;
  461. case 'z':
  462. convert_arg<size_t>(arg, t);
  463. break;
  464. case 't':
  465. convert_arg<std::ptrdiff_t>(arg, t);
  466. break;
  467. case 'L':
  468. // printf produces garbage when 'L' is omitted for long double, no
  469. // need to do the same.
  470. break;
  471. default:
  472. --it;
  473. convert_arg<void>(arg, c);
  474. }
  475. // Parse type.
  476. if (it == end) throw_format_error("invalid format string");
  477. char type = static_cast<char>(*it++);
  478. if (arg.is_integral()) {
  479. // Normalize type.
  480. switch (type) {
  481. case 'i':
  482. case 'u':
  483. type = 'd';
  484. break;
  485. case 'c':
  486. visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
  487. break;
  488. }
  489. }
  490. specs.type = parse_printf_presentation_type(type, arg.type());
  491. if (specs.type == presentation_type::none)
  492. throw_format_error("invalid format specifier");
  493. start = it;
  494. // Format argument.
  495. visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
  496. }
  497. write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
  498. }
  499. } // namespace detail
  500. using printf_context = basic_printf_context<char>;
  501. using wprintf_context = basic_printf_context<wchar_t>;
  502. using printf_args = basic_format_args<printf_context>;
  503. using wprintf_args = basic_format_args<wprintf_context>;
  504. /**
  505. \rst
  506. Constructs an `~fmt::format_arg_store` object that contains references to
  507. arguments and can be implicitly converted to `~fmt::printf_args`.
  508. \endrst
  509. */
  510. template <typename... T>
  511. inline auto make_printf_args(const T&... args)
  512. -> format_arg_store<printf_context, T...> {
  513. return {args...};
  514. }
  515. // DEPRECATED!
  516. template <typename... T>
  517. inline auto make_wprintf_args(const T&... args)
  518. -> format_arg_store<wprintf_context, T...> {
  519. return {args...};
  520. }
  521. template <typename Char>
  522. inline auto vsprintf(
  523. basic_string_view<Char> fmt,
  524. basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
  525. -> std::basic_string<Char> {
  526. auto buf = basic_memory_buffer<Char>();
  527. detail::vprintf(buf, fmt, args);
  528. return to_string(buf);
  529. }
  530. /**
  531. \rst
  532. Formats arguments and returns the result as a string.
  533. **Example**::
  534. std::string message = fmt::sprintf("The answer is %d", 42);
  535. \endrst
  536. */
  537. template <typename S, typename... T,
  538. typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
  539. inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
  540. return vsprintf(detail::to_string_view(fmt),
  541. fmt::make_format_args<basic_printf_context<Char>>(args...));
  542. }
  543. template <typename Char>
  544. inline auto vfprintf(
  545. std::FILE* f, basic_string_view<Char> fmt,
  546. basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
  547. -> int {
  548. auto buf = basic_memory_buffer<Char>();
  549. detail::vprintf(buf, fmt, args);
  550. size_t size = buf.size();
  551. return std::fwrite(buf.data(), sizeof(Char), size, f) < size
  552. ? -1
  553. : static_cast<int>(size);
  554. }
  555. /**
  556. \rst
  557. Prints formatted data to the file *f*.
  558. **Example**::
  559. fmt::fprintf(stderr, "Don't %s!", "panic");
  560. \endrst
  561. */
  562. template <typename S, typename... T, typename Char = char_t<S>>
  563. inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
  564. return vfprintf(f, detail::to_string_view(fmt),
  565. fmt::make_format_args<basic_printf_context<Char>>(args...));
  566. }
  567. template <typename Char>
  568. FMT_DEPRECATED inline auto vprintf(
  569. basic_string_view<Char> fmt,
  570. basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
  571. -> int {
  572. return vfprintf(stdout, fmt, args);
  573. }
  574. /**
  575. \rst
  576. Prints formatted data to ``stdout``.
  577. **Example**::
  578. fmt::printf("Elapsed time: %.2f seconds", 1.23);
  579. \endrst
  580. */
  581. template <typename... T>
  582. inline auto printf(string_view fmt, const T&... args) -> int {
  583. return vfprintf(stdout, fmt, make_printf_args(args...));
  584. }
  585. template <typename... T>
  586. FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
  587. const T&... args) -> int {
  588. return vfprintf(stdout, fmt, make_wprintf_args(args...));
  589. }
  590. FMT_END_EXPORT
  591. FMT_END_NAMESPACE
  592. #endif // FMT_PRINTF_H_