8 #ifndef FMTQUILL_PRINTF_H_ 9 #define FMTQUILL_PRINTF_H_ 11 #ifndef FMTQUILL_MODULE 18 FMTQUILL_BEGIN_NAMESPACE
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;
38 enum { builtin_types = 1 };
44 : out_(out), args_(args) {}
59 template <
bool IS_CONSTEXPR,
typename T,
typename Ptr = const T*>
60 FMTQUILL_CONSTEXPR
auto find(Ptr first, Ptr last, T value, Ptr& out) ->
bool {
61 for (out = first; out != last; ++out) {
62 if (*out == value)
return true;
68 inline auto find<false, char>(
const char* first,
const char* last,
char value,
69 const char*& out) ->
bool {
71 static_cast<const char*
>(memchr(first, value, to_unsigned(last - first)));
72 return out !=
nullptr;
78 template <
typename T>
static auto fits_in_int(T value) ->
bool {
79 unsigned max = to_unsigned(max_value<int>());
82 inline static auto fits_in_int(
bool) ->
bool {
return true; }
86 template <
typename T>
static auto fits_in_int(T value) ->
bool {
87 return value >= (std::numeric_limits<int>::min)() &&
90 inline static auto fits_in_int(
int) ->
bool {
return true; }
94 template <typename T, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
95 auto operator()(T value) ->
int {
96 if (!
int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
97 report_error(
"number is too big");
98 return (std::max)(
static_cast<int>(value), 0);
101 template <typename T, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
102 auto operator()(T) ->
int {
103 report_error(
"precision is not integer");
110 template <typename T, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
111 auto operator()(T value) ->
bool {
115 template <typename T, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
116 auto operator()(T) ->
bool {
129 using char_type =
typename Context::char_type;
136 : arg_(arg), type_(type) {}
138 void operator()(
bool value) {
139 if (type_ !=
's') operator()<
bool>(value);
142 template <typename U, FMTQUILL_ENABLE_IF(std::is_integral<U>::value)>
143 void operator()(U value) {
144 bool is_signed = type_ ==
'd' || type_ ==
'i';
145 using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
146 if (const_check(
sizeof(target_type) <=
sizeof(int))) {
150 arg_ =
static_cast<int>(
static_cast<target_type
>(value));
152 arg_ =
static_cast<unsigned>(
static_cast<unsigned_type
>(value));
158 arg_ =
static_cast<long long>(value);
164 template <typename U, FMTQUILL_ENABLE_IF(!std::is_integral<U>::value)>
165 void operator()(U) {}
172 template <
typename T,
typename Context,
typename Char>
185 template <typename T, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
186 void operator()(T value) {
187 arg_ =
static_cast<typename Context::char_type
>(value);
190 template <typename T, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
191 void operator()(T) {}
197 template <
typename T>
auto operator()(T) ->
const Char* {
return nullptr; }
198 auto operator()(
const Char* s) ->
const Char* {
return s; }
210 template <typename T, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
211 auto operator()(T value) ->
unsigned {
212 auto width =
static_cast<uint32_or_64_or_128_t<T>
>(value);
213 if (detail::is_negative(value)) {
214 specs_.set_align(align::left);
217 unsigned int_max = to_unsigned(max_value<int>());
218 if (width > int_max) report_error(
"number is too big");
219 return static_cast<unsigned>(width);
222 template <typename T, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
223 auto operator()(T) ->
unsigned {
224 report_error(
"width is not integer");
231 template <
typename Char>
238 template <
typename Char>
246 void write_null_pointer(
bool is_string =
false) {
247 auto s = this->specs;
248 s.set_type(presentation_type::none);
249 write_bytes<Char>(this->out, is_string ?
"(null)" :
"(nil)", s);
252 template <
typename T>
void write(T value) {
253 detail::write<Char>(this->out, value, this->specs, this->locale);
259 :
base(make_arg_formatter(iter, s)), context_(ctx) {}
261 void operator()(
monostate value) { write(value); }
263 template <typename T, FMTQUILL_ENABLE_IF(detail::is_integral<T>::value)>
264 void operator()(T value) {
267 if (!std::is_same<T, Char>::value) {
272 if (s.type() != presentation_type::none &&
273 s.type() != presentation_type::chr) {
274 return (*
this)(
static_cast<int>(value));
276 s.set_sign(sign::none);
281 if (s.align() == align::none || s.align() == align::numeric)
282 s.set_align(align::right);
283 detail::write<Char>(this->out,
static_cast<Char
>(value), s);
286 template <typename T, FMTQUILL_ENABLE_IF(std::is_floating_point<T>::value)>
287 void operator()(T value) {
291 void operator()(
const char* value) {
295 write_null_pointer(this->specs.type() != presentation_type::pointer);
298 void operator()(
const wchar_t* value) {
302 write_null_pointer(this->specs.type() != presentation_type::pointer);
307 void operator()(
const void* value) {
311 write_null_pointer();
316 handle.format(parse_ctx, context_);
320 template <
typename Char>
321 void parse_flags(
format_specs& specs,
const Char*& it,
const Char* end) {
322 for (; it != end; ++it) {
324 case '-': specs.set_align(align::left);
break;
325 case '+': specs.set_sign(sign::plus);
break;
326 case '0': specs.set_fill(
'0');
break;
328 if (specs.sign() != sign::plus) specs.set_sign(sign::space);
330 case '#': specs.set_alt();
break;
336 template <
typename Char,
typename GetArg>
337 auto parse_header(
const Char*& it,
const Char* end,
format_specs& specs,
338 GetArg get_arg) ->
int {
341 if (c >=
'0' && c <=
'9') {
344 int value = parse_nonnegative_int(it, end, -1);
345 if (it != end && *it ==
'$') {
347 arg_index = value != -1 ? value : max_value<int>();
349 if (c ==
'0') specs.set_fill(
'0');
353 if (value == -1) report_error(
"number is too big");
359 parse_flags(specs, it, end);
362 if (*it >=
'0' && *it <=
'9') {
363 specs.width = parse_nonnegative_int(it, end, -1);
364 if (specs.width == -1) report_error(
"number is too big");
365 }
else if (*it ==
'*') {
367 specs.width =
static_cast<int>(
374 inline auto parse_printf_presentation_type(
char c, type t,
bool& upper)
375 -> presentation_type {
376 using pt = presentation_type;
377 constexpr
auto integral_set = sint_set | uint_set | bool_set | char_set;
379 case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
380 case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
381 case 'X': upper =
true; FMTQUILL_FALLTHROUGH;
382 case 'x':
return in(t, integral_set) ? pt::hex : pt::none;
383 case 'E': upper =
true; FMTQUILL_FALLTHROUGH;
384 case 'e':
return in(t, float_set) ? pt::exp : pt::none;
385 case 'F': upper =
true; FMTQUILL_FALLTHROUGH;
386 case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
387 case 'G': upper =
true; FMTQUILL_FALLTHROUGH;
388 case 'g':
return in(t, float_set) ? pt::general : pt::none;
389 case 'A': upper =
true; FMTQUILL_FALLTHROUGH;
390 case 'a':
return in(t, float_set) ? pt::hexfloat : pt::none;
391 case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
392 case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
393 case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
394 default:
return pt::none;
398 template <
typename Char,
typename Context>
402 auto out = iterator(buf);
408 auto get_arg = [&](
int arg_index) {
410 arg_index = parse_ctx.next_arg_id();
412 parse_ctx.check_arg_id(--arg_index);
413 return detail::get_arg(
context, arg_index);
416 const Char* start = parse_ctx.begin();
417 const Char* end = parse_ctx.end();
420 if (!find<false, Char>(it, end,
'%', it)) {
425 if (it != end && *it == c) {
433 specs.set_align(align::right);
436 int arg_index = parse_header(it, end, specs, get_arg);
437 if (arg_index == 0) report_error(
"argument not found");
440 if (it != end && *it ==
'.') {
442 c = it != end ? *it : 0;
443 if (
'0' <= c && c <=
'9') {
444 specs.precision = parse_nonnegative_int(it, end, 0);
445 }
else if (c ==
'*') {
454 auto arg = get_arg(arg_index);
457 if (specs.precision >= 0 && is_integral_type(arg.type())) {
461 if (specs.precision >= 0 && arg.type() == type::cstring_type) {
463 auto str_end = str + specs.precision;
464 auto nul = std::find(str, str_end, Char());
466 str, to_unsigned(nul != str_end ? nul - str : specs.precision));
470 if (specs.fill_unit<Char>() ==
'0') {
471 if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
472 specs.set_align(align::numeric);
480 c = it != end ? *it++ : 0;
481 Char t = it != end ? *it : 0;
486 t = it != end ? *it : 0;
487 convert_arg<signed char>(arg, t);
489 convert_arg<short>(arg, t);
495 t = it != end ? *it : 0;
496 convert_arg<long long>(arg, t);
498 convert_arg<long>(arg, t);
501 case 'j': convert_arg<intmax_t>(arg, t);
break;
502 case 'z': convert_arg<size_t>(arg, t);
break;
503 case 't': convert_arg<std::ptrdiff_t>(arg, t);
break;
508 default: --it; convert_arg<void>(arg, c);
512 if (it == end) report_error(
"invalid format string");
513 char type =
static_cast<char>(*it++);
514 if (is_integral_type(arg.type())) {
518 case 'u': type =
'd';
break;
525 specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
526 if (specs.type() == presentation_type::none)
527 report_error(
"invalid format specifier");
528 if (upper) specs.set_upper();
547 template <
typename Char = char,
typename... T>
548 inline auto make_printf_args(T&... args)
550 return fmtquill::make_format_args<basic_printf_context<Char>>(args...);
557 template <
typename Char>
560 -> std::basic_string<Char> {
562 detail::vprintf(buf, fmt, args);
563 return {buf.data(), buf.size()};
575 inline auto sprintf(
const S& fmt,
const T&... args) -> std::basic_string<Char> {
576 return vsprintf(detail::to_string_view(fmt),
580 template <
typename Char>
584 detail::vprintf(buf, fmt, args);
585 size_t size = buf.size();
586 return std::fwrite(buf.data(),
sizeof(Char), size, f) < size
588 :
static_cast<int>(size);
600 inline auto fprintf(std::FILE* f,
const S& fmt,
const T&... args) ->
int {
601 return vfprintf(f, detail::to_string_view(fmt),
602 make_printf_args<Char>(args...));
605 template <
typename Char>
609 return vfprintf(stdout, fmt, args);
620 template <
typename... T>
621 inline auto printf(
string_view fmt,
const T&... args) ->
int {
622 return vfprintf(stdout, fmt, make_printf_args(args...));
624 template <
typename... T>
626 const T&... args) ->
int {
627 return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
631 FMTQUILL_END_NAMESPACE
633 #endif // FMTQUILL_PRINTF_H_
A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:790
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:644
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:523
typename V::value_type char_t
String's character (code unit) type. detail:: is intentional to prevent ADL.
Definition: base.h:961
basic_printf_context(basic_appender< Char > out, basic_format_args< basic_printf_context > args)
Constructs a printf_context object.
Definition: printf.h:42