diff --git a/include/fmt/std.h b/include/fmt/std.h index 5cb1ca48d874..ff96a9525f23 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -38,6 +38,10 @@ # endif #endif +#if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 202002L +# include +#endif + #if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() # include #endif @@ -242,6 +246,56 @@ struct formatter, Char, FMT_END_NAMESPACE #endif // __cpp_lib_optional +#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT + +FMT_BEGIN_NAMESPACE +namespace detail { + +template +auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (has_to_string_view::value) + return write_escaped_string(out, detail::to_string_view(v)); + if constexpr (std::is_same_v) return write_escaped_char(out, v); + return write(out, v); +} + +} // namespace detail + +FMT_END_NAMESPACE +#endif + +#ifdef __cpp_lib_expected +FMT_BEGIN_NAMESPACE + +FMT_EXPORT +template +struct formatter< + std::expected, Char, + std::enable_if_t && is_formattable>> { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const std::expected& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + if (value.has_value()) { + out = detail::write(out, "expected("); + out = detail::write_escaped_alternative(out, value.value()); + } else { + out = detail::write(out, "unexpected("); + out = detail::write_escaped_alternative(out, value.error()); + } + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif // __cpp_lib_expected + #ifdef __cpp_lib_source_location FMT_BEGIN_NAMESPACE FMT_EXPORT @@ -291,16 +345,6 @@ template class is_variant_formattable_ { decltype(check(variant_index_sequence{}))::value; }; -template -auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { - if constexpr (has_to_string_view::value) - return write_escaped_string(out, detail::to_string_view(v)); - else if constexpr (std::is_same_v) - return write_escaped_char(out, v); - else - return write(out, v); -} - } // namespace detail template struct is_variant_like { @@ -346,7 +390,7 @@ struct formatter< FMT_TRY { std::visit( [&](const auto& v) { - out = detail::write_variant_alternative(out, v); + out = detail::write_escaped_alternative(out, v); }, value); } diff --git a/test/std-test.cc b/test/std-test.cc index f5566848e023..0cdd3abbd6f7 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -102,6 +102,26 @@ TEST(std_test, optional) { #endif } +TEST(std_test, expected) { +#ifdef __cpp_lib_expected + EXPECT_EQ(fmt::format("{}", std::expected{1}), "expected(1)"); + EXPECT_EQ(fmt::format("{}", std::expected{std::unexpected(1)}), "unexpected(1)"); + EXPECT_EQ(fmt::format("{}", std::expected{"test"}), "expected(\"test\")"); + EXPECT_EQ(fmt::format("{}", std::expected{std::unexpected("test")}), "unexpected(\"test\")"); + EXPECT_EQ(fmt::format("{}", std::expected{'a'}), "expected('a')"); + EXPECT_EQ(fmt::format("{}", std::expected{std::unexpected('a')}), "unexpected('a')"); + + struct unformattable1 {}; + struct unformattable2 {}; + EXPECT_FALSE((fmt::is_formattable::value)); + EXPECT_FALSE((fmt::is_formattable::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_TRUE((fmt::is_formattable>::value)); +#endif +} + namespace my_nso { enum class my_number { one,