From 6dac6cad052d6593f5fa07529d912f3bd0d6bb11 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 5 Jul 2026 18:22:18 +0200 Subject: [PATCH 1/4] feat: add formatter for std::exception_ptr (#4808) (#4819) --- include/fmt/std.h | 19 +++++++++++++++++++ test/std-test.cc | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/fmt/std.h b/include/fmt/std.h index f7d8d495..d606508b 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -649,6 +649,25 @@ struct formatter< } }; +template <> struct formatter : formatter { + template + auto format(const std::exception_ptr& ex_ptr, FormatContext& ctx) const + -> decltype(ctx.out()) { + if (!ex_ptr) { + return detail::write(ctx.out(), string_view("nullptr")); + } + + try { + std::rethrow_exception(ex_ptr); + } catch (const std::exception& e) { + // Reuses the base formatter::format behavior perfectly + return formatter::format(e, ctx); + } catch (...) { + return detail::write(ctx.out(), string_view("unknown exception")); + } + } +}; + template struct formatter, Char> : formatter { static_assert(N <= 64, "unsupported _BitInt"); diff --git a/test/std-test.cc b/test/std-test.cc index f6fc1284..b308fe38 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -404,6 +404,26 @@ TEST(std_test, exception) { #endif } +TEST(std_test, exception_ptr) { + std::exception_ptr p1 = nullptr; + std::exception_ptr p2; + + try { + using namespace my_ns1::my_ns2; + throw my_exception("My Exception"); + } catch (...) { + p2 = std::current_exception(); + } + + EXPECT_EQ(fmt::format("{}", p1), "nullptr"); + EXPECT_EQ(fmt::format("{}", p2), "My Exception"); + +#if FMT_USE_RTTI + EXPECT_EQ(fmt::format("{:t}", p2), + "my_ns1::my_ns2::my_exception: My Exception"); +#endif +} + #if FMT_USE_RTTI TEST(std_test, type_info) { EXPECT_EQ(fmt::format("{}", typeid(std::runtime_error)), From e690107da929117fdd0ea352c6e9d1ac388b22d7 Mon Sep 17 00:00:00 2001 From: AMAN UPADHYAY Date: Sun, 5 Jul 2026 14:51:24 +0530 Subject: [PATCH 2/4] Fix FMT_COMPILE failure with format_as mapped types (#4794) --- include/fmt/compile.h | 10 +++++++++- test/compile-test.cc | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 1a987cb0..a618e162 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -168,7 +168,13 @@ constexpr auto get_arg_checked(const Args&... args) -> const T& { template struct is_compiled_format> : std::true_type {}; -// A replacement field that refers to argument N. + +template +struct compile_has_format_as : std::false_type {}; + +template +struct compile_has_format_as()))>> : std::true_type {}; + template struct field { using char_type = Char; @@ -178,6 +184,8 @@ template struct field { if constexpr (std::is_convertible>::value) { auto s = basic_string_view(arg); return copy(s.begin(), s.end(), out); + } else if constexpr (compile_has_format_as::value) { + return write(out, format_as(arg)); } else { return write(out, arg); } diff --git a/test/compile-test.cc b/test/compile-test.cc index 5a944612..94a7da1b 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -479,3 +479,17 @@ TEST(compile_test, constexpr_string_format) { EXPECT_TRUE(big); } #endif // FMT_USE_CONSTEXPR_STRING + + + + +namespace { +struct CompileFormatAsType { + int value; +}; +int format_as(CompileFormatAsType f) { return f.value; } +} // namespace + +TEST(CompileTest, FormatAs) { + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), CompileFormatAsType{42})); +} \ No newline at end of file From b1ea40fe8c8693eee6b382017a789024900d8536 Mon Sep 17 00:00:00 2001 From: AMAN UPADHYAY Date: Sun, 5 Jul 2026 18:35:00 +0530 Subject: [PATCH 3/4] Refactor: use built-in use_format_as traits per review --- include/fmt/compile.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index a618e162..2c5a4323 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -175,6 +175,7 @@ struct compile_has_format_as : std::false_type {}; template struct compile_has_format_as()))>> : std::true_type {}; + template struct field { using char_type = Char; @@ -184,14 +185,21 @@ template struct field { if constexpr (std::is_convertible>::value) { auto s = basic_string_view(arg); return copy(s.begin(), s.end(), out); - } else if constexpr (compile_has_format_as::value) { + } else if constexpr (detail::use_format_as::value) { return write(out, format_as(arg)); + } else if constexpr (detail::use_format_as_member::value) { + return write(out, formatter::format_as(arg)); } else { return write(out, arg); } } }; + + + + + template struct is_compiled_format> : std::true_type {}; From 57011673d2908610505b9a768908a60e7d6ca515 Mon Sep 17 00:00:00 2001 From: AMAN UPADHYAY Date: Mon, 6 Jul 2026 00:10:41 +0530 Subject: [PATCH 4/4] Style: remove dead code, apply snake_case, and fix newline --- include/fmt/compile.h | 13 ------------- test/compile-test.cc | 14 +++++--------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 2c5a4323..838dd0c4 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -168,14 +168,6 @@ constexpr auto get_arg_checked(const Args&... args) -> const T& { template struct is_compiled_format> : std::true_type {}; - -template -struct compile_has_format_as : std::false_type {}; - -template -struct compile_has_format_as()))>> : std::true_type {}; - - template struct field { using char_type = Char; @@ -195,11 +187,6 @@ template struct field { } }; - - - - - template struct is_compiled_format> : std::true_type {}; diff --git a/test/compile-test.cc b/test/compile-test.cc index 94a7da1b..73e86f35 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -480,16 +480,12 @@ TEST(compile_test, constexpr_string_format) { } #endif // FMT_USE_CONSTEXPR_STRING - - - namespace { -struct CompileFormatAsType { +struct compile_format_as_type { int value; }; -int format_as(CompileFormatAsType f) { return f.value; } -} // namespace - -TEST(CompileTest, FormatAs) { - EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), CompileFormatAsType{42})); +int format_as(compile_format_as_type f) { return f.value; } +} +TEST(compile_test, format_as) { + EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), compile_format_as_type{42})); } \ No newline at end of file