mirror of
https://github.com/fmtlib/fmt.git
synced 2025-11-24 11:20:11 +01:00
Add format_as support for std::variant and std::expected formatters (#4575)
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
This commit is contained in:
committed by
GitHub
parent
378a5ab3c1
commit
85f6ecc7a0
@@ -763,6 +763,14 @@ template <typename T> struct allocator : private std::decay<void> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Formatter>
|
||||
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
|
||||
-> decltype(f.set_debug_format(set)) {
|
||||
f.set_debug_format(set);
|
||||
}
|
||||
template <typename Formatter>
|
||||
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
@@ -241,14 +241,6 @@ using range_reference_type =
|
||||
template <typename Range>
|
||||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
||||
|
||||
template <typename Formatter>
|
||||
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
|
||||
-> decltype(f.set_debug_format(set)) {
|
||||
f.set_debug_format(set);
|
||||
}
|
||||
template <typename Formatter>
|
||||
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
|
||||
|
||||
template <typename T>
|
||||
struct range_format_kind_
|
||||
: std::integral_constant<range_format,
|
||||
|
||||
@@ -111,12 +111,17 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
||||
#endif // FMT_CPP_LIB_FILESYSTEM
|
||||
|
||||
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
|
||||
|
||||
template <typename Char, typename OutputIt, typename T, typename FormatContext>
|
||||
auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx)
|
||||
-> OutputIt {
|
||||
if constexpr (has_to_string_view<T>::value)
|
||||
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
||||
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
|
||||
return write<Char>(out, v);
|
||||
|
||||
formatter<std::remove_cv_t<T>, Char> underlying;
|
||||
maybe_set_debug_format(underlying, true);
|
||||
return underlying.format(v, ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -382,18 +387,9 @@ struct formatter<std::optional<T>, Char,
|
||||
static constexpr basic_string_view<Char> none =
|
||||
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
|
||||
|
||||
template <class U>
|
||||
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
|
||||
-> decltype(u.set_debug_format(set)) {
|
||||
u.set_debug_format(set);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
|
||||
maybe_set_debug_format(underlying_, true);
|
||||
detail::maybe_set_debug_format(underlying_, true);
|
||||
return underlying_.parse(ctx);
|
||||
}
|
||||
|
||||
@@ -429,10 +425,10 @@ struct formatter<std::expected<T, E>, Char,
|
||||
if (value.has_value()) {
|
||||
out = detail::write<Char>(out, "expected(");
|
||||
if constexpr (!std::is_void<T>::value)
|
||||
out = detail::write_escaped_alternative<Char>(out, *value);
|
||||
out = detail::write_escaped_alternative<Char>(out, *value, ctx);
|
||||
} else {
|
||||
out = detail::write<Char>(out, "unexpected(");
|
||||
out = detail::write_escaped_alternative<Char>(out, value.error());
|
||||
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
|
||||
}
|
||||
*out++ = ')';
|
||||
return out;
|
||||
@@ -496,7 +492,7 @@ struct formatter<Variant, Char,
|
||||
FMT_TRY {
|
||||
std::visit(
|
||||
[&](const auto& v) {
|
||||
out = detail::write_escaped_alternative<Char>(out, v);
|
||||
out = detail::write_escaped_alternative<Char>(out, v, ctx);
|
||||
},
|
||||
value);
|
||||
}
|
||||
|
||||
@@ -197,7 +197,33 @@ class my_class {
|
||||
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");
|
||||
@@ -206,6 +232,8 @@ TEST(std_test, optional_format_as) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -275,6 +303,24 @@ TEST(std_test, variant) {
|
||||
#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");
|
||||
|
||||
Reference in New Issue
Block a user