diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 32d07f6c..2cf74e0b 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -197,9 +197,7 @@ using internal::printf; // For printing into memory_buffer. template class printf_arg_formatter; -template >>> +template class basic_printf_context; /** @@ -212,12 +210,12 @@ class printf_arg_formatter : public internal::function< typename internal::arg_formatter_base::iterator>, public internal::arg_formatter_base { + public: + typedef decltype(internal::declval().begin()) iterator; private: typedef typename Range::value_type char_type; - typedef decltype(internal::declval().begin()) iterator; typedef internal::arg_formatter_base base; - typedef basic_printf_context - context_type; + typedef basic_printf_context context_type; context_type& context_; @@ -328,7 +326,7 @@ template struct printf_formatter { }; /** This template formats data and writes the output to a writer. */ -template +template class basic_printf_context { public: /** The character type for the output. */ @@ -379,11 +377,12 @@ class basic_printf_context { } /** Formats stored arguments and writes the output to the range. */ + template>>> OutputIt format(); }; -template -void basic_printf_context::parse_flags(format_specs& spec, +template +void basic_printf_context::parse_flags(format_specs& spec, const Char*& it, const Char* end) { for (; it != end; ++it) { @@ -409,9 +408,9 @@ void basic_printf_context::parse_flags(format_specs& spec, } } -template -typename basic_printf_context::format_arg -basic_printf_context::get_arg(unsigned arg_index) { +template +typename basic_printf_context::format_arg +basic_printf_context::get_arg(unsigned arg_index) { if (arg_index == std::numeric_limits::max()) arg_index = parse_ctx_.next_arg_id(); else @@ -419,8 +418,8 @@ basic_printf_context::get_arg(unsigned arg_index) { return internal::get_arg(*this, arg_index); } -template -unsigned basic_printf_context::parse_header( +template +unsigned basic_printf_context::parse_header( const Char*& it, const Char* end, format_specs& spec) { unsigned arg_index = std::numeric_limits::max(); char_type c = *it; @@ -457,8 +456,9 @@ unsigned basic_printf_context::parse_header( return arg_index; } -template -OutputIt basic_printf_context::format() { +template +template +OutputIt basic_printf_context::format() { auto out = this->out(); const Char* start = parse_ctx_.begin(); const Char* end = parse_ctx_.end(); @@ -567,7 +567,7 @@ OutputIt basic_printf_context::format() { start = it; // Format argument. - visit_format_arg(AF(out, spec, *this), arg); + visit_format_arg(ArgFormatter(out, spec, *this), arg); } return std::copy(start, it, out); } @@ -712,6 +712,19 @@ inline int vfprintf( return static_cast(buffer.size()); } +/** Formats arguments and writes the output to the range. */ +template > +typename ArgFormatter::iterator vprintf(internal::buffer& out, + basic_string_view format_str, + basic_format_args args) { + typename ArgFormatter::iterator iter(out); + Context(iter, format_str, args).template format(); + return iter; +} + + /** \rst Prints formatted data to the stream *os*. diff --git a/test/printf-test.cc b/test/printf-test.cc index d669fdd1..9e479780 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -554,3 +554,51 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) { fmt::make_wprintf_args(42, L"something"))); #endif } + +typedef fmt::printf_arg_formatter< + fmt::back_insert_range>> formatter_t; +typedef fmt::basic_printf_context context_t; + +// A custom printf argument formatter that doesn't print `-` for floating-point +// values rounded to 0. +class custom_printf_arg_formatter : public formatter_t { + public: + using formatter_t::iterator; + + custom_printf_arg_formatter(formatter_t::iterator iter, formatter_t::format_specs& spec, context_t& ctx) + : formatter_t(iter, spec, ctx) {} + + using formatter_t::operator(); + +#if FMT_MSC_VER > 0 && FMT_MSC_VER <= 1804 + template ::value)> + iterator operator()(T value) { +#else + iterator operator()(double value) { +#endif + // Comparing a float to 0.0 is safe. + if (round(value * pow(10, spec()->precision)) == 0.0) value = 0; + return formatter_t::operator()(value); + } +}; + +typedef fmt::basic_format_args format_args_t; + +std::string custom_vformat(fmt::string_view format_str, format_args_t args) { + fmt::memory_buffer buffer; + fmt::vprintf(buffer, format_str, args); + return std::string(buffer.data(), buffer.size()); +} + +template +std::string custom_format(const char* format_str, const Args&... args) { + auto va = fmt::make_printf_args (args...); + return custom_vformat(format_str, va); +} + +TEST(CustomFormatterTest, Format) { + EXPECT_EQ("0.00", custom_format("%.2f", -.00001)); + EXPECT_EQ("0.00", custom_format("%.2f", .00001)); + EXPECT_EQ("1.00", custom_format("%.2f", 1.00001)); + EXPECT_EQ("-1.00", custom_format("%.2f", -1.00001)); +}