1 // Formatting library for C++ - legacy printf implementation
3 // Copyright (c) 2012 - 2016, Victor Zverovich
4 // All rights reserved.
6 // For the license information refer to format.h.
11 #include <algorithm> // std::max
12 #include <limits> // std::numeric_limits
17 FMT_MODULE_EXPORT_BEGIN
19 template <typename T
> struct printf_formatter
{ printf_formatter() = delete; };
21 template <typename Char
>
22 class basic_printf_parse_context
: public basic_format_parse_context
<Char
> {
23 using basic_format_parse_context
<Char
>::basic_format_parse_context
;
26 template <typename OutputIt
, typename Char
> class basic_printf_context
{
29 basic_format_args
<basic_printf_context
> args_
;
32 using char_type
= Char
;
33 using format_arg
= basic_format_arg
<basic_printf_context
>;
34 using parse_context_type
= basic_printf_parse_context
<Char
>;
35 template <typename T
> using formatter_type
= printf_formatter
<T
>;
39 Constructs a ``printf_context`` object. References to the arguments are
40 stored in the context object so make sure they have appropriate lifetimes.
43 basic_printf_context(OutputIt out
,
44 basic_format_args
<basic_printf_context
> args
)
45 : out_(out
), args_(args
) {}
47 OutputIt
out() { return out_
; }
48 void advance_to(OutputIt it
) { out_
= it
; }
50 detail::locale_ref
locale() { return {}; }
52 format_arg
arg(int id
) const { return args_
.get(id
); }
54 FMT_CONSTEXPR
void on_error(const char* message
) {
55 detail::error_handler().on_error(message
);
59 FMT_BEGIN_DETAIL_NAMESPACE
61 // Checks if a value fits in int - used to avoid warnings about comparing
62 // signed and unsigned integers.
63 template <bool IsSigned
> struct int_checker
{
64 template <typename T
> static bool fits_in_int(T value
) {
65 unsigned max
= max_value
<int>();
68 static bool fits_in_int(bool) { return true; }
71 template <> struct int_checker
<true> {
72 template <typename T
> static bool fits_in_int(T value
) {
73 return value
>= (std::numeric_limits
<int>::min
)() &&
74 value
<= max_value
<int>();
76 static bool fits_in_int(int) { return true; }
79 class printf_precision_handler
{
81 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
82 int operator()(T value
) {
83 if (!int_checker
<std::numeric_limits
<T
>::is_signed
>::fits_in_int(value
))
84 FMT_THROW(format_error("number is too big"));
85 return (std::max
)(static_cast<int>(value
), 0);
88 template <typename T
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
90 FMT_THROW(format_error("precision is not integer"));
95 // An argument visitor that returns true iff arg is a zero integer.
98 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
99 bool operator()(T value
) {
103 template <typename T
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
109 template <typename T
> struct make_unsigned_or_bool
: std::make_unsigned
<T
> {};
111 template <> struct make_unsigned_or_bool
<bool> { using type
= bool; };
113 template <typename T
, typename Context
> class arg_converter
{
115 using char_type
= typename
Context::char_type
;
117 basic_format_arg
<Context
>& arg_
;
121 arg_converter(basic_format_arg
<Context
>& arg
, char_type type
)
122 : arg_(arg
), type_(type
) {}
124 void operator()(bool value
) {
125 if (type_
!= 's') operator()<bool>(value
);
128 template <typename U
, FMT_ENABLE_IF(std::is_integral
<U
>::value
)>
129 void operator()(U value
) {
130 bool is_signed
= type_
== 'd' || type_
== 'i';
131 using target_type
= conditional_t
<std::is_same
<T
, void>::value
, U
, T
>;
132 if (const_check(sizeof(target_type
) <= sizeof(int))) {
133 // Extra casts are used to silence warnings.
135 arg_
= detail::make_arg
<Context
>(
136 static_cast<int>(static_cast<target_type
>(value
)));
138 using unsigned_type
= typename make_unsigned_or_bool
<target_type
>::type
;
139 arg_
= detail::make_arg
<Context
>(
140 static_cast<unsigned>(static_cast<unsigned_type
>(value
)));
144 // glibc's printf doesn't sign extend arguments of smaller types:
145 // std::printf("%lld", -42); // prints "4294967254"
146 // but we don't have to do the same because it's a UB.
147 arg_
= detail::make_arg
<Context
>(static_cast<long long>(value
));
149 arg_
= detail::make_arg
<Context
>(
150 static_cast<typename make_unsigned_or_bool
<U
>::type
>(value
));
155 template <typename U
, FMT_ENABLE_IF(!std::is_integral
<U
>::value
)>
156 void operator()(U
) {} // No conversion needed for non-integral types.
159 // Converts an integer argument to T for printf, if T is an integral type.
160 // If T is void, the argument is converted to corresponding signed or unsigned
161 // type depending on the type specifier: 'd' and 'i' - signed, other -
163 template <typename T
, typename Context
, typename Char
>
164 void convert_arg(basic_format_arg
<Context
>& arg
, Char type
) {
165 visit_format_arg(arg_converter
<T
, Context
>(arg
, type
), arg
);
168 // Converts an integer argument to char for printf.
169 template <typename Context
> class char_converter
{
171 basic_format_arg
<Context
>& arg_
;
174 explicit char_converter(basic_format_arg
<Context
>& arg
) : arg_(arg
) {}
176 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
177 void operator()(T value
) {
178 arg_
= detail::make_arg
<Context
>(
179 static_cast<typename
Context::char_type
>(value
));
182 template <typename T
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
183 void operator()(T
) {} // No conversion needed for non-integral types.
186 // An argument visitor that return a pointer to a C string if argument is a
187 // string or null otherwise.
188 template <typename Char
> struct get_cstring
{
189 template <typename T
> const Char
* operator()(T
) { return nullptr; }
190 const Char
* operator()(const Char
* s
) { return s
; }
193 // Checks if an argument is a valid printf width specifier and sets
194 // left alignment if it is negative.
195 template <typename Char
> class printf_width_handler
{
197 using format_specs
= basic_format_specs
<Char
>;
199 format_specs
& specs_
;
202 explicit printf_width_handler(format_specs
& specs
) : specs_(specs
) {}
204 template <typename T
, FMT_ENABLE_IF(std::is_integral
<T
>::value
)>
205 unsigned operator()(T value
) {
206 auto width
= static_cast<uint32_or_64_or_128_t
<T
>>(value
);
207 if (detail::is_negative(value
)) {
208 specs_
.align
= align::left
;
211 unsigned int_max
= max_value
<int>();
212 if (width
> int_max
) FMT_THROW(format_error("number is too big"));
213 return static_cast<unsigned>(width
);
216 template <typename T
, FMT_ENABLE_IF(!std::is_integral
<T
>::value
)>
217 unsigned operator()(T
) {
218 FMT_THROW(format_error("width is not integer"));
223 // The ``printf`` argument formatter.
224 template <typename OutputIt
, typename Char
>
225 class printf_arg_formatter
: public arg_formatter
<Char
> {
227 using base
= arg_formatter
<Char
>;
228 using context_type
= basic_printf_context
<OutputIt
, Char
>;
229 using format_specs
= basic_format_specs
<Char
>;
231 context_type
& context_
;
233 OutputIt
write_null_pointer(bool is_string
= false) {
234 auto s
= this->specs
;
235 s
.type
= presentation_type::none
;
236 return write_bytes(this->out
, is_string
? "(null)" : "(nil)", s
);
240 printf_arg_formatter(OutputIt iter
, format_specs
& s
, context_type
& ctx
)
241 : base
{iter
, s
, locale_ref()}, context_(ctx
) {}
243 OutputIt
operator()(monostate value
) { return base::operator()(value
); }
245 template <typename T
, FMT_ENABLE_IF(detail::is_integral
<T
>::value
)>
246 OutputIt
operator()(T value
) {
247 // MSVC2013 fails to compile separate overloads for bool and Char so use
248 // std::is_same instead.
249 if (std::is_same
<T
, Char
>::value
) {
250 format_specs fmt_specs
= this->specs
;
251 if (fmt_specs
.type
!= presentation_type::none
&&
252 fmt_specs
.type
!= presentation_type::chr
) {
253 return (*this)(static_cast<int>(value
));
255 fmt_specs
.sign
= sign::none
;
256 fmt_specs
.alt
= false;
257 fmt_specs
.fill
[0] = ' '; // Ignore '0' flag for char types.
258 // align::numeric needs to be overwritten here since the '0' flag is
259 // ignored for non-numeric types
260 if (fmt_specs
.align
== align::none
|| fmt_specs
.align
== align::numeric
)
261 fmt_specs
.align
= align::right
;
262 return write
<Char
>(this->out
, static_cast<Char
>(value
), fmt_specs
);
264 return base::operator()(value
);
267 template <typename T
, FMT_ENABLE_IF(std::is_floating_point
<T
>::value
)>
268 OutputIt
operator()(T value
) {
269 return base::operator()(value
);
272 /** Formats a null-terminated C string. */
273 OutputIt
operator()(const char* value
) {
274 if (value
) return base::operator()(value
);
275 return write_null_pointer(this->specs
.type
!= presentation_type::pointer
);
278 /** Formats a null-terminated wide C string. */
279 OutputIt
operator()(const wchar_t* value
) {
280 if (value
) return base::operator()(value
);
281 return write_null_pointer(this->specs
.type
!= presentation_type::pointer
);
284 OutputIt
operator()(basic_string_view
<Char
> value
) {
285 return base::operator()(value
);
288 /** Formats a pointer. */
289 OutputIt
operator()(const void* value
) {
290 return value
? base::operator()(value
) : write_null_pointer();
293 /** Formats an argument of a custom (user-defined) type. */
294 OutputIt
operator()(typename basic_format_arg
<context_type
>::handle handle
) {
296 basic_printf_parse_context
<Char
>(basic_string_view
<Char
>());
297 handle
.format(parse_ctx
, context_
);
302 template <typename Char
>
303 void parse_flags(basic_format_specs
<Char
>& specs
, const Char
*& it
,
305 for (; it
!= end
; ++it
) {
308 specs
.align
= align::left
;
311 specs
.sign
= sign::plus
;
317 if (specs
.sign
!= sign::plus
) {
318 specs
.sign
= sign::space
;
330 template <typename Char
, typename GetArg
>
331 int parse_header(const Char
*& it
, const Char
* end
,
332 basic_format_specs
<Char
>& specs
, GetArg get_arg
) {
335 if (c
>= '0' && c
<= '9') {
336 // Parse an argument index (if followed by '$') or a width possibly
337 // preceded with '0' flag(s).
338 int value
= parse_nonnegative_int(it
, end
, -1);
339 if (it
!= end
&& *it
== '$') { // value is an argument index
341 arg_index
= value
!= -1 ? value
: max_value
<int>();
343 if (c
== '0') specs
.fill
[0] = '0';
345 // Nonzero value means that we parsed width and don't need to
346 // parse it or flags again, so return now.
347 if (value
== -1) FMT_THROW(format_error("number is too big"));
353 parse_flags(specs
, it
, end
);
356 if (*it
>= '0' && *it
<= '9') {
357 specs
.width
= parse_nonnegative_int(it
, end
, -1);
358 if (specs
.width
== -1) FMT_THROW(format_error("number is too big"));
359 } else if (*it
== '*') {
361 specs
.width
= static_cast<int>(visit_format_arg(
362 detail::printf_width_handler
<Char
>(specs
), get_arg(-1)));
368 template <typename Char
, typename Context
>
369 void vprintf(buffer
<Char
>& buf
, basic_string_view
<Char
> format
,
370 basic_format_args
<Context
> args
) {
371 using OutputIt
= buffer_appender
<Char
>;
372 auto out
= OutputIt(buf
);
373 auto context
= basic_printf_context
<OutputIt
, Char
>(out
, args
);
374 auto parse_ctx
= basic_printf_parse_context
<Char
>(format
);
376 // Returns the argument with specified index or, if arg_index is -1, the next
378 auto get_arg
= [&](int arg_index
) {
380 arg_index
= parse_ctx
.next_arg_id();
382 parse_ctx
.check_arg_id(--arg_index
);
383 return detail::get_arg(context
, arg_index
);
386 const Char
* start
= parse_ctx
.begin();
387 const Char
* end
= parse_ctx
.end();
390 if (!detail::find
<false, Char
>(it
, end
, '%', it
)) {
391 it
= end
; // detail::find leaves it == nullptr if it doesn't find '%'
395 if (it
!= end
&& *it
== c
) {
397 out
, basic_string_view
<Char
>(start
, detail::to_unsigned(it
- start
)));
401 out
= detail::write(out
, basic_string_view
<Char
>(
402 start
, detail::to_unsigned(it
- 1 - start
)));
404 basic_format_specs
<Char
> specs
;
405 specs
.align
= align::right
;
407 // Parse argument index, flags and width.
408 int arg_index
= parse_header(it
, end
, specs
, get_arg
);
409 if (arg_index
== 0) parse_ctx
.on_error("argument not found");
412 if (it
!= end
&& *it
== '.') {
414 c
= it
!= end
? *it
: 0;
415 if ('0' <= c
&& c
<= '9') {
416 specs
.precision
= parse_nonnegative_int(it
, end
, 0);
417 } else if (c
== '*') {
419 specs
.precision
= static_cast<int>(
420 visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
426 auto arg
= get_arg(arg_index
);
427 // For d, i, o, u, x, and X conversion specifiers, if a precision is
428 // specified, the '0' flag is ignored
429 if (specs
.precision
>= 0 && arg
.is_integral())
431 ' '; // Ignore '0' flag for non-numeric types or if '-' present.
432 if (specs
.precision
>= 0 && arg
.type() == detail::type::cstring_type
) {
433 auto str
= visit_format_arg(detail::get_cstring
<Char
>(), arg
);
434 auto str_end
= str
+ specs
.precision
;
435 auto nul
= std::find(str
, str_end
, Char());
436 arg
= detail::make_arg
<basic_printf_context
<OutputIt
, Char
>>(
437 basic_string_view
<Char
>(
438 str
, detail::to_unsigned(nul
!= str_end
? nul
- str
439 : specs
.precision
)));
441 if (specs
.alt
&& visit_format_arg(detail::is_zero_int(), arg
))
443 if (specs
.fill
[0] == '0') {
444 if (arg
.is_arithmetic() && specs
.align
!= align::left
)
445 specs
.align
= align::numeric
;
447 specs
.fill
[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
448 // flag is also present.
451 // Parse length and convert the argument to the required type.
452 c
= it
!= end
? *it
++ : 0;
453 Char t
= it
!= end
? *it
: 0;
454 using detail::convert_arg
;
459 t
= it
!= end
? *it
: 0;
460 convert_arg
<signed char>(arg
, t
);
462 convert_arg
<short>(arg
, t
);
468 t
= it
!= end
? *it
: 0;
469 convert_arg
<long long>(arg
, t
);
471 convert_arg
<long>(arg
, t
);
475 convert_arg
<intmax_t>(arg
, t
);
478 convert_arg
<size_t>(arg
, t
);
481 convert_arg
<std::ptrdiff_t>(arg
, t
);
484 // printf produces garbage when 'L' is omitted for long double, no
485 // need to do the same.
489 convert_arg
<void>(arg
, c
);
493 if (it
== end
) FMT_THROW(format_error("invalid format string"));
494 char type
= static_cast<char>(*it
++);
495 if (arg
.is_integral()) {
504 detail::char_converter
<basic_printf_context
<OutputIt
, Char
>>(arg
),
509 specs
.type
= parse_presentation_type(type
);
510 if (specs
.type
== presentation_type::none
)
511 parse_ctx
.on_error("invalid type specifier");
516 out
= visit_format_arg(
517 detail::printf_arg_formatter
<OutputIt
, Char
>(out
, specs
, context
), arg
);
519 detail::write(out
, basic_string_view
<Char
>(start
, to_unsigned(it
- start
)));
521 FMT_END_DETAIL_NAMESPACE
523 template <typename Char
>
524 using basic_printf_context_t
=
525 basic_printf_context
<detail::buffer_appender
<Char
>, Char
>;
527 using printf_context
= basic_printf_context_t
<char>;
528 using wprintf_context
= basic_printf_context_t
<wchar_t>;
530 using printf_args
= basic_format_args
<printf_context
>;
531 using wprintf_args
= basic_format_args
<wprintf_context
>;
535 Constructs an `~fmt::format_arg_store` object that contains references to
536 arguments and can be implicitly converted to `~fmt::printf_args`.
539 template <typename
... T
>
540 inline auto make_printf_args(const T
&... args
)
541 -> format_arg_store
<printf_context
, T
...> {
547 Constructs an `~fmt::format_arg_store` object that contains references to
548 arguments and can be implicitly converted to `~fmt::wprintf_args`.
551 template <typename
... T
>
552 inline auto make_wprintf_args(const T
&... args
)
553 -> format_arg_store
<wprintf_context
, T
...> {
557 template <typename S
, typename Char
= char_t
<S
>>
558 inline auto vsprintf(
560 basic_format_args
<basic_printf_context_t
<type_identity_t
<Char
>>> args
)
561 -> std::basic_string
<Char
> {
562 basic_memory_buffer
<Char
> buffer
;
563 vprintf(buffer
, detail::to_string_view(fmt
), args
);
564 return to_string(buffer
);
569 Formats arguments and returns the result as a string.
573 std::string message = fmt::sprintf("The answer is %d", 42);
576 template <typename S
, typename
... T
,
577 typename Char
= enable_if_t
<detail::is_string
<S
>::value
, char_t
<S
>>>
578 inline auto sprintf(const S
& fmt
, const T
&... args
) -> std::basic_string
<Char
> {
579 using context
= basic_printf_context_t
<Char
>;
580 return vsprintf(detail::to_string_view(fmt
),
581 fmt::make_format_args
<context
>(args
...));
584 template <typename S
, typename Char
= char_t
<S
>>
585 inline auto vfprintf(
586 std::FILE* f
, const S
& fmt
,
587 basic_format_args
<basic_printf_context_t
<type_identity_t
<Char
>>> args
)
589 basic_memory_buffer
<Char
> buffer
;
590 vprintf(buffer
, detail::to_string_view(fmt
), args
);
591 size_t size
= buffer
.size();
592 return std::fwrite(buffer
.data(), sizeof(Char
), size
, f
) < size
594 : static_cast<int>(size
);
599 Prints formatted data to the file *f*.
603 fmt::fprintf(stderr, "Don't %s!", "panic");
606 template <typename S
, typename
... T
, typename Char
= char_t
<S
>>
607 inline auto fprintf(std::FILE* f
, const S
& fmt
, const T
&... args
) -> int {
608 using context
= basic_printf_context_t
<Char
>;
609 return vfprintf(f
, detail::to_string_view(fmt
),
610 fmt::make_format_args
<context
>(args
...));
613 template <typename S
, typename Char
= char_t
<S
>>
616 basic_format_args
<basic_printf_context_t
<type_identity_t
<Char
>>> args
)
618 return vfprintf(stdout
, detail::to_string_view(fmt
), args
);
623 Prints formatted data to ``stdout``.
627 fmt::printf("Elapsed time: %.2f seconds", 1.23);
630 template <typename S
, typename
... T
, FMT_ENABLE_IF(detail::is_string
<S
>::value
)>
631 inline auto printf(const S
& fmt
, const T
&... args
) -> int {
633 detail::to_string_view(fmt
),
634 fmt::make_format_args
<basic_printf_context_t
<char_t
<S
>>>(args
...));
637 FMT_MODULE_EXPORT_END
640 #endif // FMT_PRINTF_H_