Skip to content

Commit a47b280

Browse files
committed
Add class name output to formatter for std::exception
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
1 parent 29c6000 commit a47b280

3 files changed

Lines changed: 82 additions & 13 deletions

File tree

include/fmt/core.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,18 @@ template <typename Char> class basic_string_view {
486486
size_ -= n;
487487
}
488488

489+
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
490+
basic_string_view<Char> sv) const noexcept {
491+
return size_ >= sv.size_ &&
492+
std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
493+
}
494+
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
495+
return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
496+
}
497+
FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
498+
return starts_with(basic_string_view<Char>(s));
499+
}
500+
489501
// Lexicographically compare this string reference to other.
490502
FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
491503
size_t str_size = size_ < other.size_ ? size_ : other.size_;

include/fmt/std.h

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
#ifndef FMT_STD_H_
99
#define FMT_STD_H_
1010

11+
#include <cstdlib>
1112
#include <exception>
13+
#include <memory>
1214
#include <thread>
1315
#include <type_traits>
16+
#include <typeinfo>
1417
#include <utility>
1518

1619
#include "ostream.h"
@@ -28,6 +31,16 @@
2831
# endif
2932
#endif
3033

34+
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
35+
# include <cxxabi.h>
36+
# ifndef __GABIXX_CXXABI_H__
37+
# define FMT_HAS_CXXABI_H 1
38+
# endif
39+
#endif
40+
#ifndef FMT_HAS_CXXABI_H
41+
# define FMT_HAS_CXXABI_H 0
42+
#endif
43+
3144
#ifdef __cpp_lib_filesystem
3245
FMT_BEGIN_NAMESPACE
3346

@@ -176,7 +189,33 @@ struct formatter<
176189
template <typename FormatContext>
177190
auto format(const std::exception& ex, FormatContext& ctx) const ->
178191
typename FormatContext::iterator {
179-
return fmt::formatter<string_view>::format(ex.what(), ctx);
192+
basic_memory_buffer<Char> msg;
193+
const std::type_info& ti = typeid(ex);
194+
#if FMT_HAS_CXXABI_H
195+
int status = 0;
196+
std::size_t size = 0;
197+
std::unique_ptr<char, decltype(std::free)*> demangled_name(
198+
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), std::free);
199+
if (demangled_name) {
200+
msg.append(string_view(demangled_name.get()));
201+
} else {
202+
msg.append(string_view(ti.name()));
203+
}
204+
#elif FMT_MSC_VERSION
205+
string_view sv(ti.name());
206+
if (sv.starts_with("class "))
207+
sv.remove_prefix(6);
208+
else if (sv.starts_with("struct "))
209+
sv.remove_prefix(7);
210+
msg.append(sv);
211+
#else
212+
msg.append(string_view(ti.name()));
213+
#endif
214+
msg.append(string_view(": "));
215+
msg.append(string_view(ex.what()));
216+
217+
return fmt::formatter<string_view>::format(
218+
basic_string_view<Char>(msg.data(), msg.size()), ctx);
180219
}
181220
};
182221
FMT_END_NAMESPACE

test/std-test.cc

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,39 @@ TEST(std_test, variant) {
8181
#endif
8282
}
8383

84-
TEST(std_test, exception) {
85-
std::string str("Test Exception");
86-
std::string escstr = fmt::format("\"{}\"", str);
87-
84+
template <typename Catch> void exception_test() {
8885
try {
89-
throw std::runtime_error(str);
90-
} catch (const std::exception& ex) {
91-
EXPECT_EQ(fmt::format("{}", ex), str);
92-
EXPECT_EQ(fmt::format("{:?}", ex), escstr);
86+
throw std::runtime_error("Test Exception");
87+
} catch (const Catch& ex) {
88+
EXPECT_EQ("std::runtime_error: Test Exception", fmt::format("{}", ex));
89+
EXPECT_EQ("\"std::runtime_error: Test Exception\"",
90+
fmt::format("{:?}", ex));
9391
}
92+
}
93+
94+
namespace my_ns1 {
95+
namespace my_ns2 {
96+
struct my_exception : public std::exception {
97+
private:
98+
std::string msg;
99+
100+
public:
101+
my_exception(const std::string& s) : msg(s) {}
102+
const char* what() const noexcept override;
103+
};
104+
const char* my_exception::what() const noexcept { return msg.c_str(); }
105+
} // namespace my_ns2
106+
} // namespace my_ns1
107+
108+
TEST(std_test, exception) {
109+
exception_test<std::exception>();
110+
exception_test<std::runtime_error>();
94111

95112
try {
96-
throw std::runtime_error(str);
97-
} catch (const std::runtime_error& ex) {
98-
EXPECT_EQ(fmt::format("{}", ex), str);
99-
EXPECT_EQ(fmt::format("{:?}", ex), escstr);
113+
using namespace my_ns1::my_ns2;
114+
throw my_exception("My Exception");
115+
} catch (const std::exception& ex) {
116+
EXPECT_EQ("my_ns1::my_ns2::my_exception: My Exception",
117+
fmt::format("{}", ex));
100118
}
101119
}

0 commit comments

Comments
 (0)