mirror of
https://github.com/fmtlib/fmt.git
synced 2026-10-29 02:17:28 +08:00
Fix wide formatting of variant, monostate and expected
The formatters for std::variant, std::monostate, std::expected and std::unexpected wrote narrow string literals with detail::write<Char>, which is ambiguous for wchar_t, so formatting these types into a wide string failed to compile even though is_formattable reported them as formattable. Use detail::string_literal like the std::optional formatter does.
This commit is contained in:
parent
0fc31633e7
commit
b6d6358225
@ -404,6 +404,15 @@ struct formatter<std::expected<T, E>, Char,
|
||||
std::enable_if_t<(std::is_void<T>::value ||
|
||||
is_formattable<T, Char>::value) &&
|
||||
is_formattable<E, Char>::value>> {
|
||||
private:
|
||||
static constexpr basic_string_view<Char> expected_prefix =
|
||||
detail::string_literal<Char, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd',
|
||||
'('>{};
|
||||
static constexpr basic_string_view<Char> unexpected_prefix =
|
||||
detail::string_literal<Char, 'u', 'n', 'e', 'x', 'p', 'e', 'c', 't', 'e',
|
||||
'd', '('>{};
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
return ctx.begin();
|
||||
}
|
||||
@ -414,11 +423,11 @@ struct formatter<std::expected<T, E>, Char,
|
||||
auto out = ctx.out();
|
||||
|
||||
if (value.has_value()) {
|
||||
out = detail::write<Char>(out, "expected(");
|
||||
out = detail::write<Char>(out, expected_prefix);
|
||||
if constexpr (!std::is_void<T>::value)
|
||||
out = detail::write_escaped_alternative<Char>(out, *value, ctx);
|
||||
} else {
|
||||
out = detail::write<Char>(out, "unexpected(");
|
||||
out = detail::write<Char>(out, unexpected_prefix);
|
||||
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
|
||||
}
|
||||
*out++ = ')';
|
||||
@ -429,6 +438,12 @@ struct formatter<std::expected<T, E>, Char,
|
||||
template <typename E, typename Char>
|
||||
struct formatter<std::unexpected<E>, Char,
|
||||
std::enable_if_t<is_formattable<E, Char>::value>> {
|
||||
private:
|
||||
static constexpr basic_string_view<Char> unexpected_prefix =
|
||||
detail::string_literal<Char, 'u', 'n', 'e', 'x', 'p', 'e', 'c', 't', 'e',
|
||||
'd', '('>{};
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
return ctx.begin();
|
||||
}
|
||||
@ -438,7 +453,7 @@ struct formatter<std::unexpected<E>, Char,
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
|
||||
out = detail::write<Char>(out, "unexpected(");
|
||||
out = detail::write<Char>(out, unexpected_prefix);
|
||||
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
|
||||
|
||||
*out++ = ')';
|
||||
@ -474,6 +489,12 @@ template <typename T> struct is_variant_like {
|
||||
};
|
||||
|
||||
template <typename Char> struct formatter<std::monostate, Char> {
|
||||
private:
|
||||
static constexpr basic_string_view<Char> monostate_str =
|
||||
detail::string_literal<Char, 'm', 'o', 'n', 'o', 's', 't', 'a', 't',
|
||||
'e'>{};
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
return ctx.begin();
|
||||
}
|
||||
@ -481,7 +502,7 @@ template <typename Char> struct formatter<std::monostate, Char> {
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR auto format(const std::monostate&, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return detail::write<Char>(ctx.out(), "monostate");
|
||||
return detail::write<Char>(ctx.out(), monostate_str);
|
||||
}
|
||||
};
|
||||
|
||||
@ -490,6 +511,15 @@ struct formatter<Variant, Char,
|
||||
std::enable_if_t<std::conjunction_v<
|
||||
is_variant_like<Variant>,
|
||||
detail::is_variant_formattable<Variant, Char>>>> {
|
||||
private:
|
||||
static constexpr basic_string_view<Char> variant_prefix =
|
||||
detail::string_literal<Char, 'v', 'a', 'r', 'i', 'a', 'n', 't', '('>{};
|
||||
static constexpr basic_string_view<Char> valueless_str =
|
||||
detail::string_literal<Char, 'v', 'a', 'l', 'u', 'e', 'l', 'e', 's', 's',
|
||||
' ', 'b', 'y', ' ', 'e', 'x', 'c', 'e', 'p', 't',
|
||||
'i', 'o', 'n'>{};
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
return ctx.begin();
|
||||
}
|
||||
@ -499,7 +529,7 @@ struct formatter<Variant, Char,
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
|
||||
out = detail::write<Char>(out, "variant(");
|
||||
out = detail::write<Char>(out, variant_prefix);
|
||||
FMT_TRY {
|
||||
std::visit(
|
||||
[&](const auto& v) {
|
||||
@ -508,7 +538,7 @@ struct formatter<Variant, Char,
|
||||
value);
|
||||
}
|
||||
FMT_CATCH(const std::bad_variant_access&) {
|
||||
detail::write<Char>(out, "valueless by exception");
|
||||
detail::write<Char>(out, valueless_str);
|
||||
}
|
||||
*out++ = ')';
|
||||
return out;
|
||||
|
||||
@ -455,4 +455,21 @@ TEST(std_test_xchar, optional) {
|
||||
# endif
|
||||
}
|
||||
|
||||
TEST(std_test_xchar, variant) {
|
||||
# ifdef __cpp_lib_variant
|
||||
EXPECT_EQ(fmt::format(L"{}", std::monostate{}), L"monostate");
|
||||
EXPECT_EQ(fmt::format(L"{}", std::variant<int, std::wstring>{L"wide"}),
|
||||
L"variant(\"wide\")");
|
||||
# endif
|
||||
}
|
||||
|
||||
TEST(std_test_xchar, expected) {
|
||||
# ifdef __cpp_lib_expected
|
||||
EXPECT_EQ(fmt::format(L"{}", std::expected<int, int>{1}), L"expected(1)");
|
||||
EXPECT_EQ(fmt::format(L"{}", std::expected<int, int>{std::unexpected(1)}),
|
||||
L"unexpected(1)");
|
||||
EXPECT_EQ(fmt::format(L"{}", std::unexpected(1)), L"unexpected(1)");
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user