Simplify argument formatters

This commit is contained in:
Victor Zverovich
2021-03-21 07:28:46 -07:00
parent 0f85a4683a
commit 9cb347b4b2
4 changed files with 61 additions and 114 deletions

View File

@ -307,7 +307,9 @@ void format_arg(
visit_format_arg(custom_formatter<Context>(parse_ctx, ctx), arg); visit_format_arg(custom_formatter<Context>(parse_ctx, ctx), arg);
} else { } else {
ctx.advance_to(visit_format_arg( ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx), arg)); default_arg_formatter<OutputIt, typename Context::char_type>{
ctx.out(), ctx.args(), ctx.locale()},
arg));
} }
} }
@ -368,10 +370,9 @@ auto vformat_to(OutputIt out, CompiledFormat& cf,
if (specs.precision >= 0) checker.check_precision(); if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to( ctx.advance_to(visit_format_arg(
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>( arg_formatter<OutputIt, typename Context::char_type>(ctx, specs),
ctx, &specs), arg));
arg));
break; break;
} }
} }

View File

@ -1485,11 +1485,10 @@ FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) {
} }
template <typename Char, typename Handler> template <typename Char, typename Handler>
FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>* specs, FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>& specs,
Handler&& handler) { Handler&& handler) {
if (!specs) return handler.on_char(); if (specs.type && specs.type != 'c') return handler.on_int();
if (specs->type && specs->type != 'c') return handler.on_int(); if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
if (specs->align == align::numeric || specs->sign != sign::none || specs->alt)
handler.on_error("invalid format specifier for char"); handler.on_error("invalid format specifier for char");
handler.on_char(); handler.on_char();
} }
@ -2203,8 +2202,8 @@ class arg_formatter_base {
private: private:
iterator out_; iterator out_;
const format_specs& specs_;
locale_ref locale_; locale_ref locale_;
format_specs* specs_;
// Attempts to reserve space for n extra characters in the output range. // Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_. // Returns a pointer to the reserved range or a reference to out_.
@ -2249,7 +2248,7 @@ class arg_formatter_base {
} }
void write_pointer(const void* p) { void write_pointer(const void* p) {
out_ = write_ptr<char_type>(out_, to_uintptr(p), specs_); out_ = write_ptr<char_type>(out_, to_uintptr(p), &specs_);
} }
struct char_spec_handler : ErrorHandler { struct char_spec_handler : ErrorHandler {
@ -2263,13 +2262,10 @@ class arg_formatter_base {
// char is only formatted as int if there are specs. // char is only formatted as int if there are specs.
formatter.out_ = formatter.out_ =
detail::write_int(formatter.out_, static_cast<int>(value), detail::write_int(formatter.out_, static_cast<int>(value),
*formatter.specs_, formatter.locale_); formatter.specs_, formatter.locale_);
} }
FMT_CONSTEXPR void on_char() { FMT_CONSTEXPR void on_char() {
if (formatter.specs_) formatter.out_ = write_char(formatter.out_, value, formatter.specs_);
formatter.out_ = write_char(formatter.out_, value, *formatter.specs_);
else
formatter.write(value);
} }
}; };
@ -2286,28 +2282,22 @@ class arg_formatter_base {
protected: protected:
iterator out() { return out_; } iterator out() { return out_; }
format_specs* specs() { return specs_; } const format_specs& specs() { return specs_; }
FMT_CONSTEXPR void write(bool value) { FMT_CONSTEXPR void write(bool value) {
if (specs_) write(string_view(value ? "true" : "false"), specs_);
write(string_view(value ? "true" : "false"), *specs_);
else
out_ = detail::write<Char>(out_, value);
} }
void write(const Char* value) { void write(const Char* value) {
if (!value) { if (value)
write(basic_string_view<char_type>(value), specs_);
else
FMT_THROW(format_error("string pointer is null")); FMT_THROW(format_error("string pointer is null"));
} else {
auto length = std::char_traits<char_type>::length(value);
basic_string_view<char_type> sv(value, length);
specs_ ? write(sv, *specs_) : write(sv);
}
} }
public: public:
constexpr arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) constexpr arg_formatter_base(OutputIt out, const format_specs& s, locale_ref loc)
: out_(out), locale_(loc), specs_(s) {} : out_(out), specs_(s), locale_(loc) {}
iterator operator()(monostate) { iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type"); FMT_ASSERT(false, "invalid argument type");
@ -2316,8 +2306,7 @@ class arg_formatter_base {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) { FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) {
return out_ = specs_ ? detail::write_int(out_, value, *specs_, locale_) return out_ = detail::write_int(out_, value, specs_, locale_);
: detail::write<Char>(out_, value);
} }
FMT_CONSTEXPR iterator operator()(Char value) { FMT_CONSTEXPR iterator operator()(Char value) {
@ -2327,40 +2316,33 @@ class arg_formatter_base {
} }
FMT_CONSTEXPR iterator operator()(bool value) { FMT_CONSTEXPR iterator operator()(bool value) {
if (specs_ && specs_->type && specs_->type != 's') if (specs_.type && specs_.type != 's') return (*this)(value ? 1 : 0);
return (*this)(value ? 1 : 0);
write(value != 0); write(value != 0);
return out_; return out_;
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) { iterator operator()(T value) {
auto specs = specs_ ? *specs_ : format_specs();
if (const_check(is_supported_floating_point(value))) if (const_check(is_supported_floating_point(value)))
out_ = detail::write(out_, value, specs, locale_); out_ = detail::write(out_, value, specs_, locale_);
else else
FMT_ASSERT(false, "unsupported float argument type"); FMT_ASSERT(false, "unsupported float argument type");
return out_; return out_;
} }
iterator operator()(const Char* value) { iterator operator()(const Char* value) {
if (!specs_) return write(value), out_; handle_cstring_type_spec(specs_.type, cstring_spec_handler(*this, value));
handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value));
return out_; return out_;
} }
FMT_CONSTEXPR iterator operator()(basic_string_view<Char> value) { FMT_CONSTEXPR iterator operator()(basic_string_view<Char> value) {
if (specs_) { check_string_type_spec(specs_.type, error_handler());
check_string_type_spec(specs_->type, error_handler()); write(value, specs_);
write(value, *specs_);
} else {
write(value);
}
return out_; return out_;
} }
iterator operator()(const void* value) { iterator operator()(const void* value) {
if (specs_) check_pointer_type_spec(specs_->type, error_handler()); check_pointer_type_spec(specs_.type, error_handler());
write_pointer(value); write_pointer(value);
return out_; return out_;
} }
@ -2387,8 +2369,7 @@ class arg_formatter : public arg_formatter_base<OutputIt, Char> {
*specs* contains format specifier information for standard argument types. *specs* contains format specifier information for standard argument types.
\endrst \endrst
*/ */
constexpr explicit arg_formatter(context_type& ctx, constexpr explicit arg_formatter(context_type& ctx, const format_specs& specs)
format_specs* specs = nullptr)
: base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {} : base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {}
using base::operator(); using base::operator();
@ -3167,7 +3148,7 @@ struct format_handler : detail::error_handler {
begin = parse_format_specs(begin, end, handler); begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string"); if (begin == end || *begin != '}') on_error("missing '}' in format string");
context.advance_to( context.advance_to(
visit_format_arg(arg_formatter<OutputIt, Char>(context, &specs), arg)); visit_format_arg(arg_formatter<OutputIt, Char>(context, specs), arg));
return begin; return begin;
} }
}; };
@ -3496,7 +3477,7 @@ struct formatter<T, Char,
break; break;
case detail::type::char_type: case detail::type::char_type:
handle_char_specs( handle_char_specs(
&specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh)); specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
break; break;
case detail::type::float_type: case detail::type::float_type:
if (detail::const_check(FMT_USE_FLOAT)) if (detail::const_check(FMT_USE_FLOAT))
@ -3544,7 +3525,7 @@ struct formatter<T, Char,
specs.precision, specs.precision_ref, ctx); specs.precision, specs.precision_ref, ctx);
using af = detail::arg_formatter<typename FormatContext::iterator, using af = detail::arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>; typename FormatContext::char_type>;
return visit_format_arg(af(ctx, &specs), return visit_format_arg(af(ctx, specs),
detail::make_arg<FormatContext>(val)); detail::make_arg<FormatContext>(val));
} }
@ -3647,7 +3628,7 @@ template <typename Char = char> class dynamic_formatter {
if (specs_.precision >= 0) checker.end_precision(); if (specs_.precision >= 0) checker.end_precision();
using af = detail::arg_formatter<typename FormatContext::iterator, using af = detail::arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>; typename FormatContext::char_type>;
visit_format_arg(af(ctx, &specs_), detail::make_arg<FormatContext>(val)); visit_format_arg(af(ctx, specs_), detail::make_arg<FormatContext>(val));
return ctx.out(); return ctx.out();
} }

