From a197a994c5a2363bc93bcff41bc77390802051cf Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 20 Sep 2024 07:08:47 -0700 Subject: [PATCH] Add member format_as for std --- include/fmt/base.h | 38 +++++++++++++++++++++----------------- include/fmt/format.h | 13 +++++++++++++ include/fmt/ranges.h | 4 ++-- test/format-test.cc | 2 ++ 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index a4af4f4c..04fbf22d 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -17,8 +17,6 @@ # include // FILE # include // memcmp -// is also included transitively from . -# include // std::byte # include // std::enable_if #endif @@ -309,6 +307,7 @@ using make_unsigned_t = typename std::make_unsigned::type; template using underlying_t = typename std::underlying_type::type; template using decay_t = typename std::decay::type; +using nullptr_t = decltype(nullptr); #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.9 to make void_t work in a SFINAE context. @@ -506,7 +505,7 @@ template class basic_string_view { constexpr basic_string_view(const Char* s, size_t count) noexcept : data_(s), size_(count) {} - constexpr basic_string_view(std::nullptr_t) = delete; + constexpr basic_string_view(nullptr_t) = delete; /// Constructs a string reference object from a C string. #if FMT_GCC_VERSION @@ -643,15 +642,6 @@ struct formatter { formatter() = delete; }; -// This is defined in base.h instead of format.h to avoid injecting in std. -// It is a template to avoid undesirable implicit conversions to std::byte. -#ifdef __cpp_lib_byte -template ::value)> -inline auto format_as(T b) -> unsigned char { - return static_cast(b); -} -#endif - /// Reports a format error at compile time or, via a `format_error` exception, /// at runtime. // This function is intentionally not constexpr to give a compile-time error. @@ -1071,14 +1061,24 @@ using ulong_type = conditional_t; template using format_as_result = remove_cvref_t()))>; +template +using format_as_member_result = + remove_cvref_t::format_as(std::declval()))>; template struct use_format_as : std::false_type {}; +// format_as member is only used to avoid injection into the std namespace. +template +struct use_format_as_member : std::false_type {}; // Only map owning types because mapping views can be unsafe. template struct use_format_as< - T, bool_constant>::value>> + T, bool_constant>::value>> + : std::true_type {}; +template +struct use_format_as_member< + T, bool_constant>::value>> : std::true_type {}; template > @@ -1086,7 +1086,7 @@ using use_formatter = bool_constant<(std::is_class::value || std::is_enum::value || std::is_union::value || std::is_array::value) && !has_to_string_view::value && !is_named_arg::value && - !use_format_as::value>; + !use_format_as::value && !use_format_as_member::value>; template > auto has_formatter_impl(T* p, buffered_context* ctx = nullptr) @@ -1140,13 +1140,15 @@ template struct type_mapper { static auto map(const void*) -> const void*; static auto map(volatile void*) -> const void*; static auto map(const volatile void*) -> const void*; - static auto map(std::nullptr_t) -> const void*; + static auto map(nullptr_t) -> const void*; template ::value || std::is_member_pointer::value)> static auto map(const T&) -> void; template ::value)> static auto map(const T& x) -> decltype(map(format_as(x))); + template ::value)> + static auto map(const T& x) -> decltype(map(formatter::format_as(x))); template ::value)> static auto map(T&) -> conditional_t(), T&, void>; @@ -2130,7 +2132,7 @@ template class value { : pointer(const_cast(x)) {} FMT_INLINE value(const volatile void* x FMT_BUILTIN) : pointer(const_cast(x)) {} - FMT_INLINE value(std::nullptr_t) : pointer(nullptr) {} + FMT_INLINE value(nullptr_t) : pointer(nullptr) {} template ::value || std::is_member_pointer::value)> @@ -2144,6 +2146,8 @@ template class value { template ::value)> value(const T& x) : value(format_as(x)) {} + template ::value)> + value(const T& x) : value(formatter::format_as(x)) {} template ::value)> value(const T& named_arg) : value(named_arg.value) {} @@ -2376,9 +2380,9 @@ template class basic_appender { public: using iterator_category = int; using value_type = T; - using difference_type = ptrdiff_t; using pointer = T*; using reference = T&; + using difference_type = decltype(pointer() - pointer()); using container_type = detail::buffer; FMT_CONSTEXPR basic_appender(detail::buffer& buf) : container(&buf) {} diff --git a/include/fmt/format.h b/include/fmt/format.h index 58f52990..45cab4cd 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -42,6 +42,7 @@ #ifndef FMT_MODULE # include // std::signbit +# include // std::byte # include // uint32_t # include // std::memcpy # include // std::initializer_list @@ -3919,6 +3920,18 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t { } } // namespace enums +#ifdef __cpp_lib_byte +template <> struct formatter : formatter { + static auto format_as(std::byte b) -> unsigned char { + return static_cast(b); + } + template + auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { + return formatter::format(format_as(b), ctx); + } +}; +#endif + class bytes { private: string_view data_; diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 572aa407..c6020abe 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -115,8 +115,8 @@ struct is_range_ // tuple_size and tuple_element check. template class is_tuple_like_ { - template - static auto check(U* p) -> decltype(std::tuple_size::value, int()); + template ::type> + static auto check(U* p) -> decltype(std::tuple_size::value, 0); template static void check(...); public: diff --git a/test/format-test.cc b/test/format-test.cc index 73186d02..1c907c87 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2030,6 +2030,8 @@ enum big_enum : unsigned long long { big_enum_value = 5000000000ULL }; auto format_as(big_enum e) -> unsigned long long { return e; } TEST(format_test, strong_enum) { + auto arg = fmt::basic_format_arg(big_enum_value); + EXPECT_EQ(arg.type(), fmt::detail::type::ulong_long_type); EXPECT_EQ(fmt::format("{}", big_enum_value), "5000000000"); } #endif