Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions include/fmt/std.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,14 +620,23 @@ struct formatter<
T, char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
format_specs specs_;
detail::arg_ref<char> width_ref_;
bool with_typename_ = false;

public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {

it = detail::parse_align(it, end, specs_);
if (it == end) return it;

char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
Expand All @@ -637,7 +646,20 @@ struct formatter<
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
return write(ctx.out(), ex);
auto buf = memory_buffer();
write(appender(buf), ex);
return write_padded(ctx, string_view(buf.data(), buf.size()));
}

protected:
// Applies the parsed fill/align/width to an already-formatted message.
template <typename Context>
auto write_padded(Context& ctx, string_view message) const
-> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
return detail::write(ctx.out(), message, specs);
}

private:
Expand Down Expand Up @@ -675,13 +697,13 @@ template <> struct formatter<std::exception_ptr> : formatter<std::exception> {
template <typename FormatContext>
auto format(const std::exception_ptr& ep, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!ep) return detail::write(ctx.out(), string_view("none"));
if (!ep) return this->write_padded(ctx, string_view("none"));
try {
std::rethrow_exception(ep);
} catch (const std::exception& e) {
return formatter<std::exception>::format(e, ctx);
} catch (...) {
return detail::write(ctx.out(), string_view("unknown exception"));
return this->write_padded(ctx, string_view("unknown exception"));
}
}
};
Expand Down
37 changes: 37 additions & 0 deletions test/std-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,43 @@ TEST(std_test, exception_ptr) {
#endif // FMT_USE_RTTI
}

TEST(std_test, exception_align) {
// The exception formatter supports standard width, fill and alignment.
auto ex = std::runtime_error("boom");
EXPECT_EQ(fmt::format("{:>8}", ex), " boom");
EXPECT_EQ(fmt::format("{:<8}", ex), "boom ");
EXPECT_EQ(fmt::format("{:^8}", ex), " boom ");
EXPECT_EQ(fmt::format("{:*>8}", ex), "****boom");
EXPECT_EQ(fmt::format("{:{}}", ex, 8), "boom ");
// A width smaller than the message leaves the message untouched.
EXPECT_EQ(fmt::format("{:>2}", ex), "boom");

#if FMT_USE_RTTI
// Alignment combines with the 't' (type name) presentation type.
try {
using namespace my_ns1::my_ns2;
throw my_exception("oops");
} catch (const std::exception& e) {
auto base = fmt::format("{:t}", e);
auto padded = fmt::format("{:*<40t}", e);
EXPECT_EQ(padded.size(), 40u);
EXPECT_EQ(padded.substr(0, base.size()), base);
EXPECT_EQ(padded.substr(base.size()), std::string(40 - base.size(), '*'));
}
#endif // FMT_USE_RTTI

// exception_ptr honors the same specifiers, for both the empty ("none")
// and the non-empty case.
std::exception_ptr enull = nullptr;
EXPECT_EQ(fmt::format("{:>8}", enull), " none");
std::exception_ptr ep;
try {
throw std::runtime_error("bang");
} catch (...) {
ep = std::current_exception();
}
EXPECT_EQ(fmt::format("{:>8}", ep), " bang");
}
#if FMT_USE_RTTI
TEST(std_test, type_info) {
EXPECT_EQ(fmt::format("{}", typeid(std::runtime_error)),
Expand Down