From 99768695498147627ff1c70a5c6d97d1dc7c2543 Mon Sep 17 00:00:00 2001 From: Alexey Ochapov Date: Sun, 30 May 2021 03:46:12 +0300 Subject: [PATCH] fix custom types formatting at compile-time, add test --- include/fmt/core.h | 2 +- include/fmt/format.h | 9 +++++---- test/compile-test.cc | 37 ++++++++++++++++++++++++++----------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index d71a2ab1..d2746615 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1140,7 +1140,7 @@ template class value { FMT_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} - template FMT_INLINE value(const T& val) { + template FMT_CONSTEXPR FMT_INLINE value(const T& val) { custom.value = &val; // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and diff --git a/include/fmt/format.h b/include/fmt/format.h index b03449e1..2de69927 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1935,10 +1935,11 @@ OutputIt write(OutputIt out, const T* value, } template -auto write(OutputIt out, const T& value) -> typename std::enable_if< - mapped_type_constant>::value == - type::custom_type, - OutputIt>::type { +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> + typename std::enable_if< + mapped_type_constant>::value == + type::custom_type, + OutputIt>::type { using context_type = basic_format_context; using formatter_type = conditional_t::value, diff --git a/test/compile-test.cc b/test/compile-test.cc index 58fd057d..fb84ab28 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -53,17 +53,6 @@ TEST(iterator_test, truncating_back_inserter) { EXPECT_EQ(buffer, "42"); } -struct test_formattable {}; - -FMT_BEGIN_NAMESPACE -template <> struct formatter : formatter { - template - auto format(test_formattable, FormatContext& ctx) -> decltype(ctx.out()) { - return formatter::format("foo", ctx); - } -}; -FMT_END_NAMESPACE - TEST(compile_test, compile_fallback) { // FMT_COMPILE should fallback on runtime formatting when `if constexpr` is // not available. @@ -71,6 +60,27 @@ TEST(compile_test, compile_fallback) { } #ifdef __cpp_if_constexpr +struct test_formattable {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter : formatter { + char word_spec = 'f'; + constexpr auto parse(format_parse_context& ctx) { + auto it = ctx.begin(), end = ctx.end(); + if (it == end || *it == '}') return it; + if (it != end && (*it == 'f' || *it == 'b')) word_spec = *it++; + if (it != end && *it != '}') throw format_error("invalid format"); + return it; + } + template + constexpr auto format(test_formattable, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter::format(word_spec == 'f' ? "foo" : "bar", + ctx); + } +}; +FMT_END_NAMESPACE + TEST(compile_test, format_default) { EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42)); EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u)); @@ -336,6 +346,11 @@ TEST(compile_time_formatting_test, combination) { EXPECT_EQ(" -42", test_format<5>(FMT_COMPILE("{:{}}"), -42, 4)); } +TEST(compile_time_formatting_test, custom_type) { + EXPECT_EQ("foo", test_format<4>(FMT_COMPILE("{}"), test_formattable())); + EXPECT_EQ("bar", test_format<4>(FMT_COMPILE("{:b}"), test_formattable())); +} + TEST(compile_time_formatting_test, multibyte_fill) { EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42)); }