From 41cfc739fe9b2e01742ec41871efe0e74c6b879e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 Mar 2023 18:56:45 -0700 Subject: [PATCH] Generalize format_as --- include/fmt/core.h | 12 ++++++------ include/fmt/format.h | 32 +++++++++++++++----------------- test/core-test.cc | 26 -------------------------- test/format-test.cc | 21 +++++++++++++++++++++ 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 27143856..9f93776a 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -329,6 +329,12 @@ struct monostate { # define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 #endif +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast(b); +} +#endif + FMT_BEGIN_DETAIL_NAMESPACE // Suppresses "unused variable" warnings with the method described in @@ -1359,12 +1365,6 @@ enum { long_short = sizeof(long) == sizeof(int) }; using long_type = conditional_t; using ulong_type = conditional_t; -#ifdef __cpp_lib_byte -inline auto format_as(std::byte b) -> unsigned char { - return static_cast(b); -} -#endif - template struct format_as_result { template ::value || std::is_class::value)> diff --git a/include/fmt/format.h b/include/fmt/format.h index 9f53ecdb..0ed59422 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4218,6 +4218,18 @@ formatter(ctx.out(), val, specs_, ctx.locale()); } +template +struct formatter::value>> + : private formatter> { + using base = formatter>; + using base::parse; + + template + auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { + return base::format(format_as(value), ctx); + } +}; + template struct formatter : formatter { template @@ -4370,23 +4382,9 @@ struct formatter, Char> { #else typename std::iterator_traits::value_type; #endif - using context = buffer_context; - using mapper = detail::arg_mapper; - - template ::value)> - static auto map(const T& value) -> const T& { - return value; - } - template ::value)> - static auto map(const T& value) -> decltype(mapper().map(value)) { - return mapper().map(value); - } - using formatter_type = conditional_t::value, - formatter()))>, - Char>, + formatter, Char>, detail::fallback_formatter>; formatter_type value_formatter_; @@ -4403,12 +4401,12 @@ struct formatter, Char> { auto it = value.begin; auto out = ctx.out(); if (it != value.end) { - out = value_formatter_.format(map(*it), ctx); + out = value_formatter_.format(*it, ctx); ++it; while (it != value.end) { out = detail::copy_str(value.sep.begin(), value.sep.end(), out); ctx.advance_to(out); - out = value_formatter_.format(map(*it), ctx); + out = value_formatter_.format(*it, ctx); ++it; } } diff --git a/test/core-test.cc b/test/core-test.cc index e191d720..0994cb8f 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -673,24 +673,6 @@ FMT_END_NAMESPACE enum class unformattable_scoped_enum {}; -namespace test { -enum class scoped_enum_as_int {}; -auto format_as(scoped_enum_as_int) -> int { return 42; } - -enum class scoped_enum_as_string_view {}; -auto format_as(scoped_enum_as_string_view) -> fmt::string_view { return "foo"; } - -enum class scoped_enum_as_string {}; -auto format_as(scoped_enum_as_string) -> std::string { return "foo"; } - -struct struct_as_int {}; -auto format_as(struct_as_int) -> int { return 42; } - -struct convertible_to_enum { - operator scoped_enum_as_int() const { return {}; } -}; -} // namespace test - TEST(core_test, is_formattable) { static_assert(!fmt::is_formattable::value, ""); #ifdef __cpp_char8_t @@ -729,7 +711,6 @@ TEST(core_test, is_formattable) { static_assert(!fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); - static_assert(fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); } @@ -741,13 +722,6 @@ TEST(core_test, format_to) { EXPECT_EQ(s, "42"); } -TEST(core_test, format_as) { - EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_int()), "42"); - // EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string_view()), "foo"); - // EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string()), "foo"); - EXPECT_EQ(fmt::format("{}", test::struct_as_int()), "42"); -} - #ifdef __cpp_lib_byte TEST(core_test, format_byte) { EXPECT_EQ(fmt::format("{}", std::byte(42)), "42"); diff --git a/test/format-test.cc b/test/format-test.cc index 98115839..04e4798e 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2145,6 +2145,27 @@ TEST(format_test, back_insert_slicing) { EXPECT_EQ(fmt::format("{}", check_back_appender{}), "y"); } +namespace test { +enum class scoped_enum_as_int {}; +auto format_as(scoped_enum_as_int) -> int { return 42; } + +enum class scoped_enum_as_string_view {}; +auto format_as(scoped_enum_as_string_view) -> fmt::string_view { return "foo"; } + +enum class scoped_enum_as_string {}; +auto format_as(scoped_enum_as_string) -> std::string { return "foo"; } + +struct struct_as_int {}; +auto format_as(struct_as_int) -> int { return 42; } +} // namespace test + +TEST(format_test, format_as) { + EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_int()), "42"); + EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string_view()), "foo"); + EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string()), "foo"); + EXPECT_EQ(fmt::format("{}", test::struct_as_int()), "42"); +} + template bool check_enabled_formatter() { static_assert(std::is_default_constructible>::value, "");