printf.h 20 KB

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