diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 1b71131d..645b06c3 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1158,27 +1158,30 @@ struct formatter, Char> { } template - auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(const duration& d, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs_copy = specs; + auto precision_copy = precision; auto begin = format_str.begin(), end = format_str.end(); // As a possible future optimization, we could avoid extra copying if width // is not specified. basic_memory_buffer buf; auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref, - ctx); - detail::handle_dynamic_spec(precision, + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + detail::handle_dynamic_spec(precision_copy, precision_ref, ctx); if (begin == end || *begin == '}') { - out = detail::format_duration_value(out, d.count(), precision); + out = detail::format_duration_value(out, d.count(), precision_copy); detail::format_duration_unit(out); } else { detail::chrono_formatter f( ctx, out, d); - f.precision = precision; + f.precision = precision_copy; parse_chrono_format(begin, end, f); } return detail::write( - ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); } }; diff --git a/test/compile-test.cc b/test/compile-test.cc index ed34429d..6d5af50b 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -13,6 +13,7 @@ # include #endif +#include "fmt/chrono.h" #include "fmt/compile.h" #include "gmock.h" #include "gtest-extra.h" @@ -139,102 +140,20 @@ TEST(CompileTest, FormatWideString) { EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42)); } -struct test_custom_formattable {}; - -FMT_BEGIN_NAMESPACE -template <> struct formatter { - enum class output_type { two, four } type{output_type::two}; - - FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - while (it != end && *it != '}') { - ++it; - } - auto spec = string_view(ctx.begin(), static_cast(it - ctx.begin())); - auto tag = string_view("custom"); - if (spec.size() == tag.size()) { - bool is_same = true; - for (size_t index = 0; index < spec.size(); ++index) { - if (spec[index] != tag[index]) { - is_same = false; - break; - } - } - type = is_same ? output_type::four : output_type::two; - } else { - type = output_type::two; - } - return it; - } - - template - auto format(const test_custom_formattable&, FormatContext& ctx) const - -> decltype(ctx.out()) { - return format_to(ctx.out(), type == output_type::two ? "{:>2}" : "{:>4}", - 42); - } -}; -FMT_END_NAMESPACE - TEST(CompileTest, FormatSpecs) { EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42)); - EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), test_custom_formattable())); - EXPECT_EQ(" 42", - fmt::format(FMT_COMPILE("{:custom}"), test_custom_formattable())); + EXPECT_EQ("1.2 ms ", + fmt::format(FMT_COMPILE("{:7.1%Q %q}"), + std::chrono::duration(1.234))); } -struct test_dynamic_formattable {}; - -FMT_BEGIN_NAMESPACE -template <> struct formatter { - size_t amount = 0; - detail::arg_ref width_refs[3]; - - FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { - amount = static_cast(*ctx.begin() - '0'); - if (amount >= 1) { - width_refs[0] = detail::arg_ref(ctx.next_arg_id()); - } - if (amount >= 2) { - width_refs[1] = detail::arg_ref(ctx.next_arg_id()); - } - if (amount >= 3) { - width_refs[2] = detail::arg_ref(ctx.next_arg_id()); - } - return ctx.begin() + 1; - } - - template - auto format(const test_dynamic_formattable&, FormatContext& ctx) const - -> decltype(ctx.out()) { - int widths[3]{}; - for (size_t i = 0; i < amount; ++i) { - detail::handle_dynamic_spec(widths[i], - width_refs[i], ctx); - } - if (amount == 1) { - return format_to(ctx.out(), "{:{}}", 41, widths[0]); - } else if (amount == 2) { - return format_to(ctx.out(), "{:{}}{:{}}", 41, widths[0], 42, widths[1]); - } else if (amount == 3) { - return format_to(ctx.out(), "{:{}}{:{}}{:{}}", 41, widths[0], 42, - widths[1], 43, widths[2]); - } else { - throw format_error("formatting error"); - } - } -}; -FMT_END_NAMESPACE - TEST(CompileTest, DynamicFormatSpecs) { - EXPECT_EQ(" 42foo ", - fmt::format(FMT_COMPILE("{:{}}{:{}}"), 42, 4, "foo", 5)); - EXPECT_EQ(" 41", - fmt::format(FMT_COMPILE("{:1}"), test_dynamic_formattable(), 4)); - EXPECT_EQ(" 41 42", - fmt::format(FMT_COMPILE("{:2}"), test_dynamic_formattable(), 3, 5)); - EXPECT_EQ(" 41 42 43", fmt::format(FMT_COMPILE("{:3}"), - test_dynamic_formattable(), 5, 3, 4)); + EXPECT_EQ("foo ", fmt::format(FMT_COMPILE("{:{}}"), "foo", 5)); + EXPECT_EQ(" 3.14", fmt::format(FMT_COMPILE("{:{}.{}f}"), 3.141592, 6, 2)); + EXPECT_EQ( + "=1.234ms=", + fmt::format(FMT_COMPILE("{:=^{}.{}}"), + std::chrono::duration(1.234), 9, 3)); } TEST(CompileTest, ManualOrdering) { @@ -244,9 +163,9 @@ TEST(CompileTest, ManualOrdering) { EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{1} {0}"), 43, 41)); EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{0} {2}"), 41, 42, 43)); EXPECT_EQ(" 41 43", fmt::format(FMT_COMPILE("{1:{2}} {0:4}"), 43, 41, 4)); - EXPECT_EQ("42 42", - fmt::format(FMT_COMPILE("{1} {0:custom}"), - test_custom_formattable(), test_custom_formattable())); + EXPECT_EQ("42 1.2 ms ", + fmt::format(FMT_COMPILE("{0} {1:7.1%Q %q}"), 42, + std::chrono::duration(1.234))); EXPECT_EQ( "true 42 42 foo 0x1234 foo", fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f,