mirror of
https://github.com/fmtlib/fmt.git
synced 2026-07-30 16:26:27 +08:00
Compare commits
4 Commits
a87ae0a6a8
...
630aafeb70
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
630aafeb70 | ||
|
|
17500e457b | ||
|
|
26c01df3bd | ||
|
|
b059132e13 |
15
.github/workflows/linux.yml
vendored
15
.github/workflows/linux.yml
vendored
@ -13,6 +13,10 @@ jobs:
|
||||
${{matrix.shared && 'Shared' || ''}}
|
||||
${{matrix.cxxflags_extra && 'Sanitize' || ''}}
|
||||
runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
|
||||
env:
|
||||
# Run tests in a non-UTC timezone. glibc localtime/strftime honor TZ, so
|
||||
# there is no need for timedatectl, which fails without systemd-timedated.
|
||||
TZ: Europe/Kyiv
|
||||
strategy:
|
||||
matrix:
|
||||
cxx: [g++-4.9, g++-11, clang++-3.6, clang++-11]
|
||||
@ -75,15 +79,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Set timezone
|
||||
run: sudo timedatectl set-timezone 'Europe/Kyiv'
|
||||
|
||||
- name: Install GCC 4.9
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libatomic1 libc6-dev libgomp1 libitm1 libmpc3
|
||||
# https://launchpad.net/ubuntu/xenial/amd64/g++-4.9/4.9.3-13ubuntu2
|
||||
wget --no-verbose \
|
||||
# launchpad periodically returns 503s, so retry to avoid flaky CI.
|
||||
wget --no-verbose --tries=5 --waitretry=10 \
|
||||
--retry-connrefused --retry-on-http-error=503 \
|
||||
http://launchpadlibrarian.net/230069137/libmpfr4_3.1.3-2_amd64.deb \
|
||||
http://launchpadlibrarian.net/253728424/libasan1_4.9.3-13ubuntu2_amd64.deb \
|
||||
http://launchpadlibrarian.net/445346135/libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
|
||||
@ -114,7 +117,9 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt install libtinfo5
|
||||
# https://code.launchpad.net/ubuntu/xenial/amd64/clang-3.6/1:3.6.2-3ubuntu2
|
||||
wget --no-verbose \
|
||||
# launchpad periodically returns 503s, so retry to avoid flaky CI.
|
||||
wget --no-verbose --tries=5 --waitretry=10 \
|
||||
--retry-connrefused --retry-on-http-error=503 \
|
||||
http://launchpadlibrarian.net/230019046/libffi6_3.2.1-4_amd64.deb \
|
||||
http://launchpadlibrarian.net/445346109/libasan2_5.4.0-6ubuntu1~16.04.12_amd64.deb \
|
||||
http://launchpadlibrarian.net/445346135/libubsan0_5.4.0-6ubuntu1~16.04.12_amd64.deb \
|
||||
|
||||
@ -92,6 +92,10 @@ Use `format_as` if you want to make your type formattable as some other
|
||||
type with the same format specifiers. The `format_as` function should
|
||||
take an object of your type and return an object of a formattable type.
|
||||
It should be defined in the same namespace as your type.
|
||||
`format_as` cannot be used when a type also matches another `formatter`
|
||||
specialization, such as the range `formatter`, because the specializations
|
||||
would be ambiguous. Disable the conflicting specialization, if possible,
|
||||
or provide an explicit `formatter` specialization instead.
|
||||
|
||||
Example ([run](https://godbolt.org/z/nvME4arz8)):
|
||||
|
||||
|
||||
@ -1059,6 +1059,10 @@ template <typename T> struct is_static_named_arg : std::false_type {};
|
||||
template <typename T, typename Char>
|
||||
struct is_named_arg<named_arg<T, Char>> : std::true_type {};
|
||||
|
||||
template <typename T> struct is_named_arg<const T> : is_named_arg<T> {};
|
||||
template <typename T>
|
||||
struct is_static_named_arg<const T> : is_static_named_arg<T> {};
|
||||
|
||||
template <typename T, typename Char = char> struct named_arg : view {
|
||||
const Char* name;
|
||||
const T& value;
|
||||
|
||||
@ -620,6 +620,8 @@ 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:
|
||||
@ -627,7 +629,14 @@ struct formatter<
|
||||
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;
|
||||
}
|
||||
@ -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:
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -163,6 +163,10 @@ TEST(compile_test, named) {
|
||||
fmt::format(FMT_COMPILE("{a0} {a1}"), "a0"_a = 41, "a1"_a = 43));
|
||||
EXPECT_EQ("41 43",
|
||||
fmt::format(FMT_COMPILE("{a1} {a0}"), "a0"_a = 43, "a1"_a = 41));
|
||||
|
||||
// A statically-named argument with a format spec compiles to spec_field,
|
||||
// which passes the argument to make_format_args as const (#4866).
|
||||
EXPECT_EQ("4.2", fmt::format(FMT_COMPILE("{arg:3.1f}"), "arg"_a = 4.2));
|
||||
# endif
|
||||
}
|
||||
|
||||
|
||||
@ -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)),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user