21 template <
typename Char>
class basic_printf_context {
23 basic_appender<Char> out_;
26 static_assert(std::is_same<Char, char>::value ||
27 std::is_same<Char, wchar_t>::value,
28 "Unsupported code unit type.");
31 using char_type = Char;
32 enum { builtin_types = 1 };
36 basic_printf_context(basic_appender<Char> out,
38 : out_(out), args_(args) {}
40 auto out() -> basic_appender<Char> {
return out_; }
41 void advance_to(basic_appender<Char>) {}
43 auto locale() -> locale_ref {
return {}; }
45 auto arg(
int id)
const -> basic_format_arg<basic_printf_context> {
53 template <
bool IS_CONSTEXPR,
typename T,
typename Ptr = const T*>
54 FMT_CONSTEXPR
auto find(Ptr first, Ptr last, T value, Ptr& out) ->
bool {
55 for (out = first; out != last; ++out) {
56 if (*out == value)
return true;
62 inline auto find<false, char>(
const char* first,
const char* last,
char value,
63 const char*& out) ->
bool {
65 static_cast<const char*
>(memchr(first, value, to_unsigned(last - first)));
66 return out !=
nullptr;
71 template <
bool IS_SIGNED>
struct int_checker {
72 template <
typename T>
static auto fits_in_int(T value) ->
bool {
73 return value <= to_unsigned(max_value<int>());
75 inline static auto fits_in_int(
bool) ->
bool {
return true; }
78 template <>
struct int_checker<true> {
79 template <
typename T>
static auto fits_in_int(T value) ->
bool {
80 return value >= (std::numeric_limits<int>::min)() &&
81 value <= max_value<int>();
83 inline static auto fits_in_int(
int) ->
bool {
return true; }
86 struct printf_precision_handler {
87 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
88 auto operator()(T value) ->
int {
89 if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
90 report_error(
"number is too big");
91 return max_of(static_cast<int>(value), 0);
94 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
95 auto operator()(T) ->
int {
96 report_error(
"precision is not integer");
103 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
104 auto operator()(T value) ->
bool {
108 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
109 auto operator()(T) ->
bool {
114 template <
typename T>
struct make_unsigned_or_bool : std::make_unsigned<T> {};
116 template <>
struct make_unsigned_or_bool<bool> {
120 template <
typename T,
typename Context>
class arg_converter {
122 using char_type =
typename Context::char_type;
124 basic_format_arg<Context>& arg_;
128 arg_converter(basic_format_arg<Context>& arg, char_type type)
129 : arg_(arg), type_(type) {}
131 void operator()(
bool value) {
132 if (type_ !=
's') operator()<
bool>(value);
135 template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
136 void operator()(U value) {
137 bool is_signed = type_ ==
'd' || type_ ==
'i';
138 using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
139 if (const_check(
sizeof(target_type) <=
sizeof(int))) {
141 using unsigned_type =
typename make_unsigned_or_bool<target_type>::type;
143 arg_ =
static_cast<int>(
static_cast<target_type
>(value));
145 arg_ =
static_cast<unsigned>(
static_cast<unsigned_type
>(value));
151 arg_ =
static_cast<long long>(value);
153 arg_ =
static_cast<typename make_unsigned_or_bool<U>::type
>(value);
157 template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
158 void operator()(U) {}
165 template <
typename T,
typename Context,
typename Char>
166 void convert_arg(basic_format_arg<Context>& arg, Char type) {
167 arg.visit(arg_converter<T, Context>(arg, type));
171 template <
typename Context>
class char_converter {
173 basic_format_arg<Context>& arg_;
176 explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
178 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
179 void operator()(T value) {
180 arg_ =
static_cast<typename Context::char_type
>(value);
183 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
184 void operator()(T) {}
189 template <
typename Char>
struct get_cstring {
190 template <
typename T>
auto operator()(T) ->
const Char* {
return nullptr; }
191 auto operator()(
const Char* s) ->
const Char* {
return s; }
196 class printf_width_handler {
198 format_specs& specs_;
201 inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
203 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
204 auto operator()(T value) ->
unsigned {
205 auto width =
static_cast<uint32_or_64_or_128_t<T>
>(value);
206 if (detail::is_negative(value)) {
207 specs_.set_align(align::left);
210 unsigned int_max = to_unsigned(max_value<int>());
211 if (width > int_max) report_error(
"number is too big");
212 return static_cast<unsigned>(width);
215 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
216 auto operator()(T) ->
unsigned {
217 report_error(
"width is not integer");
224 template <
typename Char>
225 auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
226 -> arg_formatter<Char> {
227 return {iter, s, locale_ref()};
231 template <
typename Char>
232 class printf_arg_formatter :
public arg_formatter<Char> {
234 using base = arg_formatter<Char>;
235 using context_type = basic_printf_context<Char>;
237 context_type& context_;
239 void write_null_pointer(
bool is_string =
false) {
240 auto s = this->specs;
241 s.set_type(presentation_type::none);
242 write_bytes<Char>(this->out, is_string ?
"(null)" :
"(nil)", s);
245 template <
typename T>
void write(T value) {
246 detail::write<Char>(this->out, value, this->specs, this->locale);
250 printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
252 : base(make_arg_formatter(iter, s)), context_(ctx) {}
254 void operator()(monostate value) { write(value); }
256 template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
257 void operator()(T value) {
260 if (!std::is_same<T, Char>::value) {
264 format_specs s = this->specs;
265 if (s.type() != presentation_type::none &&
266 s.type() != presentation_type::chr) {
267 return (*
this)(
static_cast<int>(value));
269 s.set_sign(sign::none);
274 if (s.align() == align::none || s.align() == align::numeric)
275 s.set_align(align::right);
276 detail::write<Char>(this->out,
static_cast<Char
>(value), s);
279 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
280 void operator()(T value) {
284 void operator()(
const char* value) {
288 write_null_pointer(this->specs.type() != presentation_type::pointer);
291 void operator()(
const wchar_t* value) {
295 write_null_pointer(this->specs.type() != presentation_type::pointer);
300 void operator()(
const void* value) {
304 write_null_pointer();
307 void operator()(
typename basic_format_arg<context_type>::handle handle) {
309 handle.format(parse_ctx, context_);
313 template <
typename Char>
314 void parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
315 for (; it != end; ++it) {
317 case '-': specs.set_align(align::left);
break;
318 case '+': specs.set_sign(sign::plus);
break;
319 case '0': specs.set_fill(
'0');
break;
321 if (specs.sign() != sign::plus) specs.set_sign(sign::space);
323 case '#': specs.set_alt();
break;
329 template <
typename Char,
typename GetArg>
330 auto 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.set_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)));
367 inline 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;
372 case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
373 case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
374 case 'X': upper =
true; FMT_FALLTHROUGH;
375 case 'x':
return in(t, integral_set) ? pt::hex : pt::none;
376 case 'E': upper =
true; FMT_FALLTHROUGH;
377 case 'e':
return in(t, float_set) ? pt::exp : pt::none;
378 case 'F': upper =
true; FMT_FALLTHROUGH;
379 case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
380 case 'G': upper =
true; FMT_FALLTHROUGH;
381 case 'g':
return in(t, float_set) ? pt::general : pt::none;
382 case 'A': upper =
true; FMT_FALLTHROUGH;
383 case 'a':
return in(t, float_set) ? pt::hexfloat : pt::none;
384 case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
385 case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
386 case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
387 default:
return pt::none;
391 template <
typename Char,
typename Context>
394 using iterator = basic_appender<Char>;
395 auto out = iterator(buf);
396 auto context = basic_printf_context<Char>(out, args);
401 auto get_arg = [&](
int arg_index) {
403 arg_index = parse_ctx.next_arg_id();
405 parse_ctx.check_arg_id(--arg_index);
406 auto arg = context.arg(arg_index);
407 if (!arg) report_error(
"argument not found");
411 const Char* start = parse_ctx.begin();
412 const Char* end = parse_ctx.end();
415 if (!find<false, Char>(it, end,
'%', it)) {
420 if (it != end && *it == c) {
427 auto specs = format_specs();
428 specs.set_align(align::right);
431 int arg_index = parse_header(it, end, specs, get_arg);
432 if (arg_index == 0) report_error(
"argument not found");
435 if (it != end && *it ==
'.') {
437 c = it != end ? *it : 0;
438 if (
'0' <= c && c <=
'9') {
439 specs.precision = parse_nonnegative_int(it, end, 0);
440 }
else if (c ==
'*') {
443 static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
449 auto arg = get_arg(arg_index);
452 if (specs.precision >= 0 && is_integral_type(arg.type())) {
456 if (specs.precision >= 0 && arg.type() == type::cstring_type) {
457 auto str = arg.visit(get_cstring<Char>());
458 auto str_end = str + specs.precision;
459 auto nul = std::find(str, str_end, Char());
461 str, to_unsigned(nul != str_end ? nul - str : specs.precision));
464 if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
465 if (specs.fill_unit<Char>() ==
'0') {
466 if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
467 specs.set_align(align::numeric);
475 c = it != end ? *it++ : 0;
476 Char t = it != end ? *it : 0;
481 t = it != end ? *it : 0;
482 convert_arg<signed char>(arg, t);
484 convert_arg<short>(arg, t);
490 t = it != end ? *it : 0;
491 convert_arg<long long>(arg, t);
493 convert_arg<long>(arg, t);
496 case 'j': convert_arg<intmax_t>(arg, t);
break;
497 case 'z': convert_arg<size_t>(arg, t);
break;
498 case 't': convert_arg<std::ptrdiff_t>(arg, t);
break;
503 default: --it; convert_arg<void>(arg, c);
507 if (it == end) report_error(
"invalid format string");
508 char type =
static_cast<char>(*it++);
509 if (is_integral_type(arg.type())) {
513 case 'u': type =
'd';
break;
515 arg.visit(char_converter<basic_printf_context<Char>>(arg));
520 specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
521 if (specs.type() == presentation_type::none)
522 report_error(
"invalid format specifier");
523 if (upper) specs.set_upper();
528 arg.visit(printf_arg_formatter<Char>(out, specs, context));
534 using printf_context = basic_printf_context<char>;
535 using wprintf_context = basic_printf_context<wchar_t>;
542 template <
typename Char = char,
typename... T>
543 inline auto make_printf_args(T&... args)
544 -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
545 return fmt::make_format_args<basic_printf_context<Char>>(args...);
548 template <
typename Char>
struct vprintf_args {
552 template <
typename Char>
555 -> std::basic_string<Char> {
557 detail::vprintf(buf, fmt, args);
558 return {buf.data(), buf.size()};
569 template <
typename... T>
570 inline auto sprintf(
string_view fmt,
const T&... args) -> std::string {
571 return vsprintf(fmt, make_printf_args(args...));
573 template <
typename... T>
576 return vsprintf(fmt, make_printf_args<wchar_t>(args...));
579 template <
typename Char>
583 detail::vprintf(buf, fmt, args);
584 size_t size = buf.size();
585 return std::fwrite(buf.data(),
sizeof(Char), size, f) < size
587 :
static_cast<int>(size);
598 template <
typename... T>
599 inline auto fprintf(std::FILE* f,
string_view fmt,
const T&... args) ->
int {
600 return vfprintf(f, fmt, make_printf_args(args...));
602 template <
typename... T>
604 const T&... args) ->
int {
605 return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
616 template <
typename... T>
617 inline auto printf(
string_view fmt,
const T&... args) ->
int {
618 return vfprintf(stdout, fmt, make_printf_args(args...));
624 #endif // FMT_PRINTF_H_ A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:785
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:623
Converts a string literal into a format string that will be parsed at compile time and converted into...
Definition: args.h:20
An implementation of std::basic_string_view for pre-C++17.
Definition: base.h:515