diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 511c8d85..4f0d92a0 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -237,11 +237,7 @@ struct fp { template explicit FMT_CONSTEXPR fp(Float n) { assign(n); } template - using is_supported = - bool_constant::digits == 64)>; + using is_supported = bool_constant::digits <= 64>; // Assigns d to this and return true iff predecessor is closer than successor. template ::value)> diff --git a/include/fmt/format.h b/include/fmt/format.h index 08c1f7d0..9e086669 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -288,9 +288,8 @@ template class formatbuf : public Streambuf { }; // Implementation of std::bit_cast for pre-C++20. -template +template FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { - static_assert(sizeof(To) == sizeof(From), "size mismatch"); #ifdef __cpp_lib_bit_cast if (is_constant_evaluated()) return std::bit_cast(from); #endif @@ -369,28 +368,10 @@ using uint128_t = conditional_t; #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; -inline auto to_uintptr(const void* p) -> uintptr_t { - return bit_cast(p); -} #else using uintptr_t = uint128_t; #endif -// A fallback for systems that lack uintptr_t. -template inline auto to_uintptr(const void* p) -> T { - constexpr auto size = static_cast(sizeof(void*)); - struct data_t { - unsigned char value[size]; - } data = bit_cast(p); - auto result = T(); - if (const_check(is_big_endian())) { - for (int i = 0; i < size; ++i) result = (result << 8) | data.value[i]; - } else { - for (int i = size - 1; i >= 0; --i) result = (result << 8) | data.value[i]; - } - return result; -} - // Returns the largest possible value for type T. Same as // std::numeric_limits::max() but shorter and not affected by the max macro. template constexpr auto max_value() -> T { @@ -403,6 +384,25 @@ template constexpr auto num_bits() -> int { template <> constexpr auto num_bits() -> int { return 128; } template <> constexpr auto num_bits() -> int { return 128; } +// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t +// and 128-bit pointers to uint128_fallback. +template sizeof(From))> +inline auto bit_cast(const From& from) -> To { + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); + struct data_t { + unsigned value[size]; + } data = bit_cast(from); + auto result = To(); + if (const_check(is_big_endian())) { + for (int i = 0; i < size; ++i) + result = (result << num_bits()) | data.value[i]; + } else { + for (int i = size - 1; i >= 0; --i) + result = (result << num_bits()) | data.value[i]; + } + return result; +} + FMT_INLINE void assume(bool condition) { (void)condition; #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION @@ -1275,7 +1275,7 @@ template <> struct float_info { template struct float_info::value && std::numeric_limits::digits == 64>> { - using carrier_uint = detail::uint128_opt; + using carrier_uint = detail::uint128_t; static const int significand_bits = 64; static const int exponent_bits = 15; }; @@ -1911,7 +1911,7 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s, -> OutputIt { return check_cstring_type_spec(specs.type) ? write(out, basic_string_view(s), specs, {}) - : write_ptr(out, to_uintptr(s), &specs); + : write_ptr(out, bit_cast(s), &specs); } template @@ -2394,7 +2394,7 @@ auto write(OutputIt out, const T* value, const basic_format_specs& specs = {}, locale_ref = {}) -> OutputIt { check_pointer_type_spec(specs.type, error_handler()); - return write_ptr(out, to_uintptr(value), &specs); + return write_ptr(out, bit_cast(value), &specs); } // A write overload that handles implicit conversions. diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index 2cdc2899..41f736c6 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -340,16 +340,6 @@ TEST(format_impl_test, count_digits) { test_count_digits(); } -TEST(format_impl_test, write_uintptr_fallback) { - std::string s; - fmt::detail::write_ptr( - std::back_inserter(s), - fmt::detail::to_uintptr( - reinterpret_cast(0xface)), - nullptr); - EXPECT_EQ(s, "0xface"); -} - #ifdef _WIN32 # include #endif diff --git a/test/format-test.cc b/test/format-test.cc index e7b23b7c..c7a468ad 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1445,6 +1445,18 @@ TEST(format_test, format_pointer) { EXPECT_EQ("0x0", fmt::format("{}", nullptr)); } +TEST(format_test, write_uintptr_fallback) { + // Test that formatting a pointer by converting it to uint128_fallback works. + // This is needed to support systems without uintptr_t. + auto s = std::string(); + fmt::detail::write_ptr( + std::back_inserter(s), + fmt::detail::bit_cast( + reinterpret_cast(0xface)), + nullptr); + EXPECT_EQ(s, "0xface"); +} + enum class color { red, green, blue }; TEST(format_test, format_enum_class) {