View File

@ -52,8 +52,8 @@ inline OutputIt vformat_to(
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value> bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc, inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str,
const S& format_str, Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs); return vformat_to(out, loc, to_string_view(format_str), vargs);

View File

@ -207,50 +207,32 @@ template <typename OutputIt, typename Char> class basic_printf_context;
*/ */
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = OutputIt;
private: private:
using char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>; using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = typename base::format_specs;
context_type& context_; context_type& context_;
void write_null_pointer(char) { OutputIt write_null_pointer(bool is_string = false) {
this->specs()->type = 0; auto s = this->specs();
this->write("(nil)"); s.type = 0;
} return detail::write(this->out(),
string_view(is_string ? "(null)" : "(nil)"), s);
void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
} }
public: public:
using format_specs = typename base::format_specs; printf_arg_formatter(OutputIt iter, format_specs& specs, context_type& ctx)
: base(iter, specs, detail::locale_ref()), context_(ctx) {}
/** OutputIt operator()(monostate value) { return base::operator()(value); }
\rst
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *specs* contains format
specifier information for standard argument types.
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) { OutputIt operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and Char so use
// use std::is_same instead. // std::is_same instead.
if (std::is_same<T, bool>::value) { if (std::is_same<T, Char>::value) {
format_specs& fmt_specs = *this->specs(); format_specs fmt_specs = this->specs();
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c') if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none; fmt_specs.sign = sign::none;
@ -260,56 +242,39 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
// ignored for non-numeric types // ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right; fmt_specs.align = align::right;
return base::operator()(value); return write_char(this->out(), static_cast<Char>(value), fmt_specs);
} else {
return base::operator()(value);
} }
return this->out(); return base::operator()(value);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) { OutputIt operator()(T value) {
return base::operator()(value); return base::operator()(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
iterator operator()(const char* value) { OutputIt operator()(const char* value) {
if (value) if (value) return base::operator()(value);
base::operator()(value); return write_null_pointer(this->specs().type != 'p');
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) { OutputIt operator()(const wchar_t* value) {
if (value) if (value) return base::operator()(value);
base::operator()(value); return write_null_pointer(this->specs().type != 'p');
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
} }
iterator operator()(basic_string_view<char_type> value) { OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value); return base::operator()(value);
} }
iterator operator()(monostate value) { return base::operator()(value); }
/** Formats a pointer. */ /** Formats a pointer. */
iterator operator()(const void* value) { OutputIt operator()(const void* value) {
if (value) return base::operator()(value); return value ? base::operator()(value) : write_null_pointer();
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
} }
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) { OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_); handle.format(context_.parse_context(), context_);
return this->out(); return this->out();
} }