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
|
} // namespace detail
|
||||||
|
|
||||||
FMT_BEGIN_EXPORT
|
FMT_BEGIN_EXPORT
|
||||||
|
|||||||
@@ -241,14 +241,6 @@ using range_reference_type =
|
|||||||
template <typename Range>
|
template <typename Range>
|
||||||
using uncvref_type = remove_cvref_t<range_reference_type<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>
|
template <typename T>
|
||||||
struct range_format_kind_
|
struct range_format_kind_
|
||||||
: std::integral_constant<range_format,
|
: std::integral_constant<range_format,
|
||||||
|
|||||||
@@ -111,12 +111,17 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
|||||||
#endif // FMT_CPP_LIB_FILESYSTEM
|
#endif // FMT_CPP_LIB_FILESYSTEM
|
||||||
|
|
||||||
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
|
#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)
|
if constexpr (has_to_string_view<T>::value)
|
||||||
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
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);
|
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
|
#endif
|
||||||
|
|
||||||
@@ -382,18 +387,9 @@ struct formatter<std::optional<T>, Char,
|
|||||||
static constexpr basic_string_view<Char> none =
|
static constexpr basic_string_view<Char> none =
|
||||||
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
|
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:
|
public:
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
|
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);
|
return underlying_.parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,10 +425,10 @@ struct formatter<std::expected<T, E>, Char,
|
|||||||
if (value.has_value()) {
|
if (value.has_value()) {
|
||||||
out = detail::write<Char>(out, "expected(");
|
out = detail::write<Char>(out, "expected(");
|
||||||
if constexpr (!std::is_void<T>::value)
|
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 {
|
} else {
|
||||||
out = detail::write<Char>(out, "unexpected(");
|
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++ = ')';
|
*out++ = ')';
|
||||||
return out;
|
return out;
|
||||||
@@ -496,7 +492,7 @@ struct formatter<Variant, Char,
|
|||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
std::visit(
|
std::visit(
|
||||||
[&](const auto& v) {
|
[&](const auto& v) {
|
||||||
out = detail::write_escaped_alternative<Char>(out, v);
|
out = detail::write_escaped_alternative<Char>(out, v, ctx);
|
||||||
},
|
},
|
||||||
value);
|
value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,33 @@ class my_class {
|
|||||||
return fmt::to_string(elm.av);
|
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
|
} // 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) {
|
TEST(std_test, optional_format_as) {
|
||||||
#ifdef __cpp_lib_optional
|
#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>{}), "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>{}), "none");
|
||||||
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}),
|
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}),
|
||||||
"optional(\"7\")");
|
"optional(\"7\")");
|
||||||
|
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class_int{8}}),
|
||||||
|
"optional(8)");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,6 +303,24 @@ TEST(std_test, variant) {
|
|||||||
#endif
|
#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) {
|
TEST(std_test, error_code) {
|
||||||
auto& generic = std::generic_category();
|
auto& generic = std::generic_category();
|
||||||
EXPECT_EQ(fmt::format("{}", std::error_code(42, generic)), "generic:42");
|
EXPECT_EQ(fmt::format("{}", std::error_code(42, generic)), "generic:42");
|
||||||
|
|||||||
Reference in New Issue
Block a user