diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 65771c09..cae32442 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -17,11 +17,60 @@ FMT_BEGIN_NAMESPACE FMT_MODULE_EXPORT_BEGIN +template struct printf_formatter { printf_formatter() = delete; }; + template class basic_printf_parse_context : public basic_format_parse_context { using basic_format_parse_context::basic_format_parse_context; }; -template class basic_printf_context; + +template class basic_printf_context { + public: + /** The character type for the output. */ + using char_type = Char; + using iterator = OutputIt; + using format_arg = basic_format_arg; + using parse_context_type = basic_printf_parse_context; + template using formatter_type = printf_formatter; + + private: + using format_specs = basic_format_specs; + + OutputIt out_; + basic_format_args args_; + parse_context_type parse_ctx_; + + static void parse_flags(format_specs& specs, const Char*& it, + const Char* end); + + public: + /** + \rst + Constructs a ``printf_context`` object. References to the arguments are + stored in the context object so make sure they have appropriate lifetimes. + \endrst + */ + basic_printf_context(OutputIt out, basic_string_view format_str, + basic_format_args args) + : out_(out), args_(args), parse_ctx_(format_str) {} + + OutputIt out() { return out_; } + void advance_to(OutputIt it) { out_ = it; } + + detail::locale_ref locale() { return {}; } + + format_arg arg(int id) const { return args_.get(id); } + + // Returns the argument with specified index or, if arg_index is -1, the next + // argument. + format_arg get_arg(int arg_index = -1); + + parse_context_type& parse_context() { return parse_ctx_; } + + FMT_CONSTEXPR void on_error(const char* message) { + parse_ctx_.on_error(message); + } +}; FMT_BEGIN_DETAIL_NAMESPACE @@ -262,88 +311,9 @@ class printf_arg_formatter : public arg_formatter { } }; -template -void vprintf(buffer& buf, basic_string_view format, - basic_format_args args) { - Context(buffer_appender(buf), format, args).format(); -} -FMT_END_DETAIL_NAMESPACE - -template struct printf_formatter { - printf_formatter() = delete; - - template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } - - template - auto format(const T&, FormatContext& ctx) -> decltype(ctx.out()) { - return ctx.out(); - } -}; - -/** - This template formats data and writes the output through an output iterator. - */ -template class basic_printf_context { - public: - /** The character type for the output. */ - using char_type = Char; - using iterator = OutputIt; - using format_arg = basic_format_arg; - using parse_context_type = basic_printf_parse_context; - template using formatter_type = printf_formatter; - - private: - using format_specs = basic_format_specs; - - OutputIt out_; - basic_format_args args_; - parse_context_type parse_ctx_; - - static void parse_flags(format_specs& specs, const Char*& it, - const Char* end); - - // Returns the argument with specified index or, if arg_index is -1, the next - // argument. - format_arg get_arg(int arg_index = -1); - - // Parses argument index, flags and width and returns the argument index. - int parse_header(const Char*& it, const Char* end, format_specs& specs); - - public: - /** - \rst - Constructs a ``printf_context`` object. References to the arguments are - stored in the context object so make sure they have appropriate lifetimes. - \endrst - */ - basic_printf_context(OutputIt out, basic_string_view format_str, - basic_format_args args) - : out_(out), args_(args), parse_ctx_(format_str) {} - - OutputIt out() { return out_; } - void advance_to(OutputIt it) { out_ = it; } - - detail::locale_ref locale() { return {}; } - - format_arg arg(int id) const { return args_.get(id); } - - parse_context_type& parse_context() { return parse_ctx_; } - - FMT_CONSTEXPR void on_error(const char* message) { - parse_ctx_.on_error(message); - } - - /** Formats stored arguments and writes the output to the range. */ - OutputIt format(); -}; - -template -void basic_printf_context::parse_flags(format_specs& specs, - const Char*& it, - const Char* end) { +template +void parse_flags(basic_format_specs& specs, const Char*& it, + const Char* end) { for (; it != end; ++it) { switch (*it) { case '-': @@ -369,22 +339,11 @@ void basic_printf_context::parse_flags(format_specs& specs, } } -template -typename basic_printf_context::format_arg -basic_printf_context::get_arg(int arg_index) { - if (arg_index < 0) - arg_index = parse_ctx_.next_arg_id(); - else - parse_ctx_.check_arg_id(--arg_index); - return detail::get_arg(*this, arg_index); -} - -template -int basic_printf_context::parse_header(const Char*& it, - const Char* end, - format_specs& specs) { +template +int parse_header(const Char*& it, const Char* end, + basic_format_specs& specs, GetArg get_arg) { int arg_index = -1; - char_type c = *it; + Char c = *it; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). @@ -412,24 +371,28 @@ int basic_printf_context::parse_header(const Char*& it, } else if (*it == '*') { ++it; specs.width = static_cast(visit_format_arg( - detail::printf_width_handler(specs), get_arg())); + detail::printf_width_handler(specs), get_arg())); } } return arg_index; } -template -OutputIt basic_printf_context::format() { - auto out = this->out(); - const Char* start = parse_ctx_.begin(); - const Char* end = parse_ctx_.end(); +template +void vprintf(buffer& buf, basic_string_view format, + basic_format_args args) { + using OutputIt = buffer_appender; + auto out = OutputIt(buf); + auto context = basic_printf_context(out, format, args); + auto& parse_ctx = context.parse_context(); + const Char* start = parse_ctx.begin(); + const Char* end = parse_ctx.end(); auto it = start; while (it != end) { if (!detail::find(it, end, '%', it)) { it = end; // detail::find leaves it == nullptr if it doesn't find '%' break; } - char_type c = *it++; + Char c = *it++; if (it != end && *it == c) { out = detail::write( out, basic_string_view(start, detail::to_unsigned(it - start))); @@ -439,12 +402,13 @@ OutputIt basic_printf_context::format() { out = detail::write(out, basic_string_view( start, detail::to_unsigned(it - 1 - start))); - format_specs specs; + basic_format_specs specs; specs.align = align::right; // Parse argument index, flags and width. - int arg_index = parse_header(it, end, specs); - if (arg_index == 0) on_error("argument not found"); + int arg_index = + parse_header(it, end, specs, [&]() { return context.get_arg(); }); + if (arg_index == 0) parse_ctx.on_error("argument not found"); // Parse precision. if (it != end && *it == '.') { @@ -455,14 +419,14 @@ OutputIt basic_printf_context::format() { specs.precision = parse_nonnegative_int(it, end, eh); } else if (c == '*') { ++it; - specs.precision = static_cast( - visit_format_arg(detail::printf_precision_handler(), get_arg())); + specs.precision = static_cast(visit_format_arg( + detail::printf_precision_handler(), context.get_arg())); } else { specs.precision = 0; } } - format_arg arg = get_arg(arg_index); + auto arg = context.get_arg(arg_index); // For d, i, o, u, x, and X conversion specifiers, if a precision is // specified, the '0' flag is ignored if (specs.precision >= 0 && arg.is_integral()) @@ -472,9 +436,10 @@ OutputIt basic_printf_context::format() { auto str = visit_format_arg(detail::get_cstring(), arg); auto str_end = str + specs.precision; auto nul = std::find(str, str_end, Char()); - arg = detail::make_arg(basic_string_view( - str, - detail::to_unsigned(nul != str_end ? nul - str : specs.precision))); + arg = detail::make_arg>( + basic_string_view( + str, detail::to_unsigned(nul != str_end ? nul - str + : specs.precision))); } if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) specs.alt = false; @@ -488,7 +453,7 @@ OutputIt basic_printf_context::format() { // Parse length and convert the argument to the required type. c = it != end ? *it++ : 0; - char_type t = it != end ? *it : 0; + Char t = it != end ? *it : 0; using detail::convert_arg; switch (c) { case 'h': @@ -538,8 +503,9 @@ OutputIt basic_printf_context::format() { specs.type = 'd'; break; case 'c': - visit_format_arg(detail::char_converter(arg), - arg); + visit_format_arg( + detail::char_converter>(arg), + arg); break; } } @@ -548,10 +514,20 @@ OutputIt basic_printf_context::format() { // Format argument. out = visit_format_arg( - detail::printf_arg_formatter(out, specs, *this), arg); + detail::printf_arg_formatter(out, specs, context), arg); } - return detail::write( - out, basic_string_view(start, detail::to_unsigned(it - start))); + detail::write(out, basic_string_view(start, to_unsigned(it - start))); +} +FMT_END_DETAIL_NAMESPACE + +template +typename basic_printf_context::format_arg +basic_printf_context::get_arg(int arg_index) { + if (arg_index < 0) + arg_index = parse_ctx_.next_arg_id(); + else + parse_ctx_.check_arg_id(--arg_index); + return detail::get_arg(*this, arg_index); } template @@ -668,7 +644,7 @@ inline auto printf(const S& fmt, const T&... args) -> int { } template > -inline auto vfprintf( +FMT_DEPRECATED auto vfprintf( std::basic_ostream& os, const S& fmt, basic_format_args>> args) -> int { @@ -677,19 +653,9 @@ inline auto vfprintf( os.write(buffer.data(), static_cast(buffer.size())); return static_cast(buffer.size()); } - -/** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - fmt::fprintf(cerr, "Don't %s!", "panic"); - \endrst - */ template > -inline auto fprintf(std::basic_ostream& os, const S& fmt, - const T&... args) -> int { +FMT_DEPRECATED auto fprintf(std::basic_ostream& os, const S& fmt, + const T&... args) -> int { return vfprintf(os, to_string_view(fmt), fmt::make_format_args>(args...)); } diff --git a/test/printf-test.cc b/test/printf-test.cc index 5aacd37f..5aaa306e 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -571,20 +571,12 @@ TEST(printf_test, printf_custom) { EXPECT_EQ("abc", test_sprintf("%s", test_string("abc"))); } -TEST(printf_test, ostream) { - std::ostringstream os; - int ret = fmt::fprintf(os, "Don't %s!", "panic"); - EXPECT_EQ("Don't panic!", os.str()); - EXPECT_EQ(12, ret); -} - TEST(printf_test, vprintf) { fmt::format_arg_store as{42}; fmt::basic_format_args args(as); EXPECT_EQ(fmt::vsprintf("%d", args), "42"); EXPECT_WRITE(stdout, fmt::vprintf("%d", args), "42"); EXPECT_WRITE(stdout, fmt::vfprintf(stdout, "%d", args), "42"); - EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42"); } template