diff --git a/include/fmt/std.h b/include/fmt/std.h index 63dbebe3..02ca6721 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -8,9 +8,12 @@ #ifndef FMT_STD_H_ #define FMT_STD_H_ +#include #include +#include #include #include +#include #include #include "ostream.h" @@ -28,6 +31,16 @@ # endif #endif +// GCC 4 does not support FMT_HAS_INCLUDE. +#if FMT_HAS_INCLUDE() || defined(__GLIBCXX__) +# include +// Android NDK with gabi++ library on some archtectures does not implement +// abi::__cxa_demangle(). +# ifndef __GABIXX_CXXABI_H__ +# define FMT_HAS_ABI_CXA_DEMANGLE +# endif +#endif + #ifdef __cpp_lib_filesystem FMT_BEGIN_NAMESPACE @@ -170,12 +183,56 @@ FMT_BEGIN_NAMESPACE template struct formatter< T, Char, - typename std::enable_if::value>::type> - : formatter { - template - auto format(const std::exception& ex, FormatContext& ctx) const -> - typename FormatContext::iterator { - return fmt::formatter::format(ex.what(), ctx); + typename std::enable_if::value>::type> { + private: + bool with_typename_ = false; + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto it = ctx.begin(); + auto end = ctx.end(); + if (it == end || *it == '}') return it; + if (*it == 't') { + ++it; + with_typename_ = true; + } + return it; + } + + template + auto format(const std::exception& ex, + basic_format_context& ctx) const -> OutputIt { + basic_format_specs spec; + auto out = ctx.out(); + if (!with_typename_) + return detail::write_bytes(out, string_view(ex.what()), spec); + + const std::type_info& ti = typeid(ex); +#ifdef FMT_HAS_ABI_CXA_DEMANGLE + int status = 0; + std::size_t size = 0; + std::unique_ptr demangled_name_ptr( + abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); + out = detail::write_bytes( + out, + string_view(demangled_name_ptr ? demangled_name_ptr.get() : ti.name()), + spec); +#elif FMT_MSC_VERSION + string_view demangled_name_view(ti.name()); + if (demangled_name_view.starts_with("class ")) + demangled_name_view.remove_prefix(6); + else if (demangled_name_view.starts_with("struct ")) + demangled_name_view.remove_prefix(7); + out = detail::write_bytes(out, demangled_name_view, spec); +#else + out = detail::write_bytes(out, string_view(ti.name()), spec); +#endif + out = detail::write(out, Char(':')); + out = detail::write(out, Char(' ')); + out = detail::write_bytes(out, string_view(ex.what()), spec); + + return out; } }; FMT_END_NAMESPACE diff --git a/test/std-test.cc b/test/std-test.cc index 77cf7934..0f6bf11a 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -81,21 +81,39 @@ TEST(std_test, variant) { #endif } -TEST(std_test, exception) { - std::string str("Test Exception"); - std::string escstr = fmt::format("\"{}\"", str); - +template void exception_test() { try { - throw std::runtime_error(str); - } catch (const std::exception& ex) { - EXPECT_EQ(fmt::format("{}", ex), str); - EXPECT_EQ(fmt::format("{:?}", ex), escstr); - } - - try { - throw std::runtime_error(str); - } catch (const std::runtime_error& ex) { - EXPECT_EQ(fmt::format("{}", ex), str); - EXPECT_EQ(fmt::format("{:?}", ex), escstr); + throw std::runtime_error("Test Exception"); + } catch (const Catch& ex) { + EXPECT_EQ("Test Exception", fmt::format("{}", ex)); + EXPECT_EQ("std::runtime_error: Test Exception", fmt::format("{:t}", ex)); + } +} + +namespace my_ns1 { +namespace my_ns2 { +struct my_exception : public std::exception { + private: + std::string msg; + + public: + my_exception(const std::string& s) : msg(s) {} + const char* what() const noexcept override; +}; +const char* my_exception::what() const noexcept { return msg.c_str(); } +} // namespace my_ns2 +} // namespace my_ns1 + +TEST(std_test, exception) { + exception_test(); + exception_test(); + + try { + using namespace my_ns1::my_ns2; + throw my_exception("My Exception"); + } catch (const std::exception& ex) { + EXPECT_EQ("my_ns1::my_ns2::my_exception: My Exception", + fmt::format("{:t}", ex)); + EXPECT_EQ("My Exception", fmt::format("{:}", ex)); } }