8 #ifndef FMTQUILL_PRINTF_H_ 9 #define FMTQUILL_PRINTF_H_ 11 #ifndef FMTQUILL_MODULE 18 FMTQUILL_BEGIN_NAMESPACE
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 };
38 : out_(out), args_(args) {}
53 template <
bool IS_CONSTEXPR,
typename T,
typename Ptr = const T*>
54 FMTQUILL_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;
72 template <
typename T>
static auto fits_in_int(T value) ->
bool {
75 inline static auto fits_in_int(
bool) ->
bool {
return true; }
79 template <
typename T>
static auto fits_in_int(T value) ->
bool {
80 return value >= (std::numeric_limits<int>::min)() &&
83 inline static auto fits_in_int(
int) ->
bool {
return true; }
87 template <typename T, FMTQUILL_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, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
95 auto operator()(T) ->
int {
96 report_error(
"precision is not integer");
103 template <typename T, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
104 auto operator()(T value) ->
bool {
108 template <typename T, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
109 auto operator()(T) ->
bool {
122 using char_type =
typename Context::char_type;
129 : arg_(arg), type_(type) {}
131 void operator()(
bool value) {
132 if (type_ !=
's') operator()<
bool>(value);
135 template <typename U, FMTQUILL_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))) {
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);
157 template <typename U, FMTQUILL_ENABLE_IF(!std::is_integral<U>::value)>
158 void operator()(U) {}
165 template <
typename T,
typename Context,
typename Char>
178 template <typename T, FMTQUILL_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, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
184 void operator()(T) {}
190 template <
typename T>
auto operator()(T) ->
const Char* {
return nullptr; }
191 auto operator()(
const Char* s) ->
const Char* {
return s; }
203 template <typename T, FMTQUILL_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, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
216 auto operator()(T) ->
unsigned {
217 report_error(
"width is not integer");
224 template <
typename Char>
231 template <
typename Char>
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);
252 :
base(make_arg_formatter(iter, s)), context_(ctx) {}
254 void operator()(
monostate value) { write(value); }
256 template <typename T, FMTQUILL_ENABLE_IF(detail::is_integral<T>::value)>
257 void operator()(T value) {
260 if (!std::is_same<T, Char>::value) {
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, FMTQUILL_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();
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>(
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; FMTQUILL_FALLTHROUGH;
375 case 'x':
return in(t, integral_set) ? pt::hex : pt::none;
376 case 'E': upper =
true; FMTQUILL_FALLTHROUGH;
377 case 'e':
return in(t, float_set) ? pt::exp : pt::none;
378 case 'F': upper =
true; FMTQUILL_FALLTHROUGH;
379 case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
380 case 'G': upper =
true; FMTQUILL_FALLTHROUGH;
381 case 'g':
return in(t, float_set) ? pt::general : pt::none;
382 case 'A': upper =
true; FMTQUILL_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>
395 auto out = iterator(buf);
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) {
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 ==
'*') {
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) {
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));
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;
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();
542 template <
typename Char = char,
typename... T>
543 inline auto make_printf_args(T&... args)
545 return fmtquill::make_format_args<basic_printf_context<Char>>(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...));
622 FMTQUILL_END_NAMESPACE
624 #endif // FMTQUILL_PRINTF_H_
A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:809
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:634
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
An implementation of std::basic_string_view for pre-C++17.
Definition: base.h:526
basic_printf_context(basic_appender< Char > out, basic_format_args< basic_printf_context > args)
Constructs a printf_context object.
Definition: printf.h:36