21template <
typename T>
struct printf_formatter {
22 printf_formatter() =
delete;
30 static_assert(std::is_same<Char, char>::value ||
31 std::is_same<Char, wchar_t>::value,
32 "Unsupported code unit type.");
35 using char_type = Char;
43 : out_(out), args_(args) {}
46 void advance_to(basic_appender<Char>) {}
48 auto locale() -> detail::locale_ref {
return {}; }
50 auto arg(
int id)
const -> basic_format_arg<basic_printf_context> {
60 template <
typename T>
static auto fits_in_int(T
value) ->
bool {
61 unsigned max = to_unsigned(max_value<int>());
64 static auto fits_in_int(
bool) ->
bool {
return true; }
68 template <
typename T>
static auto fits_in_int(T
value) ->
bool {
69 return value >= (std::numeric_limits<int>::min)() &&
70 value <= max_value<int>();
72 static auto fits_in_int(
int) ->
bool {
return true; }
76 template <
typename T, FMT_ENABLE_IF(std::is_
integral<T>::value)>
77 auto operator()(T
value) ->
int {
79 report_error(
"number is too big");
80 return (std::max)(
static_cast<int>(
value), 0);
83 template <
typename T, FMT_ENABLE_IF(!std::is_
integral<T>::value)>
84 auto operator()(T) ->
int {
85 report_error(
"precision is not integer");
92 template <
typename T, FMT_ENABLE_IF(std::is_
integral<T>::value)>
93 auto operator()(T
value) ->
bool {
97 template <
typename T, FMT_ENABLE_IF(!std::is_
integral<T>::value)>
98 auto operator()(T) ->
bool {
109template <
typename T,
typename Context>
class arg_converter {
111 using char_type =
typename Context::char_type;
118 : arg_(arg), type_(type) {}
120 void operator()(
bool value) {
121 if (type_ !=
's') operator()<
bool>(
value);
124 template <
typename U, FMT_ENABLE_IF(std::is_
integral<U>::value)>
125 void operator()(U
value) {
126 bool is_signed = type_ ==
'd' || type_ ==
'i';
127 using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
128 if (const_check(
sizeof(target_type) <=
sizeof(
int))) {
131 auto n =
static_cast<int>(
static_cast<target_type
>(
value));
132 arg_ = detail::make_arg<Context>(n);
135 auto n =
static_cast<unsigned>(
static_cast<unsigned_type
>(
value));
136 arg_ = detail::make_arg<Context>(n);
143 auto n =
static_cast<long long>(
value);
144 arg_ = detail::make_arg<Context>(n);
147 arg_ = detail::make_arg<Context>(n);
152 template <
typename U, FMT_ENABLE_IF(!std::is_
integral<U>::value)>
153 void operator()(U) {}
160template <
typename T,
typename Context,
typename Char>
166template <
typename Context>
class char_converter {
173 template <
typename T, FMT_ENABLE_IF(std::is_
integral<T>::value)>
174 void operator()(T
value) {
175 auto c =
static_cast<typename Context::char_type
>(
value);
176 arg_ = detail::make_arg<Context>(c);
179 template <
typename T, FMT_ENABLE_IF(!std::is_
integral<T>::value)>
180 void operator()(T) {}
186 template <
typename T>
auto operator()(T) ->
const Char* {
return nullptr; }
187 auto operator()(
const Char* s) ->
const Char* {
return s; }
192class printf_width_handler {
197 explicit printf_width_handler(
format_specs& specs) : specs_(specs) {}
199 template <
typename T, FMT_ENABLE_IF(std::is_
integral<T>::value)>
200 auto operator()(T
value) ->
unsigned {
201 auto width =
static_cast<uint32_or_64_or_128_t<T>
>(
value);
202 if (detail::is_negative(
value)) {
203 specs_.align = align::left;
206 unsigned int_max = to_unsigned(max_value<int>());
207 if (width > int_max) report_error(
"number is too big");
208 return static_cast<unsigned>(width);
211 template <
typename T, FMT_ENABLE_IF(!std::is_
integral<T>::value)>
212 auto operator()(T) ->
unsigned {
213 report_error(
"width is not integer");
220template <
typename Char>
227template <
typename Char>
233 context_type& context_;
235 void write_null_pointer(
bool is_string =
false) {
236 auto s = this->specs;
237 s.type = presentation_type::none;
238 write_bytes<Char>(this->out, is_string ?
"(null)" :
"(nil)", s);
244 : base(make_arg_formatter(iter, s)), context_(ctx) {}
248 template <
typename T, FMT_ENABLE_IF(detail::is_
integral<T>::value)>
249 void operator()(T
value) {
252 if (!std::is_same<T, Char>::value) {
253 base::operator()(
value);
257 if (s.type != presentation_type::none && s.type != presentation_type::chr) {
258 return (*
this)(
static_cast<int>(
value));
265 if (s.align == align::none || s.align == align::numeric)
266 s.align = align::right;
267 write<Char>(this->out,
static_cast<Char
>(
value), s);
270 template <
typename T, FMT_ENABLE_IF(std::is_
floating_po
int<T>::value)>
271 void operator()(T
value) {
272 base::operator()(
value);
275 void operator()(
const char*
value) {
277 base::operator()(
value);
279 write_null_pointer(this->specs.type != presentation_type::pointer);
282 void operator()(
const wchar_t*
value) {
284 base::operator()(
value);
286 write_null_pointer(this->specs.type != presentation_type::pointer);
291 void operator()(
const void*
value) {
293 base::operator()(
value);
295 write_null_pointer();
300 handle.format(parse_ctx, context_);
304template <
typename Char>
305void parse_flags(
format_specs& specs,
const Char*& it,
const Char* end) {
306 for (; it != end; ++it) {
309 specs.align = align::left;
312 specs.sign = sign::plus;
318 if (specs.sign != sign::plus) specs.sign = sign::space;
329template <
typename Char,
typename GetArg>
330auto parse_header(
const Char*& it,
const Char* end, format_specs& specs,
331 GetArg get_arg) ->
int {
334 if (c >=
'0' && c <=
'9') {
337 int value = parse_nonnegative_int(it, end, -1);
338 if (it != end && *it ==
'$') {
340 arg_index =
value != -1 ?
value : max_value<int>();
342 if (c ==
'0') specs.fill =
'0';
346 if (
value == -1) report_error(
"number is too big");
352 parse_flags(specs, it, end);
355 if (*it >=
'0' && *it <=
'9') {
356 specs.width = parse_nonnegative_int(it, end, -1);
357 if (specs.width == -1) report_error(
"number is too big");
358 }
else if (*it ==
'*') {
360 specs.width =
static_cast<int>(
361 get_arg(-1).visit(detail::printf_width_handler(specs)));
367inline auto parse_printf_presentation_type(
char c, type t,
bool& upper)
368 -> presentation_type {
369 using pt = presentation_type;
370 constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
373 return in(t, integral_set) ? pt::dec : pt::none;
375 return in(t, integral_set) ? pt::oct : pt::none;
380 return in(t, integral_set) ? pt::hex : pt::none;
385 return in(t, float_set) ? pt::exp : pt::none;
390 return in(t, float_set) ? pt::fixed : pt::none;
395 return in(t, float_set) ? pt::general : pt::none;
400 return in(t, float_set) ? pt::hexfloat : pt::none;
402 return in(t, integral_set) ? pt::chr : pt::none;
404 return in(t, string_set | cstring_set) ? pt::string : pt::none;
406 return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
412template <
typename Char,
typename Context>
413void vprintf(
buffer<Char>& buf, basic_string_view<Char> format,
414 basic_format_args<Context> args) {
415 using iterator = basic_appender<Char>;
416 auto out = iterator(buf);
417 auto context = basic_printf_context<Char>(out, args);
418 auto parse_ctx = basic_format_parse_context<Char>(format);
422 auto get_arg = [&](
int arg_index) {
424 arg_index = parse_ctx.next_arg_id();
426 parse_ctx.check_arg_id(--arg_index);
427 return detail::get_arg(context, arg_index);
430 const Char* start = parse_ctx.begin();
431 const Char* end = parse_ctx.end();
434 if (!find<false, Char>(it, end,
'%', it)) {
439 if (it != end && *it == c) {
440 write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
444 write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
446 auto specs = format_specs();
447 specs.align = align::right;
450 int arg_index = parse_header(it, end, specs, get_arg);
451 if (arg_index == 0) report_error(
"argument not found");
454 if (it != end && *it ==
'.') {
456 c = it != end ? *it : 0;
457 if (
'0' <= c && c <=
'9') {
458 specs.precision = parse_nonnegative_int(it, end, 0);
459 }
else if (c ==
'*') {
468 auto arg = get_arg(arg_index);
471 if (specs.precision >= 0 && arg.is_integral()) {
475 if (specs.precision >= 0 && arg.type() == type::cstring_type) {
477 auto str_end = str + specs.precision;
478 auto nul = std::find(str, str_end, Char());
479 auto sv = basic_string_view<Char>(
480 str, to_unsigned(nul != str_end ? nul - str : specs.precision));
481 arg = make_arg<basic_printf_context<Char>>(sv);
483 if (specs.alt && arg.visit(
is_zero_int())) specs.alt =
false;
484 if (specs.fill.template get<Char>() ==
'0') {
485 if (arg.is_arithmetic() && specs.align != align::left)
486 specs.align = align::numeric;
493 c = it != end ? *it++ : 0;
494 Char t = it != end ? *it : 0;
499 t = it != end ? *it : 0;
500 convert_arg<signed char>(arg, t);
502 convert_arg<short>(arg, t);
508 t = it != end ? *it : 0;
509 convert_arg<long long>(arg, t);
511 convert_arg<long>(arg, t);
515 convert_arg<intmax_t>(arg, t);
518 convert_arg<size_t>(arg, t);
521 convert_arg<std::ptrdiff_t>(arg, t);
529 convert_arg<void>(arg, c);
533 if (it == end) report_error(
"invalid format string");
534 char type =
static_cast<char>(*it++);
535 if (arg.is_integral()) {
548 specs.type = parse_printf_presentation_type(type, arg.type(), upper);
549 if (specs.type == presentation_type::none)
550 report_error(
"invalid format specifier");
558 write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
570template <
typename Char = char,
typename... T>
571inline auto make_printf_args(T&... args)
572 ->
decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
573 return fmt::make_format_args<basic_printf_context<Char>>(args...);
580template <
typename Char>
582 typename vprintf_args<Char>::type args)
583 -> std::basic_string<Char> {
585 detail::vprintf(buf, fmt, args);
586 return to_string(buf);
597template <
typename S,
typename... T,
typename Char = char_t<S>>
598inline auto sprintf(
const S& fmt,
const T&... args) -> std::basic_string<Char> {
599 return vsprintf(detail::to_string_view(fmt),
603template <
typename Char>
605 typename vprintf_args<Char>::type args) ->
int {
607 detail::vprintf(buf, fmt, args);
608 size_t size = buf.size();
609 return std::fwrite(buf.data(),
sizeof(Char), size, f) < size
611 :
static_cast<int>(size);
622template <
typename S,
typename... T,
typename Char = char_t<S>>
623inline auto fprintf(std::FILE* f,
const S& fmt,
const T&... args) ->
int {
624 return vfprintf(f, detail::to_string_view(fmt),
625 make_printf_args<Char>(args...));
628template <
typename Char>
630 typename vprintf_args<Char>::type args)
632 return vfprintf(stdout, fmt, args);
643template <
typename... T>
644inline auto printf(string_view fmt,
const T&... args) ->
int {
645 return vfprintf(stdout, fmt, make_printf_args(args...));
647template <
typename... T>
649 const T&... args) ->
int {
650 return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
basic_printf_context(basic_appender< Char > out, basic_format_args< basic_printf_context > args)
Definition printf.h:41