Skip to content

Commit

Permalink
Simplify argument formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Mar 21, 2021
1 parent 0f85a46 commit 9cb347b
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 114 deletions.
11 changes: 6 additions & 5 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,9 @@ void format_arg(
visit_format_arg(custom_formatter<Context>(parse_ctx, ctx), arg);
} else {
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));
}
}

Expand Down Expand Up @@ -368,10 +370,9 @@ auto vformat_to(OutputIt out, CompiledFormat& cf,
if (specs.precision >= 0) checker.check_precision();

advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, &specs),
arg));
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, specs),
arg));
break;
}
}
Expand Down
71 changes: 26 additions & 45 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1485,11 +1485,10 @@ FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) {
}

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) {
if (!specs) return handler.on_char();
if (specs->type && specs->type != 'c') return handler.on_int();
if (specs->align == align::numeric || specs->sign != sign::none || specs->alt)
if (specs.type && specs.type != 'c') return handler.on_int();
if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
handler.on_error("invalid format specifier for char");
handler.on_char();
}
Expand Down Expand Up @@ -2203,8 +2202,8 @@ class arg_formatter_base {

private:
iterator out_;
const format_specs& specs_;
locale_ref locale_;
format_specs* specs_;

// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
Expand Down Expand Up @@ -2249,7 +2248,7 @@ class arg_formatter_base {
}

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 {
Expand All @@ -2263,13 +2262,10 @@ class arg_formatter_base {
// char is only formatted as int if there are specs.
formatter.out_ =
detail::write_int(formatter.out_, static_cast<int>(value),
*formatter.specs_, formatter.locale_);
formatter.specs_, formatter.locale_);
}
FMT_CONSTEXPR void on_char() {
if (formatter.specs_)
formatter.out_ = write_char(formatter.out_, value, *formatter.specs_);
else
formatter.write(value);
formatter.out_ = write_char(formatter.out_, value, formatter.specs_);
}
};

Expand All @@ -2286,28 +2282,22 @@ class arg_formatter_base {

protected:
iterator out() { return out_; }
format_specs* specs() { return specs_; }
const format_specs& specs() { return specs_; }

FMT_CONSTEXPR void write(bool value) {
if (specs_)
write(string_view(value ? "true" : "false"), *specs_);
else
out_ = detail::write<Char>(out_, value);
write(string_view(value ? "true" : "false"), specs_);
}

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"));
} 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:
constexpr arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc)
: out_(out), locale_(loc), specs_(s) {}
constexpr arg_formatter_base(OutputIt out, const format_specs& s, locale_ref loc)
: out_(out), specs_(s), locale_(loc) {}

iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type");
Expand All @@ -2316,8 +2306,7 @@ class arg_formatter_base {

template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) {
return out_ = specs_ ? detail::write_int(out_, value, *specs_, locale_)
: detail::write<Char>(out_, value);
return out_ = detail::write_int(out_, value, specs_, locale_);
}

FMT_CONSTEXPR iterator operator()(Char value) {
Expand All @@ -2327,40 +2316,33 @@ class arg_formatter_base {
}

FMT_CONSTEXPR iterator operator()(bool value) {
if (specs_ && specs_->type && specs_->type != 's')
return (*this)(value ? 1 : 0);
if (specs_.type && specs_.type != 's') return (*this)(value ? 1 : 0);
write(value != 0);
return out_;
}

template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
auto specs = specs_ ? *specs_ : format_specs();
if (const_check(is_supported_floating_point(value)))
out_ = detail::write(out_, value, specs, locale_);
out_ = detail::write(out_, value, specs_, locale_);
else
FMT_ASSERT(false, "unsupported float argument type");
return out_;
}

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_;
}

FMT_CONSTEXPR iterator operator()(basic_string_view<Char> value) {
if (specs_) {
check_string_type_spec(specs_->type, error_handler());
write(value, *specs_);
} else {
write(value);
}
check_string_type_spec(specs_.type, error_handler());
write(value, specs_);
return out_;
}

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);
return out_;
}
Expand All @@ -2387,8 +2369,7 @@ class arg_formatter : public arg_formatter_base<OutputIt, Char> {
*specs* contains format specifier information for standard argument types.
\endrst
*/
constexpr explicit arg_formatter(context_type& ctx,
format_specs* specs = nullptr)
constexpr explicit arg_formatter(context_type& ctx, const format_specs& specs)
: base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {}

using base::operator();
Expand Down Expand Up @@ -3167,7 +3148,7 @@ struct format_handler : detail::error_handler {
begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string");
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;
}
};
Expand Down Expand Up @@ -3496,7 +3477,7 @@ struct formatter<T, Char,
break;
case detail::type::char_type:
handle_char_specs(
&specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
break;
case detail::type::float_type:
if (detail::const_check(FMT_USE_FLOAT))
Expand Down Expand Up @@ -3544,7 +3525,7 @@ struct formatter<T, Char,
specs.precision, specs.precision_ref, ctx);
using af = detail::arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(af(ctx, &specs),
return visit_format_arg(af(ctx, specs),
detail::make_arg<FormatContext>(val));
}

Expand Down Expand Up @@ -3647,7 +3628,7 @@ template <typename Char = char> class dynamic_formatter {
if (specs_.precision >= 0) checker.end_precision();
using af = detail::arg_formatter<typename FormatContext::iterator,
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();
}

Expand Down
4 changes: 2 additions & 2 deletions include/fmt/locale.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ inline OutputIt vformat_to(

template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) ->
inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
Expand Down
89 changes: 27 additions & 62 deletions include/fmt/printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,50 +207,32 @@ template <typename OutputIt, typename Char> class basic_printf_context;
*/
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = OutputIt;

private:
using char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = typename base::format_specs;

context_type& context_;

void write_null_pointer(char) {
this->specs()->type = 0;
this->write("(nil)");
}

void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
OutputIt write_null_pointer(bool is_string = false) {
auto s = this->specs();
s.type = 0;
return detail::write(this->out(),
string_view(is_string ? "(null)" : "(nil)"), s);
}

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) {}

/**
\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) {}
OutputIt operator()(monostate value) { return base::operator()(value); }

template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
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();
OutputIt operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (std::is_same<T, Char>::value) {
format_specs fmt_specs = this->specs();
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
Expand All @@ -260,56 +242,39 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
return write_char(this->out(), static_cast<Char>(value), fmt_specs);
}
return this->out();
return base::operator()(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);
}

/** Formats a null-terminated C string. */
iterator operator()(const char* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
OutputIt operator()(const char* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs().type != 'p');
}

/** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
OutputIt operator()(const wchar_t* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs().type != 'p');
}

iterator operator()(basic_string_view<char_type> value) {
OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value);
}

iterator operator()(monostate value) { return base::operator()(value); }

/** Formats a pointer. */
iterator operator()(const void* value) {
if (value) return base::operator()(value);
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
OutputIt operator()(const void* value) {
return value ? base::operator()(value) : write_null_pointer();
}

/** 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_);
return this->out();
}
Expand Down

0 comments on commit 9cb347b

Please sign in to comment.