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:
Marvel Harisson 2026-10-19 10:57:54 +09:00
parent 0fc31633e7
commit b6d6358225
2 changed files with 53 additions and 6 deletions

View File

@ -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;

View File

@ -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