From 936a1833c25d1808c1536eed2c92f9842daa1855 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 8 Jun 2020 07:23:18 -0700 Subject: [PATCH] Add default_arg_formatter --- include/fmt/format.h | 155 +++++++++++++++++++++++++++++++----------- include/fmt/ostream.h | 4 +- include/fmt/printf.h | 2 +- test/ostream-test.cc | 4 +- 4 files changed, 121 insertions(+), 44 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 79f39b8d..76c4fcea 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1435,7 +1435,7 @@ OutputIt write_int(OutputIt out, int num_digits, string_view prefix, template OutputIt write(OutputIt out, basic_string_view s, - const basic_format_specs& specs = {}) { + const basic_format_specs& specs) { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) @@ -1671,13 +1671,100 @@ template struct is_integral : std::is_integral {}; template <> struct is_integral : std::true_type {}; template <> struct is_integral : std::true_type {}; +template +OutputIt write(OutputIt out, monostate) { + FMT_ASSERT(false, ""); + return out; +} + +template ::value)> +OutputIt write(OutputIt out, string_view value) { + auto it = reserve(out, value.size()); + it = copy_str(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, basic_string_view value) { + auto it = reserve(out, value.size()); + it = std::copy(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +OutputIt write(OutputIt out, T value) { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto it = reserve(out, (negative ? 1 : 0) + static_cast(num_digits)); + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits); + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, bool value) { + return write(out, string_view(value ? "true" : "false")); +} + +template +OutputIt write(OutputIt out, Char value) { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, const Char* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); + } else { + auto length = std::char_traits::length(value); + out = write(out, basic_string_view(value, length)); + } + return out; +} + +template +OutputIt write(OutputIt out, const void* value) { + return write_ptr(out, to_uintptr(value), nullptr); +} + +// An argument visitor that formats the argument and writes it via the output +// iterator. It's a class and not a generic lambda for compatibility with C++11. +template struct default_arg_formatter { + using context = basic_format_context; + + OutputIt out; + basic_format_args args; + locale_ref loc; + + template OutputIt operator()(T value) { + return write(out, value); + } + + OutputIt operator()(typename basic_format_arg::handle handle) { + Char s[] = {Char('}')}; + basic_format_parse_context parse_ctx(basic_string_view(s, 1)); + basic_format_context format_ctx(out, args, loc); + handle.format(parse_ctx, format_ctx); + return format_ctx.out(); + } +}; + template class arg_formatter_base { public: using iterator = OutputIt; using char_type = Char; - using format_specs = basic_format_specs; + using format_specs = basic_format_specs; private: iterator out_; @@ -1693,21 +1780,9 @@ class arg_formatter_base { using reserve_iterator = remove_reference_t(), 0))>; - // Writes a decimal integer. - template void write_decimal(Int value) { - auto abs_value = static_cast>(value); - bool negative = is_negative(value); - // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); - auto&& it = reserve((negative ? 1 : 0) + static_cast(num_digits)); - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits); - } - template void write_int(T value, const format_specs& spec) { using uint_type = uint32_or_64_or_128_t; - int_writer w(out_, locale_, value, spec); + int_writer w(out_, locale_, value, spec); handle_int_type_spec(spec.type, w); out_ = w.out; } @@ -1717,18 +1792,17 @@ class arg_formatter_base { *it++ = value; } - template ::value)> + template ::value)> void write(Ch value) { - auto&& it = reserve(1); - *it++ = value; + out_ = detail::write(out_, value); } void write(string_view value) { auto&& it = reserve(value.size()); - it = copy_str(value.begin(), value.end(), it); + it = copy_str(value.begin(), value.end(), it); } void write(wstring_view value) { - static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); auto&& it = reserve(value.size()); it = std::copy(value.begin(), value.end(), it); } @@ -1739,7 +1813,7 @@ class arg_formatter_base { ? count_code_points(basic_string_view(s, size)) : 0; out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { - return copy_str(s, s + size, it); + return copy_str(s, s + size, it); }); } @@ -1754,9 +1828,9 @@ class arg_formatter_base { struct char_spec_handler : ErrorHandler { arg_formatter_base& formatter; - char_type value; + Char value; - char_spec_handler(arg_formatter_base& f, char_type val) + char_spec_handler(arg_formatter_base& f, Char val) : formatter(f), value(val) {} void on_int() { @@ -1773,9 +1847,9 @@ class arg_formatter_base { struct cstring_spec_handler : error_handler { arg_formatter_base& formatter; - const char_type* value; + const Char* value; - cstring_spec_handler(arg_formatter_base& f, const char_type* val) + cstring_spec_handler(arg_formatter_base& f, const Char* val) : formatter(f), value(val) {} void on_string() { formatter.write(value); } @@ -1787,11 +1861,13 @@ class arg_formatter_base { format_specs* specs() { return specs_; } void write(bool value) { - string_view sv(value ? "true" : "false"); - specs_ ? write(sv, *specs_) : write(sv); + if (specs_) + write(string_view(value ? "true" : "false"), *specs_); + else + out_ = detail::write(out_, value); } - void write(const char_type* value) { + void write(const Char* value) { if (!value) { FMT_THROW(format_error("string pointer is null")); } else { @@ -1815,13 +1891,13 @@ class arg_formatter_base { if (specs_) write_int(value, *specs_); else - write_decimal(value); + out_ = detail::write(out_, value); return out_; } - iterator operator()(char_type value) { + iterator operator()(Char value) { handle_char_specs(specs_, - char_spec_handler(*this, static_cast(value))); + char_spec_handler(*this, static_cast(value))); return out_; } @@ -1841,13 +1917,13 @@ class arg_formatter_base { return out_; } - iterator operator()(const char_type* value) { + iterator operator()(const Char* value) { if (!specs_) return write(value), out_; handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); return out_; } - iterator operator()(basic_string_view value) { + iterator operator()(basic_string_view value) { if (specs_) { check_string_type_spec(specs_->type, error_handler()); write(value, *specs_); @@ -3125,14 +3201,15 @@ typename Context::iterator vformat_to( typename ArgFormatter::iterator out, basic_string_view format_str, basic_format_args args, detail::locale_ref loc = detail::locale_ref()) { + if (format_str.size() == 2 && detail::equal2(format_str.data(), "{}")) { + auto arg = args.get(0); + if (!arg) detail::error_handler().on_error("argument not found"); + using it = typename ArgFormatter::iterator; + return visit_format_arg( + detail::default_arg_formatter{out, args, loc}, arg); + } detail::format_handler h(out, format_str, args, loc); - if (format_str.size() == 2 && detail::equal2(format_str.data(), "{}")) { - auto arg = detail::get_arg(h.context, 0); - return visit_format_arg( - ArgFormatter(h.context, &h.parse_context, nullptr, &format_str[1]), - arg); - } detail::parse_format_string(format_str, h); return h.context.out(); } diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 72ba1da0..c16107f7 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -80,7 +80,7 @@ template class is_streamable { // Write the content of buf to os. template -void write(std::basic_ostream& os, buffer& buf) { +void write_buffer(std::basic_ostream& os, buffer& buf) { const Char* buf_data = buf.data(); using unsigned_streamsize = std::make_unsigned::type; unsigned_streamsize size = buf.size(); @@ -144,7 +144,7 @@ void vprint(std::basic_ostream& os, basic_string_view format_str, basic_format_args>> args) { basic_memory_buffer buffer; detail::vformat_to(buffer, format_str, args); - detail::write(os, buffer); + detail::write_buffer(os, buffer); } /** diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 1fbc9953..d4440ed1 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -714,7 +714,7 @@ inline int vfprintf( basic_format_args>> args) { basic_memory_buffer buffer; vprintf(buffer, to_string_view(format), args); - detail::write(os, buffer); + detail::write_buffer(os, buffer); return static_cast(buffer.size()); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index c4e8a27f..1e9d1896 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -141,7 +141,7 @@ TEST(OStreamTest, WriteToOStream) { fmt::memory_buffer buffer; const char* foo = "foo"; buffer.append(foo, foo + std::strlen(foo)); - fmt::detail::write(os, buffer); + fmt::detail::write_buffer(os, buffer); EXPECT_EQ("foo", os.str()); } @@ -178,7 +178,7 @@ TEST(OStreamTest, WriteToOStreamMaxSize) { data += n; size -= n; } while (size != 0); - fmt::detail::write(os, buffer); + fmt::detail::write_buffer(os, buffer); } TEST(OStreamTest, Join) {