diff --git a/include/fmt/format.h b/include/fmt/format.h index 036921a7..5005c279 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1385,7 +1385,22 @@ class basic_arg { friend class basic_args; friend class internal::arg_map; + using char_type = typename Context::char_type; + public: + class handle { + public: + explicit handle(internal::custom_value custom) + : custom_(custom) {} + + void format(basic_buffer &buf, Context &ctx) { + custom_.format(buf, custom_.value, &ctx); + } + + private: + internal::custom_value custom_; + }; + constexpr basic_arg() : type_(internal::NONE) {} explicit operator bool() const noexcept { return type_ != internal::NONE; } @@ -1407,7 +1422,7 @@ class basic_arg { template constexpr typename std::result_of::type visit(Visitor &&vis, basic_arg arg) { - typedef typename Context::char_type Char; + using char_type = typename Context::char_type; switch (arg.type_) { case internal::NONE: return vis(monostate()); @@ -1425,7 +1440,7 @@ constexpr typename std::result_of::type case internal::BOOL: return vis(arg.value_.int_value != 0); case internal::CHAR: - return vis(static_cast(arg.value_.int_value)); + return vis(static_cast(arg.value_.int_value)); case internal::DOUBLE: return vis(arg.value_.double_value); case internal::LONG_DOUBLE: @@ -1433,12 +1448,12 @@ constexpr typename std::result_of::type case internal::CSTRING: return vis(arg.value_.string.value); case internal::STRING: - return vis(basic_string_view( + return vis(basic_string_view( arg.value_.string.value, arg.value_.string.size)); case internal::POINTER: return vis(arg.value_.pointer); case internal::CUSTOM: - return vis(arg.value_.custom); + return vis(typename basic_arg::handle(arg.value_.custom)); } return typename std::result_of::type(); } @@ -2161,8 +2176,8 @@ class custom_formatter { custom_formatter(basic_buffer &buffer, Context &ctx) : buffer_(buffer), ctx_(ctx) {} - bool operator()(internal::custom_value custom) { - custom.format(buffer_, custom.value, &ctx_); + bool operator()(typename basic_arg::handle h) { + h.format(buffer_, ctx_); return true; } @@ -2795,8 +2810,8 @@ class arg_formatter : public internal::arg_formatter_base { using internal::arg_formatter_base::operator(); /** Formats an argument of a custom (user-defined) type. */ - void operator()(internal::custom_value c) { - c.format(this->writer().buffer(), c.value, &ctx_); + void operator()(typename basic_arg>::handle handle) { + handle.format(this->writer().buffer(), ctx_); } }; diff --git a/include/fmt/printf.h b/include/fmt/printf.h index e6e76296..e43e9404 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -200,6 +200,13 @@ class PrintfWidthHandler { }; } // namespace internal +template +class printf_arg_formatter; + +template > +class printf_context; + /** \rst The ``printf`` argument formatter. @@ -208,6 +215,8 @@ class PrintfWidthHandler { template class printf_arg_formatter : public internal::arg_formatter_base { private: + printf_context& context_; + void write_null_pointer() { this->spec().type_ = 0; this->write("(nil)"); @@ -225,8 +234,9 @@ class printf_arg_formatter : public internal::arg_formatter_base { specifier information for standard argument types. \endrst */ - printf_arg_formatter(basic_buffer &buffer, format_specs &spec) - : internal::arg_formatter_base(buffer, spec) {} + printf_arg_formatter( + basic_buffer &buffer, format_specs &spec, printf_context &ctx) + : internal::arg_formatter_base(buffer, spec), context_(ctx) {} using Base::operator(); @@ -268,18 +278,11 @@ class printf_arg_formatter : public internal::arg_formatter_base { } /** Formats an argument of a custom (user-defined) type. */ - void operator()(internal::custom_value c) { - const Char format_str[] = {'}', '\0'}; - auto args = basic_args>(); - basic_context ctx(basic_string_view(format_str), args); - c.format(this->writer().buffer(), c.value, &ctx); + void operator()(typename basic_arg>::handle handle) { + handle.format(this->writer().buffer(), context_); } }; -template > -class printf_context; - template struct printf_formatter { template @@ -506,7 +509,7 @@ void printf_context::format(basic_buffer &buffer) { start = it; // Format argument. - visit(AF(buffer, spec), arg); + visit(AF(buffer, spec, *this), arg); } buffer.append(pointer_from(start), pointer_from(it)); } diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index e189fa4d..129d26e1 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -29,22 +29,6 @@ class CustomArgFormatter : public fmt::arg_formatter { } }; -// A custom argument formatter that doesn't print `-` for floating-point values -// rounded to 0. -class CustomPrintfArgFormatter : public printf_arg_formatter { - public: - CustomPrintfArgFormatter(fmt::buffer &buf, fmt::format_specs &spec) - : printf_arg_formatter(buf, spec) {} - - using printf_arg_formatter::operator(); - - void operator()(double value) { - if (round(value * pow(10, spec().precision())) == 0) - value = 0; - printf_arg_formatter::operator()(value); - } -}; - std::string custom_vformat(fmt::string_view format_str, fmt::args args) { fmt::memory_buffer buffer; // Pass custom argument formatter as a template arg to vwrite. @@ -58,25 +42,6 @@ std::string custom_format(const char *format_str, const Args & ... args) { return custom_vformat(format_str, va); } -typedef fmt::printf_context - CustomPrintfFormatter; - -std::string custom_vsprintf( - const char* format_str, - fmt::basic_args args) { - fmt::memory_buffer buffer; - CustomPrintfFormatter formatter(format_str, args); - formatter.format(buffer); - return std::string(buffer.data(), buffer.size()); -} - -template -std::string custom_sprintf(const char *format_str, const Args & ... args) { - auto va = fmt::make_args(args...); - return custom_vsprintf(format_str, va); -} - TEST(CustomFormatterTest, Format) { EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001)); - EXPECT_EQ("0.00", custom_sprintf("%.2f", -.00001)); } diff --git a/test/format-test.cc b/test/format-test.cc index 64f95382..c078365b 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1509,7 +1509,7 @@ class MockArgFormatter : public fmt::internal::arg_formatter_base { void operator()(int value) { call(value); } - void operator()(fmt::internal::custom_value) {} + void operator()(fmt::basic_arg::handle) {} }; void custom_vformat(fmt::string_view format_str, fmt::args args) { diff --git a/test/util-test.cc b/test/util-test.cc index 459e5914..952181d6 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -590,18 +590,17 @@ TEST(UtilTest, PointerArg) { TEST(UtilTest, CustomArg) { ::Test test; - typedef MockVisitor> Visitor; - testing::StrictMock visitor; - EXPECT_CALL(visitor, visit(_)).WillOnce( - testing::Invoke([&](fmt::internal::custom_value custom) { - EXPECT_EQ(&test, custom.value); + using handle = typename fmt::basic_arg::handle; + using visitor = MockVisitor; + testing::StrictMock v; + EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke([&](handle h) { fmt::memory_buffer buffer; fmt::context ctx("", fmt::args()); - custom.format(buffer, &test, &ctx); + h.format(buffer, ctx); EXPECT_EQ("test", std::string(buffer.data(), buffer.size())); - return Visitor::Result(); + return visitor::Result(); })); - fmt::visit(visitor, make_arg(test)); + fmt::visit(v, make_arg(test)); } TEST(ArgVisitorTest, VisitInvalidArg) {