mirror of
https://github.com/fmtlib/fmt.git
synced 2026-07-30 16:26:27 +08:00
The formatter for std::exception (and std::exception_ptr) previously
accepted only the optional 't' type-name specifier, so standard fill,
align and width specifiers were rejected with "unknown format
specifier". This made it impossible to pad or align exception messages,
e.g. when laying them out in a log column.
Parse the standard fill/align/width specifiers (as the std::filesystem::
path formatter already does) and apply them when writing the message.
Dynamic width ({:{}}) and the 'none'/'unknown exception' cases for
exception_ptr are handled too. Existing behavior ({}, {:t}, nested
exception unwinding) is unchanged.
645 lines
22 KiB
C++
645 lines
22 KiB
C++
// Formatting library for C++ - tests of formatters for standard library types
|
|
//
|
|
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
|
|
// All rights reserved.
|
|
//
|
|
// For the license information refer to format.h.
|
|
|
|
#include "fmt/std.h"
|
|
|
|
#include <bitset>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "fmt/os.h" // fmt::system_category
|
|
#include "fmt/ranges.h"
|
|
#include "gtest-extra.h" // StartsWith
|
|
|
|
#ifdef __cpp_lib_filesystem
|
|
TEST(std_test, path) {
|
|
using std::filesystem::path;
|
|
EXPECT_EQ(fmt::format("{}", path("/usr/bin")), "/usr/bin");
|
|
|
|
// see #4303
|
|
const path p = "/usr/bin";
|
|
EXPECT_EQ(fmt::format("{}", p), "/usr/bin");
|
|
|
|
EXPECT_EQ(fmt::format("{:?}", path("/usr/bin")), "\"/usr/bin\"");
|
|
EXPECT_EQ(fmt::format("{:8}", path("foo")), "foo ");
|
|
|
|
EXPECT_EQ(fmt::format("{}", path("foo\"bar")), "foo\"bar");
|
|
EXPECT_EQ(fmt::format("{:?}", path("foo\"bar")), "\"foo\\\"bar\"");
|
|
|
|
EXPECT_EQ(fmt::format("{:g}", path("/usr/bin")), "/usr/bin");
|
|
# ifdef _WIN32
|
|
EXPECT_EQ(fmt::format("{}", path("C:\\foo")), "C:\\foo");
|
|
EXPECT_EQ(fmt::format("{:g}", path("C:\\foo")), "C:/foo");
|
|
|
|
EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
|
|
L"\x0447\x044B\x043D\x0430")),
|
|
"Шчучыншчына");
|
|
EXPECT_EQ(fmt::format("{}", path(L"\xD800")), "\xED\xA0\x80");
|
|
EXPECT_EQ(fmt::format("{}", path(L"[\xD800]")), "[\xED\xA0\x80]");
|
|
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xDE00]")), "[\xF0\x9F\x98\x80]");
|
|
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xD83D\xDE00]")),
|
|
"[\xED\xA0\xBD\xF0\x9F\x98\x80]");
|
|
EXPECT_EQ(fmt::format("{:?}", path(L"\xD800")), "\"\\ud800\"");
|
|
# endif
|
|
}
|
|
|
|
// Intentionally delayed include to test #4303
|
|
# include "fmt/ranges.h"
|
|
|
|
// Test ambiguity problem described in #2954.
|
|
TEST(ranges_std_test, format_vector_path) {
|
|
auto p = std::filesystem::path("foo/bar.txt");
|
|
auto c = std::vector<std::string>{"abc", "def"};
|
|
EXPECT_EQ(fmt::format("path={}, range={}", p, c),
|
|
"path=foo/bar.txt, range=[\"abc\", \"def\"]");
|
|
}
|
|
|
|
// Test that path is not escaped twice in the debug mode.
|
|
TEST(ranges_std_test, format_quote_path) {
|
|
auto vec =
|
|
std::vector<std::filesystem::path>{"path1/file1.txt", "path2/file2.txt"};
|
|
EXPECT_EQ(fmt::format("{}", vec),
|
|
"[\"path1/file1.txt\", \"path2/file2.txt\"]");
|
|
# ifdef __cpp_lib_optional
|
|
auto o = std::optional<std::filesystem::path>("path/file.txt");
|
|
EXPECT_EQ(fmt::format("{}", o), "optional(\"path/file.txt\")");
|
|
EXPECT_EQ(fmt::format("{:?}", o), "optional(\"path/file.txt\")");
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
TEST(std_test, thread_id) {
|
|
EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty());
|
|
}
|
|
|
|
TEST(std_test, complex) {
|
|
using limits = std::numeric_limits<double>;
|
|
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, limits::quiet_NaN())),
|
|
"(1+nan i)");
|
|
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, -limits::infinity())),
|
|
"(1-inf i)");
|
|
|
|
EXPECT_EQ(fmt::format("{}", std::complex<int>(1, 2)), "(1+2i)");
|
|
|
|
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, 2.2)), "(1+2.2i)");
|
|
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, -2.2)), "(1-2.2i)");
|
|
EXPECT_EQ(fmt::format("{}", std::complex<double>(0, 2.2)), "2.2i");
|
|
EXPECT_EQ(fmt::format("{}", std::complex<double>(0, -2.2)), "-2.2i");
|
|
|
|
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(0, 2.2)), "+2.2i");
|
|
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(0, -2.2)), "-2.2i");
|
|
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(1, -2.2)), "(+1-2.2i)");
|
|
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(1, 2.2)), "(+1+2.2i)");
|
|
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, 2.2)), "( 1+2.2i)");
|
|
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, -2.2)), "( 1-2.2i)");
|
|
|
|
EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), "(1+2i) ");
|
|
EXPECT_EQ(fmt::format("{:-<8}", std::complex<double>(1, 2)), "(1+2i)--");
|
|
|
|
EXPECT_EQ(fmt::format("{:>20.2f}", std::complex<double>(1, 2.2)),
|
|
" (1.00+2.20i)");
|
|
EXPECT_EQ(fmt::format("{:<20.2f}", std::complex<double>(1, 2.2)),
|
|
"(1.00+2.20i) ");
|
|
EXPECT_EQ(fmt::format("{:<20.2f}", std::complex<double>(1, -2.2)),
|
|
"(1.00-2.20i) ");
|
|
EXPECT_EQ(fmt::format("{:<{}.{}f}", std::complex<double>(1, -2.2), 20, 2),
|
|
"(1.00-2.20i) ");
|
|
}
|
|
|
|
#ifdef __cpp_lib_source_location
|
|
TEST(std_test, source_location) {
|
|
std::source_location loc = std::source_location::current();
|
|
EXPECT_EQ(fmt::format("{}", loc),
|
|
fmt::format("{}:{}:{}: {}", loc.file_name(), loc.line(),
|
|
loc.column(), loc.function_name()));
|
|
}
|
|
#endif
|
|
|
|
TEST(std_test, optional) {
|
|
#ifdef __cpp_lib_optional
|
|
EXPECT_EQ(fmt::format("{}", std::optional<int>{}), "none");
|
|
EXPECT_EQ(fmt::format("{}", std::pair{1, "second"}), "(1, \"second\")");
|
|
EXPECT_EQ(fmt::format("{}", std::vector{std::optional{1}, std::optional{2},
|
|
std::optional{3}}),
|
|
"[optional(1), optional(2), optional(3)]");
|
|
EXPECT_EQ(
|
|
fmt::format("{}", std::optional<std::optional<const char*>>{{"nested"}}),
|
|
"optional(optional(\"nested\"))");
|
|
EXPECT_EQ(
|
|
fmt::format("{:<{}}", std::optional{std::string{"left aligned"}}, 30),
|
|
"optional(\"left aligned\" )");
|
|
EXPECT_EQ(
|
|
fmt::format("{::d}", std::optional{std::vector{'h', 'e', 'l', 'l', 'o'}}),
|
|
"optional([104, 101, 108, 108, 111])");
|
|
EXPECT_EQ(fmt::format("{}", std::optional{std::string{"string"}}),
|
|
"optional(\"string\")");
|
|
EXPECT_EQ(fmt::format("{}", std::optional{'C'}), "optional(\'C\')");
|
|
EXPECT_EQ(fmt::format("{:.{}f}", std::optional{3.14}, 1), "optional(3.1)");
|
|
|
|
struct unformattable {};
|
|
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
|
EXPECT_FALSE((fmt::is_formattable<std::optional<unformattable>>::value));
|
|
EXPECT_TRUE((fmt::is_formattable<std::optional<int>>::value));
|
|
EXPECT_TRUE((fmt::is_formattable<std::optional<const int>>::value));
|
|
#endif
|
|
}
|
|
|
|
TEST(std_test, expected) {
|
|
#ifdef __cpp_lib_expected
|
|
EXPECT_EQ(fmt::format("{}", std::expected<void, int>{}), "expected()");
|
|
EXPECT_EQ(fmt::format("{}", std::expected<int, int>{1}), "expected(1)");
|
|
EXPECT_EQ(fmt::format("{}", std::expected<int, int>{std::unexpected(1)}),
|
|
"unexpected(1)");
|
|
EXPECT_EQ(fmt::format("{}", std::expected<std::string, int>{"test"}),
|
|
"expected(\"test\")");
|
|
EXPECT_EQ(fmt::format(
|
|
"{}", std::expected<int, std::string>{std::unexpected("test")}),
|
|
"unexpected(\"test\")");
|
|
EXPECT_EQ(fmt::format("{}", std::expected<char, int>{'a'}), "expected('a')");
|
|
EXPECT_EQ(fmt::format("{}", std::expected<int, char>{std::unexpected('a')}),
|
|
"unexpected('a')");
|
|
|
|
struct unformattable1 {};
|
|
struct unformattable2 {};
|
|
EXPECT_FALSE((fmt::is_formattable<unformattable1>::value));
|
|
EXPECT_FALSE((fmt::is_formattable<unformattable2>::value));
|
|
EXPECT_FALSE((fmt::is_formattable<
|
|
std::expected<unformattable1, unformattable2>>::value));
|
|
EXPECT_FALSE(
|
|
(fmt::is_formattable<std::expected<unformattable1, int>>::value));
|
|
EXPECT_FALSE(
|
|
(fmt::is_formattable<std::expected<int, unformattable2>>::value));
|
|
EXPECT_TRUE((fmt::is_formattable<std::expected<int, int>>::value));
|
|
EXPECT_TRUE((fmt::is_formattable<std::expected<void, int>>::value));
|
|
|
|
EXPECT_EQ(fmt::format("{}", std::unexpected{1}), "unexpected(1)");
|
|
EXPECT_EQ(fmt::format("{}", std::unexpected<std::string>{"test"}),
|
|
"unexpected(\"test\")");
|
|
|
|
EXPECT_EQ(fmt::format("{}", std::unexpected<char>{'a'}), "unexpected('a')");
|
|
|
|
EXPECT_FALSE((fmt::is_formattable<std::unexpected<unformattable2>>::value));
|
|
#endif
|
|
}
|
|
|
|
namespace my_nso {
|
|
enum class my_number {
|
|
one,
|
|
two,
|
|
};
|
|
auto format_as(my_number number) -> fmt::string_view {
|
|
return number == my_number::one ? "first" : "second";
|
|
}
|
|
|
|
class my_class {
|
|
public:
|
|
int av;
|
|
|
|
private:
|
|
friend auto format_as(const my_class& elm) -> std::string {
|
|
return fmt::to_string(elm.av);
|
|
}
|
|
};
|
|
|
|
class my_class_int {
|
|
public:
|
|
int av;
|
|
|
|
private:
|
|
friend auto format_as(const my_class_int& elm) -> int { return elm.av; }
|
|
};
|
|
} // namespace my_nso
|
|
|
|
TEST(std_test, expected_format_as) {
|
|
#ifdef __cpp_lib_expected
|
|
EXPECT_EQ(
|
|
fmt::format(
|
|
"{}", std::expected<my_nso::my_number, int>{my_nso::my_number::one}),
|
|
"expected(\"first\")");
|
|
EXPECT_EQ(
|
|
fmt::format("{}",
|
|
std::expected<my_nso::my_class, int>{my_nso::my_class{7}}),
|
|
"expected(\"7\")");
|
|
EXPECT_EQ(fmt::format("{}",
|
|
std::expected<my_nso::my_class_int, int>{
|
|
my_nso::my_class_int{8}}),
|
|
"expected(8)");
|
|
#endif
|
|
}
|
|
|
|
TEST(std_test, optional_format_as) {
|
|
#ifdef __cpp_lib_optional
|
|
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_number>{}), "none");
|
|
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_number::one}),
|
|
"optional(\"first\")");
|
|
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_class>{}), "none");
|
|
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}),
|
|
"optional(\"7\")");
|
|
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class_int{8}}),
|
|
"optional(8)");
|
|
#endif
|
|
}
|
|
|
|
struct throws_on_move {
|
|
throws_on_move() = default;
|
|
|
|
[[noreturn]] throws_on_move(throws_on_move&&) {
|
|
throw std::runtime_error("Thrown by throws_on_move");
|
|
}
|
|
|
|
throws_on_move(const throws_on_move&) = default;
|
|
};
|
|
|
|
namespace fmt {
|
|
template <> struct formatter<throws_on_move> : formatter<string_view> {
|
|
auto format(const throws_on_move&, format_context& ctx) const
|
|
-> decltype(ctx.out()) {
|
|
string_view str("<throws_on_move>");
|
|
return formatter<string_view>::format(str, ctx);
|
|
}
|
|
};
|
|
} // namespace fmt
|
|
|
|
TEST(std_test, variant) {
|
|
#ifdef __cpp_lib_variant
|
|
EXPECT_EQ(fmt::format("{}", std::monostate{}), "monostate");
|
|
using V0 = std::variant<int, float, std::string, char>;
|
|
V0 v0(42);
|
|
V0 v1(1.5f);
|
|
V0 v2("hello");
|
|
V0 v3('i');
|
|
EXPECT_EQ(fmt::format("{}", v0), "variant(42)");
|
|
EXPECT_EQ(fmt::format("{}", v1), "variant(1.5)");
|
|
EXPECT_EQ(fmt::format("{}", v2), "variant(\"hello\")");
|
|
EXPECT_EQ(fmt::format("{}", v3), "variant('i')");
|
|
|
|
struct unformattable {};
|
|
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
|
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable>>::value));
|
|
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable, int>>::value));
|
|
EXPECT_FALSE((fmt::is_formattable<std::variant<int, unformattable>>::value));
|
|
EXPECT_FALSE(
|
|
(fmt::is_formattable<std::variant<unformattable, unformattable>>::value));
|
|
EXPECT_TRUE((fmt::is_formattable<std::variant<int, float>>::value));
|
|
|
|
using V1 = std::variant<std::monostate, std::string, std::string>;
|
|
V1 v4{};
|
|
V1 v5{std::in_place_index<1>, "yes, this is variant"};
|
|
|
|
EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)");
|
|
EXPECT_EQ(fmt::format("{}", v5), "variant(\"yes, this is variant\")");
|
|
|
|
volatile int i = 42; // Test compile error before GCC 11 described in #3068.
|
|
EXPECT_EQ(fmt::format("{}", i), "42");
|
|
|
|
std::variant<std::monostate, throws_on_move> v6;
|
|
|
|
try {
|
|
throws_on_move thrower;
|
|
v6.emplace<throws_on_move>(std::move(thrower));
|
|
} catch (const std::runtime_error&) {
|
|
}
|
|
// v6 is now valueless by exception
|
|
|
|
EXPECT_EQ(fmt::format("{}", v6), "variant(valueless by exception)");
|
|
|
|
#endif
|
|
}
|
|
|
|
TEST(std_test, variant_format_as) {
|
|
#ifdef __cpp_lib_variant
|
|
|
|
EXPECT_EQ(fmt::format("{}", std::variant<my_nso::my_number>{}),
|
|
"variant(\"first\")");
|
|
EXPECT_EQ(fmt::format(
|
|
"{}", std::variant<my_nso::my_number>{my_nso::my_number::one}),
|
|
"variant(\"first\")");
|
|
EXPECT_EQ(
|
|
fmt::format("{}", std::variant<my_nso::my_class>{my_nso::my_class{7}}),
|
|
"variant(\"7\")");
|
|
EXPECT_EQ(
|
|
fmt::format("{}",
|
|
std::variant<my_nso::my_class_int>{my_nso::my_class_int{8}}),
|
|
"variant(8)");
|
|
#endif
|
|
}
|
|
|
|
TEST(std_test, error_code) {
|
|
auto& generic = std::generic_category();
|
|
EXPECT_EQ(fmt::format("{}", std::error_code(42, generic)), "generic:42");
|
|
EXPECT_EQ(fmt::format("{:>12}", std::error_code(42, generic)),
|
|
" generic:42");
|
|
EXPECT_EQ(fmt::format("{:12}", std::error_code(42, generic)), "generic:42 ");
|
|
EXPECT_EQ(fmt::format("{}", std::error_code(42, fmt::system_category())),
|
|
"system:42");
|
|
EXPECT_EQ(fmt::format("{}", std::error_code(-42, fmt::system_category())),
|
|
"system:-42");
|
|
auto ec = std::make_error_code(std::errc::value_too_large);
|
|
EXPECT_EQ(fmt::format("{:s}", ec), ec.message());
|
|
EXPECT_EQ(fmt::format("{:?}", std::error_code(42, generic)),
|
|
"\"generic:42\"");
|
|
EXPECT_EQ(fmt::format("{}",
|
|
std::map<std::error_code, int>{
|
|
{std::error_code(42, generic), 0}}),
|
|
"{\"generic:42\": 0}");
|
|
}
|
|
|
|
template <typename Catch> void exception_test() {
|
|
try {
|
|
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) {
|
|
using testing::StartsWith;
|
|
exception_test<std::exception>();
|
|
exception_test<std::runtime_error>();
|
|
|
|
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));
|
|
}
|
|
|
|
try {
|
|
throw std::system_error(std::error_code(), "message");
|
|
} catch (const std::system_error& ex) {
|
|
EXPECT_THAT(fmt::format("{:t}", ex), StartsWith("std::system_error: "));
|
|
}
|
|
|
|
#ifdef __cpp_lib_filesystem
|
|
// Tests that the inline namespace is stripped out, e.g.
|
|
// std::filesystem::__cxx11::* -> std::filesystem::*.
|
|
try {
|
|
throw std::filesystem::filesystem_error("message", std::error_code());
|
|
} catch (const std::filesystem::filesystem_error& ex) {
|
|
EXPECT_THAT(fmt::format("{:t}", ex),
|
|
StartsWith("std::filesystem::filesystem_error: "));
|
|
}
|
|
#endif
|
|
|
|
#if FMT_USE_RTTI
|
|
// Nested exceptions (e.g. from std::throw_with_nested) are unwound.
|
|
try {
|
|
try {
|
|
throw std::runtime_error("inner");
|
|
} catch (...) {
|
|
std::throw_with_nested(std::runtime_error("outer"));
|
|
}
|
|
} catch (const std::exception& ex) {
|
|
EXPECT_EQ("outer: inner", fmt::format("{}", ex));
|
|
}
|
|
|
|
// Multiple levels of nesting.
|
|
try {
|
|
try {
|
|
try {
|
|
throw std::runtime_error("level 3");
|
|
} catch (...) {
|
|
std::throw_with_nested(std::runtime_error("level 2"));
|
|
}
|
|
} catch (...) {
|
|
std::throw_with_nested(std::runtime_error("level 1"));
|
|
}
|
|
} catch (const std::exception& ex) {
|
|
EXPECT_EQ("level 1: level 2: level 3", fmt::format("{}", ex));
|
|
}
|
|
#endif // FMT_USE_RTTI
|
|
}
|
|
|
|
TEST(std_test, exception_ptr) {
|
|
std::exception_ptr p1 = nullptr;
|
|
std::exception_ptr p2;
|
|
|
|
try {
|
|
using namespace my_ns1::my_ns2;
|
|
throw my_exception("My Exception");
|
|
} catch (...) {
|
|
p2 = std::current_exception();
|
|
}
|
|
|
|
EXPECT_EQ(fmt::format("{}", p1), "none");
|
|
EXPECT_EQ(fmt::format("{}", p2), "My Exception");
|
|
|
|
#if FMT_USE_RTTI
|
|
EXPECT_EQ(fmt::format("{:t}", p2),
|
|
"my_ns1::my_ns2::my_exception: My Exception");
|
|
|
|
// Nested exceptions are unwound through an exception_ptr too.
|
|
std::exception_ptr p3;
|
|
try {
|
|
try {
|
|
throw std::runtime_error("inner");
|
|
} catch (...) {
|
|
std::throw_with_nested(std::runtime_error("outer"));
|
|
}
|
|
} catch (...) {
|
|
p3 = std::current_exception();
|
|
}
|
|
EXPECT_EQ(fmt::format("{}", p3), "outer: inner");
|
|
#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)),
|
|
"std::runtime_error");
|
|
}
|
|
#endif // FMT_USE_RTTI
|
|
|
|
#if FMT_USE_BITINT
|
|
FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
|
|
|
|
TEST(std_test, bitint) {
|
|
using fmt::detail::bitint;
|
|
using fmt::detail::ubitint;
|
|
|
|
EXPECT_EQ(fmt::format("{}", ubitint<3>(7)), "7");
|
|
EXPECT_EQ(fmt::format("{}", bitint<7>()), "0");
|
|
|
|
EXPECT_EQ(fmt::format("{}", ubitint<15>(31000)), "31000");
|
|
EXPECT_EQ(fmt::format("{}", bitint<16>(INT16_MIN)), "-32768");
|
|
EXPECT_EQ(fmt::format("{}", bitint<16>(INT16_MAX)), "32767");
|
|
|
|
EXPECT_EQ(fmt::format("{}", ubitint<32>(4294967295)), "4294967295");
|
|
|
|
EXPECT_EQ(fmt::format("{}", ubitint<47>(140737488355327ULL)),
|
|
"140737488355327");
|
|
EXPECT_EQ(fmt::format("{}", bitint<47>(-40737488355327LL)),
|
|
"-40737488355327");
|
|
|
|
// Check lvalues and const
|
|
auto a = bitint<8>(0);
|
|
auto b = ubitint<32>(4294967295);
|
|
const auto c = bitint<7>(0);
|
|
const auto d = ubitint<32>(4294967295);
|
|
EXPECT_EQ(fmt::format("{}", a), "0");
|
|
EXPECT_EQ(fmt::format("{}", b), "4294967295");
|
|
EXPECT_EQ(fmt::format("{}", c), "0");
|
|
EXPECT_EQ(fmt::format("{}", d), "4294967295");
|
|
|
|
static_assert(fmt::is_formattable<bitint<64>, char>{}, "");
|
|
static_assert(fmt::is_formattable<ubitint<64>, char>{}, "");
|
|
}
|
|
#endif
|
|
|
|
TEST(std_test, format_bit_reference) {
|
|
std::bitset<2> bs(1);
|
|
EXPECT_EQ(fmt::format("{} {}", bs[0], bs[1]), "true false");
|
|
std::vector<bool> v = {true, false};
|
|
EXPECT_EQ(fmt::format("{} {}", v[0], v[1]), "true false");
|
|
}
|
|
|
|
TEST(std_test, format_const_bit_reference) {
|
|
const std::bitset<2> bs(1);
|
|
EXPECT_EQ(fmt::format("{} {}", bs[0], bs[1]), "true false");
|
|
const std::vector<bool> v = {true, false};
|
|
EXPECT_EQ(fmt::format("{} {}", v[0], v[1]), "true false");
|
|
}
|
|
|
|
TEST(std_test, format_bitset) {
|
|
auto bs = std::bitset<6>(42);
|
|
EXPECT_EQ(fmt::format("{}", bs), "101010");
|
|
EXPECT_EQ(fmt::format("{:0>8}", bs), "00101010");
|
|
EXPECT_EQ(fmt::format("{:-^12}", bs), "---101010---");
|
|
}
|
|
|
|
#ifdef __cpp_lib_byte
|
|
TEST(base_test, format_byte) {
|
|
auto s = std::string();
|
|
fmt::format_to(std::back_inserter(s), "{}", std::byte(42));
|
|
EXPECT_EQ(s, "42");
|
|
}
|
|
#endif
|
|
|
|
TEST(std_test, format_atomic) {
|
|
std::atomic<bool> b(false);
|
|
EXPECT_EQ(fmt::format("{}", b), "false");
|
|
|
|
const std::atomic<bool> cb(true);
|
|
EXPECT_EQ(fmt::format("{}", cb), "true");
|
|
}
|
|
|
|
#ifdef __cpp_lib_atomic_flag_test
|
|
TEST(std_test, format_atomic_flag) {
|
|
std::atomic_flag f;
|
|
(void)f.test_and_set();
|
|
EXPECT_EQ(fmt::format("{}", f), "true");
|
|
|
|
f.clear();
|
|
const std::atomic_flag& cf = f;
|
|
EXPECT_EQ(fmt::format("{}", cf), "false");
|
|
}
|
|
#endif // __cpp_lib_atomic_flag_test
|
|
|
|
TEST(std_test, format_unique_ptr) {
|
|
std::unique_ptr<int> up(new int(1));
|
|
EXPECT_EQ(fmt::format("{}", fmt::ptr(up.get())),
|
|
fmt::format("{}", fmt::ptr(up)));
|
|
struct custom_deleter {
|
|
void operator()(int* p) const { delete p; }
|
|
};
|
|
std::unique_ptr<int, custom_deleter> upcd(new int(1));
|
|
EXPECT_EQ(fmt::format("{}", fmt::ptr(upcd.get())),
|
|
fmt::format("{}", fmt::ptr(upcd)));
|
|
}
|
|
|
|
TEST(std_test, format_shared_ptr) {
|
|
std::shared_ptr<int> sp(new int(1));
|
|
EXPECT_EQ(fmt::format("{}", fmt::ptr(sp.get())),
|
|
fmt::format("{}", fmt::ptr(sp)));
|
|
}
|
|
|
|
TEST(std_test, format_reference_wrapper) {
|
|
int num = 35;
|
|
EXPECT_EQ(fmt::to_string(std::cref(num)), "35");
|
|
EXPECT_EQ(fmt::to_string(std::ref(num)), "35");
|
|
EXPECT_EQ(fmt::format("{}", std::cref(num)), "35");
|
|
EXPECT_EQ(fmt::format("{}", std::ref(num)), "35");
|
|
}
|
|
|
|
// Regression test for https://github.com/fmtlib/fmt/issues/4424.
|
|
struct type_with_format_as {};
|
|
int format_as(type_with_format_as) { return 20; }
|
|
|
|
TEST(std_test, format_reference_wrapper_with_format_as) {
|
|
type_with_format_as t;
|
|
EXPECT_EQ(fmt::to_string(std::cref(t)), "20");
|
|
EXPECT_EQ(fmt::to_string(std::ref(t)), "20");
|
|
EXPECT_EQ(fmt::format("{}", std::cref(t)), "20");
|
|
EXPECT_EQ(fmt::format("{}", std::ref(t)), "20");
|
|
}
|
|
|
|
struct type_with_format_as_string {};
|
|
std::string format_as(type_with_format_as_string) { return "foo"; }
|
|
|
|
TEST(std_test, format_reference_wrapper_with_format_as_string) {
|
|
type_with_format_as_string t;
|
|
EXPECT_EQ(fmt::to_string(std::cref(t)), "foo");
|
|
EXPECT_EQ(fmt::to_string(std::ref(t)), "foo");
|
|
EXPECT_EQ(fmt::format("{}", std::cref(t)), "foo");
|
|
EXPECT_EQ(fmt::format("{}", std::ref(t)), "foo");
|
|
}
|