Skip to content

Commit

Permalink
Add class name output to formatter for std::exception
Browse files Browse the repository at this point in the history
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
  • Loading branch information
phprus committed Sep 7, 2022
1 parent bac5395 commit 93b7d39
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 18 deletions.
76 changes: 70 additions & 6 deletions include/fmt/std.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
#ifndef FMT_STD_H_
#define FMT_STD_H_

#include <cstdlib>
#include <exception>
#include <memory>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>

#include "ostream.h"
Expand All @@ -28,6 +31,16 @@
# endif
#endif

#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_CXXABI_H 1
# endif
#endif
#ifndef FMT_HAS_CXXABI_H
# define FMT_HAS_CXXABI_H 0
#endif

#ifdef __cpp_lib_filesystem
FMT_BEGIN_NAMESPACE

Expand Down Expand Up @@ -170,12 +183,63 @@ FMT_BEGIN_NAMESPACE
template <typename T, typename Char>
struct formatter<
T, Char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type>
: formatter<string_view> {
template <typename FormatContext>
auto format(const std::exception& ex, FormatContext& ctx) const ->
typename FormatContext::iterator {
return fmt::formatter<string_view>::format(ex.what(), ctx);
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
formatter<basic_string_view<Char>, Char> underlying_;
bool print_typename_{false};

public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
print_typename_ = true;
}
if (*it == '}') return it;
if (*it != ':')
FMT_THROW(
format_error("no other top-level exception formatters supported"));
++it;
ctx.advance_to(it);
return underlying_.parse(ctx);
}

template <typename OutputIt>
auto format(const std::exception& ex,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
if (print_typename_ == false)
return underlying_.format(basic_string_view<Char>(ex.what()), ctx);

basic_memory_buffer<Char> msg;
const std::type_info& ti = typeid(ex);
#if FMT_HAS_CXXABI_H
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, decltype(std::free)*> demangled_name(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), std::free);
if (demangled_name) {
msg.append(string_view(demangled_name.get()));
} else {
msg.append(string_view(ti.name()));
}
#elif FMT_MSC_VERSION
string_view sv(ti.name());
if (sv.starts_with("class "))
sv.remove_prefix(6);
else if (sv.starts_with("struct "))
sv.remove_prefix(7);
msg.append(sv);
#else
msg.append(string_view(ti.name()));
#endif
msg.append(string_view(": "));
msg.append(string_view(ex.what()));

return underlying_.format(basic_string_view<Char>(msg.data(), msg.size()),
ctx);
}
};
FMT_END_NAMESPACE
Expand Down
45 changes: 33 additions & 12 deletions test/std-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,42 @@ TEST(std_test, variant) {
#endif
}

TEST(std_test, exception) {
std::string str("Test Exception");
std::string escstr = fmt::format("\"{}\"", str);

template <typename Catch> 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);
throw std::runtime_error("Test Exception");
} catch (const Catch& ex) {
EXPECT_EQ("Test Exception", fmt::format("{}", ex));
EXPECT_EQ("\"Test Exception\"", fmt::format("{::?}", ex));
EXPECT_EQ("std::runtime_error: Test Exception", fmt::format("{:t}", 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<std::exception>();
exception_test<std::runtime_error>();

try {
throw std::runtime_error(str);
} catch (const std::runtime_error& ex) {
EXPECT_EQ(fmt::format("{}", ex), str);
EXPECT_EQ(fmt::format("{:?}", ex), escstr);
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));
}
}

0 comments on commit 93b7d39

Please sign in to comment.