]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/fmt/include/fmt/printf.h
update download target update for octopus release
[ceph.git] / ceph / src / seastar / fmt / include / fmt / printf.h
CommitLineData
11fdf7f2
TL
1// Formatting library for C++
2//
3// Copyright (c) 2012 - 2016, Victor Zverovich
4// All rights reserved.
5//
6// For the license information refer to format.h.
7
8#ifndef FMT_PRINTF_H_
9#define FMT_PRINTF_H_
10
11#include <algorithm> // std::fill_n
12#include <limits> // std::numeric_limits
13
14#include "ostream.h"
15
16FMT_BEGIN_NAMESPACE
17namespace internal {
18
19// Checks if a value fits in int - used to avoid warnings about comparing
20// signed and unsigned integers.
21template <bool IsSigned>
22struct int_checker {
23 template <typename T>
24 static bool fits_in_int(T value) {
25 unsigned max = std::numeric_limits<int>::max();
26 return value <= max;
27 }
28 static bool fits_in_int(bool) { return true; }
29};
30
31template <>
32struct int_checker<true> {
33 template <typename T>
34 static bool fits_in_int(T value) {
35 return value >= std::numeric_limits<int>::min() &&
36 value <= std::numeric_limits<int>::max();
37 }
38 static bool fits_in_int(int) { return true; }
39};
40
41class printf_precision_handler: public function<int> {
42 public:
43 template <typename T>
44 typename std::enable_if<std::is_integral<T>::value, int>::type
45 operator()(T value) {
46 if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
47 FMT_THROW(format_error("number is too big"));
48 return static_cast<int>(value);
49 }
50
51 template <typename T>
52 typename std::enable_if<!std::is_integral<T>::value, int>::type operator()(T) {
53 FMT_THROW(format_error("precision is not integer"));
54 return 0;
55 }
56};
57
58// An argument visitor that returns true iff arg is a zero integer.
59class is_zero_int: public function<bool> {
60 public:
61 template <typename T>
62 typename std::enable_if<std::is_integral<T>::value, bool>::type
63 operator()(T value) { return value == 0; }
64
65 template <typename T>
66 typename std::enable_if<!std::is_integral<T>::value, bool>::type
67 operator()(T) { return false; }
68};
69
70template <typename T>
71struct make_unsigned_or_bool : std::make_unsigned<T> {};
72
73template <>
74struct make_unsigned_or_bool<bool> {
75 typedef bool type;
76};
77
78template <typename T, typename Context>
79class arg_converter: public function<void> {
80 private:
81 typedef typename Context::char_type Char;
82
83 basic_format_arg<Context> &arg_;
84 typename Context::char_type type_;
85
86 public:
87 arg_converter(basic_format_arg<Context> &arg, Char type)
88 : arg_(arg), type_(type) {}
89
90 void operator()(bool value) {
91 if (type_ != 's')
92 operator()<bool>(value);
93 }
94
95 template <typename U>
96 typename std::enable_if<std::is_integral<U>::value>::type
97 operator()(U value) {
98 bool is_signed = type_ == 'd' || type_ == 'i';
99 typedef typename std::conditional<
100 std::is_same<T, void>::value, U, T>::type TargetType;
101 if (const_check(sizeof(TargetType) <= sizeof(int))) {
102 // Extra casts are used to silence warnings.
103 if (is_signed) {
104 arg_ = internal::make_arg<Context>(
105 static_cast<int>(static_cast<TargetType>(value)));
106 } else {
107 typedef typename make_unsigned_or_bool<TargetType>::type Unsigned;
108 arg_ = internal::make_arg<Context>(
109 static_cast<unsigned>(static_cast<Unsigned>(value)));
110 }
111 } else {
112 if (is_signed) {
113 // glibc's printf doesn't sign extend arguments of smaller types:
114 // std::printf("%lld", -42); // prints "4294967254"
115 // but we don't have to do the same because it's a UB.
116 arg_ = internal::make_arg<Context>(static_cast<long long>(value));
117 } else {
118 arg_ = internal::make_arg<Context>(
119 static_cast<typename make_unsigned_or_bool<U>::type>(value));
120 }
121 }
122 }
123
124 template <typename U>
125 typename std::enable_if<!std::is_integral<U>::value>::type operator()(U) {
126 // No coversion needed for non-integral types.
127 }
128};
129
130// Converts an integer argument to T for printf, if T is an integral type.
131// If T is void, the argument is converted to corresponding signed or unsigned
132// type depending on the type specifier: 'd' and 'i' - signed, other -
133// unsigned).
134template <typename T, typename Context, typename Char>
135void convert_arg(basic_format_arg<Context> &arg, Char type) {
eafe8130 136 visit_format_arg(arg_converter<T, Context>(arg, type), arg);
11fdf7f2
TL
137}
138
139// Converts an integer argument to char for printf.
140template <typename Context>
141class char_converter: public function<void> {
142 private:
143 basic_format_arg<Context> &arg_;
144
145 public:
146 explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
147
148 template <typename T>
149 typename std::enable_if<std::is_integral<T>::value>::type
150 operator()(T value) {
151 typedef typename Context::char_type Char;
152 arg_ = internal::make_arg<Context>(static_cast<Char>(value));
153 }
154
155 template <typename T>
156 typename std::enable_if<!std::is_integral<T>::value>::type operator()(T) {
157 // No coversion needed for non-integral types.
158 }
159};
160
161// Checks if an argument is a valid printf width specifier and sets
162// left alignment if it is negative.
163template <typename Char>
164class printf_width_handler: public function<unsigned> {
165 private:
166 typedef basic_format_specs<Char> format_specs;
167
168 format_specs &spec_;
169
170 public:
171 explicit printf_width_handler(format_specs &spec) : spec_(spec) {}
172
173 template <typename T>
174 typename std::enable_if<std::is_integral<T>::value, unsigned>::type
175 operator()(T value) {
176 typedef typename internal::int_traits<T>::main_type UnsignedType;
177 UnsignedType width = static_cast<UnsignedType>(value);
178 if (internal::is_negative(value)) {
179 spec_.align_ = ALIGN_LEFT;
180 width = 0 - width;
181 }
182 unsigned int_max = std::numeric_limits<int>::max();
183 if (width > int_max)
184 FMT_THROW(format_error("number is too big"));
185 return static_cast<unsigned>(width);
186 }
187
188 template <typename T>
189 typename std::enable_if<!std::is_integral<T>::value, unsigned>::type
190 operator()(T) {
191 FMT_THROW(format_error("width is not integer"));
192 return 0;
193 }
194};
195} // namespace internal
196
197template <typename Range>
198class printf_arg_formatter;
199
200template <
201 typename OutputIt, typename Char,
202 typename ArgFormatter =
203 printf_arg_formatter<back_insert_range<internal::basic_buffer<Char>>>>
204class basic_printf_context;
205
206/**
207 \rst
208 The ``printf`` argument formatter.
209 \endrst
210 */
211template <typename Range>
212class printf_arg_formatter:
213 public internal::function<
214 typename internal::arg_formatter_base<Range>::iterator>,
215 public internal::arg_formatter_base<Range> {
216 private:
217 typedef typename Range::value_type char_type;
218 typedef decltype(internal::declval<Range>().begin()) iterator;
219 typedef internal::arg_formatter_base<Range> base;
220 typedef basic_printf_context<iterator, char_type> context_type;
221
222 context_type &context_;
223
224 void write_null_pointer(char) {
eafe8130 225 this->spec()->type = 0;
11fdf7f2
TL
226 this->write("(nil)");
227 }
228
229 void write_null_pointer(wchar_t) {
eafe8130 230 this->spec()->type = 0;
11fdf7f2
TL
231 this->write(L"(nil)");
232 }
233
234 public:
235 typedef typename base::format_specs format_specs;
236
237 /**
238 \rst
239 Constructs an argument formatter object.
240 *buffer* is a reference to the output buffer and *spec* contains format
241 specifier information for standard argument types.
242 \endrst
243 */
244 printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
245 format_specs &spec, context_type &ctx)
eafe8130
TL
246 : base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec,
247 ctx.locale()),
11fdf7f2
TL
248 context_(ctx) {}
249
250 template <typename T>
251 typename std::enable_if<std::is_integral<T>::value, iterator>::type
252 operator()(T value) {
253 // MSVC2013 fails to compile separate overloads for bool and char_type so
254 // use std::is_same instead.
255 if (std::is_same<T, bool>::value) {
256 format_specs &fmt_spec = *this->spec();
eafe8130 257 if (fmt_spec.type != 's')
11fdf7f2 258 return base::operator()(value ? 1 : 0);
eafe8130 259 fmt_spec.type = 0;
11fdf7f2
TL
260 this->write(value != 0);
261 } else if (std::is_same<T, char_type>::value) {
262 format_specs &fmt_spec = *this->spec();
eafe8130 263 if (fmt_spec.type && fmt_spec.type != 'c')
11fdf7f2 264 return (*this)(static_cast<int>(value));
eafe8130 265 fmt_spec.flags = 0;
11fdf7f2
TL
266 fmt_spec.align_ = ALIGN_RIGHT;
267 return base::operator()(value);
268 } else {
269 return base::operator()(value);
270 }
271 return this->out();
272 }
273
274 template <typename T>
275 typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
276 operator()(T value) {
277 return base::operator()(value);
278 }
279
280 /** Formats a null-terminated C string. */
281 iterator operator()(const char *value) {
282 if (value)
283 base::operator()(value);
eafe8130 284 else if (this->spec()->type == 'p')
11fdf7f2
TL
285 write_null_pointer(char_type());
286 else
287 this->write("(null)");
288 return this->out();
289 }
290
291 /** Formats a null-terminated wide C string. */
292 iterator operator()(const wchar_t *value) {
293 if (value)
294 base::operator()(value);
eafe8130 295 else if (this->spec()->type == 'p')
11fdf7f2
TL
296 write_null_pointer(char_type());
297 else
298 this->write(L"(null)");
299 return this->out();
300 }
301
302 iterator operator()(basic_string_view<char_type> value) {
303 return base::operator()(value);
304 }
305
306 iterator operator()(monostate value) {
307 return base::operator()(value);
308 }
309
310 /** Formats a pointer. */
311 iterator operator()(const void *value) {
312 if (value)
313 return base::operator()(value);
eafe8130 314 this->spec()->type = 0;
11fdf7f2
TL
315 write_null_pointer(char_type());
316 return this->out();
317 }
318
319 /** Formats an argument of a custom (user-defined) type. */
320 iterator operator()(typename basic_format_arg<context_type>::handle handle) {
321 handle.format(context_);
322 return this->out();
323 }
324};
325
326template <typename T>
327struct printf_formatter {
328 template <typename ParseContext>
329 auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { return ctx.begin(); }
330
331 template <typename FormatContext>
332 auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out()) {
333 internal::format_value(internal::get_container(ctx.out()), value);
334 return ctx.out();
335 }
336};
337
338/** This template formats data and writes the output to a writer. */
339template <typename OutputIt, typename Char, typename ArgFormatter>
340class basic_printf_context :
341 // Inherit publicly as a workaround for the icc bug
342 // https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476.
343 public internal::context_base<
344 OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
345 public:
346 /** The character type for the output. */
347 typedef Char char_type;
348
349 template <typename T>
350 struct formatter_type { typedef printf_formatter<T> type; };
351
352 private:
353 typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
354 typedef typename base::format_arg format_arg;
355 typedef basic_format_specs<char_type> format_specs;
356 typedef internal::null_terminating_iterator<char_type> iterator;
357
358 void parse_flags(format_specs &spec, iterator &it);
359
360 // Returns the argument with specified index or, if arg_index is equal
361 // to the maximum unsigned value, the next argument.
362 format_arg get_arg(
363 iterator it,
364 unsigned arg_index = (std::numeric_limits<unsigned>::max)());
365
366 // Parses argument index, flags and width and returns the argument index.
367 unsigned parse_header(iterator &it, format_specs &spec);
368
369 public:
370 /**
371 \rst
372 Constructs a ``printf_context`` object. References to the arguments and
373 the writer are stored in the context object so make sure they have
374 appropriate lifetimes.
375 \endrst
376 */
377 basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
378 basic_format_args<basic_printf_context> args)
379 : base(out, format_str, args) {}
380
381 using base::parse_context;
382 using base::out;
383 using base::advance_to;
384
385 /** Formats stored arguments and writes the output to the range. */
386 void format();
387};
388
389template <typename OutputIt, typename Char, typename AF>
390void basic_printf_context<OutputIt, Char, AF>::parse_flags(
391 format_specs &spec, iterator &it) {
392 for (;;) {
393 switch (*it++) {
394 case '-':
395 spec.align_ = ALIGN_LEFT;
396 break;
397 case '+':
eafe8130 398 spec.flags |= SIGN_FLAG | PLUS_FLAG;
11fdf7f2
TL
399 break;
400 case '0':
401 spec.fill_ = '0';
402 break;
403 case ' ':
eafe8130 404 spec.flags |= SIGN_FLAG;
11fdf7f2
TL
405 break;
406 case '#':
eafe8130 407 spec.flags |= HASH_FLAG;
11fdf7f2
TL
408 break;
409 default:
410 --it;
411 return;
412 }
413 }
414}
415
416template <typename OutputIt, typename Char, typename AF>
417typename basic_printf_context<OutputIt, Char, AF>::format_arg
418 basic_printf_context<OutputIt, Char, AF>::get_arg(
419 iterator it, unsigned arg_index) {
420 (void)it;
421 if (arg_index == std::numeric_limits<unsigned>::max())
422 return this->do_get_arg(this->parse_context().next_arg_id());
423 return base::get_arg(arg_index - 1);
424}
425
426template <typename OutputIt, typename Char, typename AF>
427unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
428 iterator &it, format_specs &spec) {
429 unsigned arg_index = std::numeric_limits<unsigned>::max();
430 char_type c = *it;
431 if (c >= '0' && c <= '9') {
432 // Parse an argument index (if followed by '$') or a width possibly
433 // preceded with '0' flag(s).
434 internal::error_handler eh;
435 unsigned value = parse_nonnegative_int(it, eh);
436 if (*it == '$') { // value is an argument index
437 ++it;
438 arg_index = value;
439 } else {
440 if (c == '0')
441 spec.fill_ = '0';
442 if (value != 0) {
443 // Nonzero value means that we parsed width and don't need to
444 // parse it or flags again, so return now.
445 spec.width_ = value;
446 return arg_index;
447 }
448 }
449 }
450 parse_flags(spec, it);
451 // Parse width.
452 if (*it >= '0' && *it <= '9') {
453 internal::error_handler eh;
454 spec.width_ = parse_nonnegative_int(it, eh);
455 } else if (*it == '*') {
456 ++it;
eafe8130
TL
457 spec.width_ = visit_format_arg(
458 internal::printf_width_handler<char_type>(spec), get_arg(it));
11fdf7f2
TL
459 }
460 return arg_index;
461}
462
463template <typename OutputIt, typename Char, typename AF>
464void basic_printf_context<OutputIt, Char, AF>::format() {
465 auto &buffer = internal::get_container(this->out());
466 auto start = iterator(this->parse_context());
467 auto it = start;
468 using internal::pointer_from;
469 while (*it) {
470 char_type c = *it++;
471 if (c != '%') continue;
472 if (*it == c) {
473 buffer.append(pointer_from(start), pointer_from(it));
474 start = ++it;
475 continue;
476 }
477 buffer.append(pointer_from(start), pointer_from(it) - 1);
478
479 format_specs spec;
480 spec.align_ = ALIGN_RIGHT;
481
482 // Parse argument index, flags and width.
483 unsigned arg_index = parse_header(it, spec);
484
485 // Parse precision.
486 if (*it == '.') {
487 ++it;
488 if ('0' <= *it && *it <= '9') {
489 internal::error_handler eh;
eafe8130 490 spec.precision = static_cast<int>(parse_nonnegative_int(it, eh));
11fdf7f2
TL
491 } else if (*it == '*') {
492 ++it;
eafe8130
TL
493 spec.precision =
494 visit_format_arg(internal::printf_precision_handler(), get_arg(it));
11fdf7f2 495 } else {
eafe8130 496 spec.precision = 0;
11fdf7f2
TL
497 }
498 }
499
500 format_arg arg = get_arg(it, arg_index);
eafe8130
TL
501 if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
502 spec.flags &= ~internal::to_unsigned<int>(HASH_FLAG);
11fdf7f2
TL
503 if (spec.fill_ == '0') {
504 if (arg.is_arithmetic())
505 spec.align_ = ALIGN_NUMERIC;
506 else
507 spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
508 }
509
510 // Parse length and convert the argument to the required type.
511 using internal::convert_arg;
512 switch (*it++) {
513 case 'h':
514 if (*it == 'h')
515 convert_arg<signed char>(arg, *++it);
516 else
517 convert_arg<short>(arg, *it);
518 break;
519 case 'l':
520 if (*it == 'l')
521 convert_arg<long long>(arg, *++it);
522 else
523 convert_arg<long>(arg, *it);
524 break;
525 case 'j':
526 convert_arg<intmax_t>(arg, *it);
527 break;
528 case 'z':
529 convert_arg<std::size_t>(arg, *it);
530 break;
531 case 't':
532 convert_arg<std::ptrdiff_t>(arg, *it);
533 break;
534 case 'L':
535 // printf produces garbage when 'L' is omitted for long double, no
536 // need to do the same.
537 break;
538 default:
539 --it;
540 convert_arg<void>(arg, *it);
541 }
542
543 // Parse type.
544 if (!*it)
545 FMT_THROW(format_error("invalid format string"));
eafe8130 546 spec.type = static_cast<char>(*it++);
11fdf7f2
TL
547 if (arg.is_integral()) {
548 // Normalize type.
eafe8130 549 switch (spec.type) {
11fdf7f2 550 case 'i': case 'u':
eafe8130 551 spec.type = 'd';
11fdf7f2
TL
552 break;
553 case 'c':
554 // TODO: handle wchar_t better?
eafe8130
TL
555 visit_format_arg(
556 internal::char_converter<basic_printf_context>(arg), arg);
11fdf7f2
TL
557 break;
558 }
559 }
560
561 start = it;
562
563 // Format argument.
eafe8130 564 visit_format_arg(AF(buffer, spec, *this), arg);
11fdf7f2
TL
565 }
566 buffer.append(pointer_from(start), pointer_from(it));
567}
568
569template <typename Char, typename Context>
570void printf(internal::basic_buffer<Char> &buf, basic_string_view<Char> format,
571 basic_format_args<Context> args) {
572 Context(std::back_inserter(buf), format, args).format();
573}
574
575template <typename Buffer>
eafe8130 576struct basic_printf_context_t {
11fdf7f2
TL
577 typedef basic_printf_context<
578 std::back_insert_iterator<Buffer>, typename Buffer::value_type> type;
579};
580
eafe8130
TL
581typedef basic_printf_context_t<internal::buffer>::type printf_context;
582typedef basic_printf_context_t<internal::wbuffer>::type wprintf_context;
583
584typedef basic_format_args<printf_context> printf_args;
585typedef basic_format_args<wprintf_context> wprintf_args;
586
587/**
588 \rst
589 Constructs an `~fmt::format_arg_store` object that contains references to
590 arguments and can be implicitly converted to `~fmt::printf_args`.
591 \endrst
592 */
593template<typename... Args>
594inline format_arg_store<printf_context, Args...>
595 make_printf_args(const Args &... args) { return {args...}; }
11fdf7f2 596
eafe8130
TL
597/**
598 \rst
599 Constructs an `~fmt::format_arg_store` object that contains references to
600 arguments and can be implicitly converted to `~fmt::wprintf_args`.
601 \endrst
602 */
603template<typename... Args>
604inline format_arg_store<wprintf_context, Args...>
605 make_wprintf_args(const Args &... args) { return {args...}; }
606
607template <typename S, typename Char = FMT_CHAR(S)>
608inline std::basic_string<Char>
609vsprintf(const S &format,
610 basic_format_args<typename basic_printf_context_t<
611 internal::basic_buffer<Char>>::type> args) {
612 basic_memory_buffer<Char> buffer;
613 printf(buffer, to_string_view(format), args);
11fdf7f2
TL
614 return to_string(buffer);
615}
616
617/**
618 \rst
619 Formats arguments and returns the result as a string.
620
621 **Example**::
622
623 std::string message = fmt::sprintf("The answer is %d", 42);
624 \endrst
625*/
eafe8130
TL
626template <typename S, typename... Args>
627inline FMT_ENABLE_IF_STRING(S, std::basic_string<FMT_CHAR(S)>)
628 sprintf(const S &format, const Args & ... args) {
629 internal::check_format_string<Args...>(format);
630 typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
631 typedef typename basic_printf_context_t<buffer>::type context;
632 format_arg_store<context, Args...> as{ args... };
633 return vsprintf(to_string_view(format),
634 basic_format_args<context>(as));
11fdf7f2
TL
635}
636
eafe8130
TL
637template <typename S, typename Char = FMT_CHAR(S)>
638inline int vfprintf(std::FILE *f, const S &format,
639 basic_format_args<typename basic_printf_context_t<
11fdf7f2
TL
640 internal::basic_buffer<Char>>::type> args) {
641 basic_memory_buffer<Char> buffer;
eafe8130 642 printf(buffer, to_string_view(format), args);
11fdf7f2
TL
643 std::size_t size = buffer.size();
644 return std::fwrite(
645 buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
646}
647
648/**
649 \rst
650 Prints formatted data to the file *f*.
651
652 **Example**::
653
654 fmt::fprintf(stderr, "Don't %s!", "panic");
655 \endrst
656 */
eafe8130
TL
657template <typename S, typename... Args>
658inline FMT_ENABLE_IF_STRING(S, int)
659 fprintf(std::FILE *f, const S &format, const Args & ... args) {
660 internal::check_format_string<Args...>(format);
661 typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
662 typedef typename basic_printf_context_t<buffer>::type context;
663 format_arg_store<context, Args...> as{ args... };
664 return vfprintf(f, to_string_view(format),
665 basic_format_args<context>(as));
11fdf7f2
TL
666}
667
eafe8130
TL
668template <typename S, typename Char = FMT_CHAR(S)>
669inline int vprintf(const S &format,
670 basic_format_args<typename basic_printf_context_t<
671 internal::basic_buffer<Char>>::type> args) {
672 return vfprintf(stdout, to_string_view(format), args);
11fdf7f2
TL
673}
674
675/**
676 \rst
677 Prints formatted data to ``stdout``.
678
679 **Example**::
680
681 fmt::printf("Elapsed time: %.2f seconds", 1.23);
682 \endrst
683 */
eafe8130
TL
684template <typename S, typename... Args>
685inline FMT_ENABLE_IF_STRING(S, int)
686 printf(const S &format_str, const Args & ... args) {
687 internal::check_format_string<Args...>(format_str);
688 typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
689 typedef typename basic_printf_context_t<buffer>::type context;
690 format_arg_store<context, Args...> as{ args... };
691 return vprintf(to_string_view(format_str),
692 basic_format_args<context>(as));
11fdf7f2
TL
693}
694
eafe8130
TL
695template <typename S, typename Char = FMT_CHAR(S)>
696inline int vfprintf(std::basic_ostream<Char> &os,
697 const S &format,
698 basic_format_args<typename basic_printf_context_t<
699 internal::basic_buffer<Char>>::type> args) {
700 basic_memory_buffer<Char> buffer;
701 printf(buffer, to_string_view(format), args);
11fdf7f2
TL
702 internal::write(os, buffer);
703 return static_cast<int>(buffer.size());
704}
705
706/**
707 \rst
708 Prints formatted data to the stream *os*.
709
710 **Example**::
711
712 fmt::fprintf(cerr, "Don't %s!", "panic");
713 \endrst
714 */
eafe8130
TL
715template <typename S, typename... Args>
716inline FMT_ENABLE_IF_STRING(S, int)
717 fprintf(std::basic_ostream<FMT_CHAR(S)> &os,
718 const S &format_str, const Args & ... args) {
719 internal::check_format_string<Args...>(format_str);
720 typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
721 typedef typename basic_printf_context_t<buffer>::type context;
722 format_arg_store<context, Args...> as{ args... };
723 return vfprintf(os, to_string_view(format_str),
724 basic_format_args<context>(as));
11fdf7f2
TL
725}
726FMT_END_NAMESPACE
727
728#endif // FMT_PRINTF_H_