From e99809f29da1002e8b9246e9278084ad231174e5 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 12 Apr 2020 07:38:54 -0700 Subject: [PATCH] Fix ostream support in sprintf (#1631) --- include/fmt/core.h | 12 +++++++----- include/fmt/ostream.h | 33 ++++++++++++++++++++++++++++----- include/fmt/printf.h | 9 +++++++-- test/core-test.cc | 3 ++- test/format-test.cc | 5 +++-- test/printf-test.cc | 4 +--- 6 files changed, 48 insertions(+), 18 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index cab3dfe0b..1a75100ad 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -827,7 +827,8 @@ template struct string_value { template struct custom_value { using parse_context = basic_format_parse_context; const void* value; - void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); + void (*format)(const void* arg, + typename Context::parse_context_type& parse_ctx, Context& ctx); }; // A formatting argument value. @@ -887,9 +888,9 @@ template class value { private: // Formats an argument of a custom type, such as a user-defined class. template - static void format_custom_arg( - const void* arg, basic_format_parse_context& parse_ctx, - Context& ctx) { + static void format_custom_arg(const void* arg, + typename Context::parse_context_type& parse_ctx, + Context& ctx) { Formatter f; parse_ctx.advance_to(f.parse(parse_ctx)); ctx.advance_to(f.format(*static_cast(arg), ctx)); @@ -1066,7 +1067,7 @@ template class basic_format_arg { public: explicit handle(internal::custom_value custom) : custom_(custom) {} - void format(basic_format_parse_context& parse_ctx, + void format(typename Context::parse_context_type& parse_ctx, Context& ctx) const { custom_.format(custom_.value, parse_ctx, ctx); } @@ -1277,6 +1278,7 @@ template class basic_format_context { public: using iterator = OutputIt; using format_arg = basic_format_arg; + using parse_context_type = basic_format_parse_context; template using formatter_type = formatter; basic_format_context(const basic_format_context&) = delete; diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index c4831533d..4526f5238 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -9,9 +9,14 @@ #define FMT_OSTREAM_H_ #include + #include "format.h" FMT_BEGIN_NAMESPACE + +template class basic_printf_parse_context; +template class basic_printf_context; + namespace internal { template class formatbuf : public std::basic_streambuf { @@ -93,9 +98,9 @@ void format_value(buffer& buf, const T& value, locale_ref loc = locale_ref()) { formatbuf format_buf(buf); std::basic_ostream output(&format_buf); - #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) if (loc) output.imbue(loc.get()); - #endif +#endif output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output << value; buf.resize(buf.size()); @@ -104,14 +109,32 @@ void format_value(buffer& buf, const T& value, // Formats an object of type T that has an overloaded ostream operator<<. template struct fallback_formatter::value>> - : formatter, Char> { - template - auto format(const T& value, Context& ctx) -> decltype(ctx.out()) { + : private formatter, Char> { + auto parse(basic_format_parse_context& ctx) -> decltype(ctx.begin()) { + return formatter, Char>::parse(ctx); + } + template >::value)> + auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const T& value, basic_format_context& ctx) + -> OutputIt { basic_memory_buffer buffer; format_value(buffer, value, ctx.locale()); basic_string_view str(buffer.data(), buffer.size()); return formatter, Char>::format(str, ctx); } + template + auto format(const T& value, basic_printf_context& ctx) + -> OutputIt { + basic_memory_buffer buffer; + format_value(buffer, value, ctx.locale()); + return std::copy(buffer.begin(), buffer.end(), ctx.out()); + } }; } // namespace internal diff --git a/include/fmt/printf.h b/include/fmt/printf.h index bfbaa0479..9c7b85ac5 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -189,6 +189,10 @@ using internal::vprintf; template class printf_arg_formatter; +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; /** @@ -324,6 +328,7 @@ template class basic_printf_context { 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: @@ -331,7 +336,7 @@ template class basic_printf_context { OutputIt out_; basic_format_args args_; - basic_format_parse_context parse_ctx_; + parse_context_type parse_ctx_; static void parse_flags(format_specs& specs, const Char*& it, const Char* end); @@ -362,7 +367,7 @@ template class basic_printf_context { format_arg arg(int id) const { return args_.get(id); } - basic_format_parse_context& parse_context() { return parse_ctx_; } + parse_context_type& parse_context() { return parse_ctx_; } FMT_CONSTEXPR void on_error(const char* message) { parse_ctx_.on_error(message); diff --git a/test/core-test.cc b/test/core-test.cc index f19d0423b..735fcde26 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -210,7 +210,8 @@ TEST(ArgTest, FormatArgs) { } struct custom_context { - typedef char char_type; + using char_type = char; + using parse_context_type = fmt::format_parse_context; template struct formatter_type { template diff --git a/test/format-test.cc b/test/format-test.cc index 6a873185c..e2bd98d27 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2260,8 +2260,9 @@ struct test_parse_context { }; struct test_context { - typedef char char_type; - typedef fmt::basic_format_arg format_arg; + using char_type = char; + using format_arg = fmt::basic_format_arg; + using parse_context_type = fmt::format_parse_context; template struct formatter_type { typedef fmt::formatter type; diff --git a/test/printf-test.cc b/test/printf-test.cc index 545e02aab..70b1238da 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -508,9 +508,7 @@ TEST(PrintfTest, PrintfError) { TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); } TEST(PrintfTest, PrintfCustom) { - // The test is disabled for now because it requires decoupling - // fallback_formatter::format from format_context. - //EXPECT_EQ("abc", test_sprintf("%s", TestString("abc"))); + EXPECT_EQ("abc", test_sprintf("%s", TestString("abc"))); } TEST(PrintfTest, OStream) {