diff --git a/include/fmt/base.h b/include/fmt/base.h index 7bae9789491c..6222142c2c10 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -428,6 +428,33 @@ enum class uint128_opt {}; template auto convert_for_visit(T) -> monostate { return {}; } #endif +#ifndef FMT_USE_BITINT +# if FMT_CLANG_VERSION >= 1400 +# define FMT_USE_BITINT 1 +# else +# define FMT_USE_BITINT 0 +# endif +#endif + +template struct bitint_traits {}; +#if FMT_USE_BITINT +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wbit-int-extension" + +// fmt only supports up to 128 bits https://github.com/fmtlib/fmt/pull/4072 +template struct bitint_traits<_BitInt(N)> { + static constexpr bool is_formattable = N <= 128; + using formatter_type = conditional_t<(N <= 64), long long, __int128>; +}; +template struct bitint_traits { + static constexpr bool is_formattable = N <= 128; + using formatter_type = + conditional_t<(N <= 64), unsigned long long, unsigned __int128>; +}; + +# pragma clang diagnostic pop +#endif + // Casts a nonnegative integer to unsigned. template FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t { @@ -1476,6 +1503,17 @@ template struct arg_mapper { FMT_MAP_API auto map(double val) -> double { return val; } FMT_MAP_API auto map(long double val) -> long double { return val; } + template >::is_formattable)> + FMT_MAP_API auto map(T&& val) -> decltype(val) { + return val; + } + template >::is_formattable)> + FMT_MAP_API auto map(T&&) -> unformattable { + return {}; + } + FMT_MAP_API auto map(char_type* val) -> const char_type* { return val; } FMT_MAP_API auto map(const char_type* val) -> const char_type* { return val; } template , diff --git a/include/fmt/format.h b/include/fmt/format.h index f0555011b74c..c3ffef6afb30 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3950,6 +3950,11 @@ class formatter, Char> template struct formatter : formatter, Char> {}; +template +struct formatter::is_formattable)>> + : formatter::formatter_type, Char> {}; + /** * Converts `p` to `const void*` for pointer formatting. * diff --git a/test/format-test.cc b/test/format-test.cc index 64570b7831dc..8efdddfd47e9 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -30,10 +30,13 @@ # include #endif +#include + +#include + #include "gtest-extra.h" #include "mock-allocator.h" #include "util.h" - using fmt::basic_memory_buffer; using fmt::format_error; using fmt::memory_buffer; @@ -931,8 +934,7 @@ TEST(format_test, runtime_width) { } TEST(format_test, exponent_range) { - for (int e = -1074; e <= 1023; ++e) - (void)fmt::format("{}", std::ldexp(1, e)); + for (int e = -1074; e <= 1023; ++e) (void)fmt::format("{}", std::ldexp(1, e)); } TEST(format_test, precision) { @@ -2507,3 +2509,62 @@ TEST(format_test, writer) { fmt::writer(s).print("foo"); EXPECT_EQ(s.str(), "foo"); } + +#if FMT_USE_BITINT +# pragma clang diagnostic ignored "-Wbit-int-extension" + +template +using bitint_helper = + fmt::conditional_t; +template using signed_bitint = bitint_helper; +template using unsigned_bitint = bitint_helper; + +TEST(format_test, bitint) { + EXPECT_EQ(fmt::format("{}", unsigned_bitint<3>(7)), "7"); + EXPECT_EQ(fmt::format("{}", signed_bitint<7>()), "0"); + + EXPECT_EQ(fmt::format("{}", unsigned_bitint<15>(31000)), "31000"); + EXPECT_EQ(fmt::format("{}", signed_bitint<16>(INT16_MIN)), "-32768"); + EXPECT_EQ(fmt::format("{}", signed_bitint<16>(INT16_MAX)), "32767"); + + EXPECT_EQ(fmt::format("{}", unsigned_bitint<32>(4294967295)), "4294967295"); + + EXPECT_EQ(fmt::format("{}", unsigned_bitint<47>(140737488355327ULL)), + "140737488355327"); + EXPECT_EQ(fmt::format("{}", signed_bitint<47>(-40737488355327LL)), + "-40737488355327"); + + // Check lvalues and const + auto a = signed_bitint<8>(0); + auto b = unsigned_bitint<32>(4294967295); + const auto c = signed_bitint<7>(0); + const auto d = unsigned_bitint<32>(4294967295); + EXPECT_EQ(fmt::format("{}", a), "0"); + EXPECT_EQ(fmt::format("{}", b), "4294967295"); + EXPECT_EQ(fmt::format("{}", c), "0"); + EXPECT_EQ(fmt::format("{}", d), "4294967295"); + + static_assert(fmt::is_formattable, char>{}, ""); + static_assert(fmt::is_formattable, char>{}, ""); + +# if FMT_USE_INT128 + static_assert(fmt::is_formattable, char>{}, ""); + static_assert(fmt::is_formattable, char>{}, ""); + + EXPECT_EQ(fmt::format("{}", signed_bitint<128>(0)), "0"); + EXPECT_EQ(fmt::format("{}", unsigned_bitint<128>(0)), "0"); + EXPECT_EQ("9223372036854775808", + fmt::format("{}", signed_bitint<65>(INT64_MAX) + 1)); + EXPECT_EQ("-9223372036854775809", + fmt::format("{}", signed_bitint<65>(INT64_MIN) - 1)); + EXPECT_EQ("18446744073709551616", + fmt::format("{}", unsigned_bitint<66>(UINT64_MAX) + 1)); + EXPECT_EQ("170141183460469231731687303715884105727", + fmt::format("{}", signed_bitint<128>(int128_max))); + EXPECT_EQ("-170141183460469231731687303715884105728", + fmt::format("{}", signed_bitint<128>(int128_min))); + EXPECT_EQ("340282366920938463463374607431768211455", + fmt::format("{}", unsigned_bitint<128>(uint128_max))); +# endif +} +#endif