From 6ae402fd0bf4e6491dc7b228401d531057dbb094 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 18 Mar 2021 10:11:06 -0700 Subject: [PATCH] Fix handling of types with to_string_view and formatter specialization (#2180) --- include/fmt/args.h | 4 ++- test/args-test.cc | 61 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/include/fmt/args.h b/include/fmt/args.h index 0fe75932..562e8ab1 100644 --- a/include/fmt/args.h +++ b/include/fmt/args.h @@ -95,7 +95,9 @@ class dynamic_format_arg_store }; template - using stored_type = conditional_t::value, + using stored_type = conditional_t::value && + !has_formatter::value && + !detail::is_reference_wrapper::value, std::basic_string, T>; // Storage of basic_format_arg must be contiguous. diff --git a/test/args-test.cc b/test/args-test.cc index 27a874d5..e8c501c5 100644 --- a/test/args-test.cc +++ b/test/args-test.cc @@ -10,7 +10,7 @@ #include "gmock.h" TEST(ArgsTest, Basic) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(42); store.push_back("abc1"); store.push_back(1.5f); @@ -19,14 +19,14 @@ TEST(ArgsTest, Basic) { TEST(ArgsTest, StringsAndRefs) { // Unfortunately the tests are compiled with old ABI so strings use COW. - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); char str[] = "1234567890"; store.push_back(str); store.push_back(std::cref(str)); store.push_back(fmt::string_view{str}); str[0] = 'X'; - std::string result = fmt::vformat("{} and {} and {}", store); + auto result = fmt::vformat("{} and {} and {}", store); EXPECT_EQ("1234567890 and X234567890 and X234567890", result); } @@ -48,27 +48,52 @@ template <> struct formatter { FMT_END_NAMESPACE TEST(ArgsTest, CustomFormat) { - fmt::dynamic_format_arg_store store; - custom_type c{}; + auto store = fmt::dynamic_format_arg_store(); + auto c = custom_type(); store.push_back(c); ++c.i; store.push_back(c); ++c.i; store.push_back(std::cref(c)); ++c.i; - std::string result = fmt::vformat("{} and {} and {}", store); + auto result = fmt::vformat("{} and {} and {}", store); EXPECT_EQ("cust=0 and cust=1 and cust=3", result); } +struct to_stringable { + friend fmt::string_view to_string_view(to_stringable) { return {}; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter { + auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const to_stringable&, FormatContext& ctx) -> decltype(ctx.out()) { + return ctx.out(); + } +}; +FMT_END_NAMESPACE + +TEST(ArgsTest, ToStringAndFormatter) { + auto store = fmt::dynamic_format_arg_store(); + auto s = to_stringable(); + store.push_back(s); + store.push_back(std::cref(s)); + fmt::vformat("", store); +} + TEST(ArgsTest, NamedInt) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(fmt::arg("a1", 42)); EXPECT_EQ("42", fmt::vformat("{a1}", store)); } TEST(ArgsTest, NamedStrings) { - fmt::dynamic_format_arg_store store; - char str[]{"1234567890"}; + auto store = fmt::dynamic_format_arg_store(); + char str[] = "1234567890"; store.push_back(fmt::arg("a1", str)); store.push_back(fmt::arg("a2", std::cref(str))); str[0] = 'X'; @@ -76,7 +101,7 @@ TEST(ArgsTest, NamedStrings) { } TEST(ArgsTest, NamedArgByRef) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); char band[] = "Rolling Stones"; store.push_back(fmt::arg("band", std::cref(band))); band[9] = 'c'; // Changing band affects the output. @@ -84,23 +109,23 @@ TEST(ArgsTest, NamedArgByRef) { } TEST(ArgsTest, NamedCustomFormat) { - fmt::dynamic_format_arg_store store; - custom_type c{}; + auto store = fmt::dynamic_format_arg_store(); + auto c = custom_type(); store.push_back(fmt::arg("c1", c)); ++c.i; store.push_back(fmt::arg("c2", c)); ++c.i; store.push_back(fmt::arg("c_ref", std::cref(c))); ++c.i; - std::string result = fmt::vformat("{c1} and {c2} and {c_ref}", store); + auto result = fmt::vformat("{c1} and {c2} and {c_ref}", store); EXPECT_EQ("cust=0 and cust=1 and cust=3", result); } TEST(ArgsTest, Clear) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(42); - std::string result = fmt::vformat("{}", store); + auto result = fmt::vformat("{}", store); EXPECT_EQ("42", result); store.push_back(43); @@ -114,11 +139,11 @@ TEST(ArgsTest, Clear) { } TEST(ArgsTest, Reserve) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.reserve(2, 1); store.push_back(1.5f); store.push_back(fmt::arg("a1", 42)); - std::string result = fmt::vformat("{a1} and {}", store); + auto result = fmt::vformat("{a1} and {}", store); EXPECT_EQ("42 and 1.5", result); } @@ -139,7 +164,7 @@ template <> struct formatter { FMT_END_NAMESPACE TEST(ArgsTest, ThrowOnCopy) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(std::string("foo")); try { store.push_back(copy_throwable());