diff --git a/include/fmt/std.h b/include/fmt/std.h index 32c3e454e2933..c4f5e7747d454 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -29,6 +29,9 @@ # if FMT_HAS_INCLUDE() # include # endif +# if FMT_HAS_INCLUDE() +# include +# endif #endif // GCC 4 does not support FMT_HAS_INCLUDE. @@ -91,6 +94,39 @@ template struct formatter : basic_ostream_formatter {}; FMT_END_NAMESPACE +#ifdef __cpp_lib_optional +FMT_BEGIN_NAMESPACE +template +struct formatter, Char, + std::enable_if_t::value>> { + formatter underlying_; + static constexpr basic_string_view OPTION = detail::string_literal{}; + static constexpr basic_string_view EMPTY = detail::string_literal{}; + + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) { + return underlying_.parse(ctx); + } + template + auto format(std::optional const& opt, FormatContext& ctx) const + -> decltype(ctx.out()) { + if (opt) { + auto out = ctx.out(); + out = detail::write(out, OPTION); + if constexpr (detail::is_string::value) out = detail::write(out, '"'); + else if constexpr (std::is_same_v) out = detail::write(out, '\''); + ctx.advance_to(out); + out = underlying_.format(*opt, ctx); + if constexpr (detail::is_string::value) out = detail::write(out, '"'); + else if constexpr (std::is_same_v) out = detail::write(out, '\''); + return detail::write(out, ')'); + } + return detail::write(ctx.out(), EMPTY); + } +}; +FMT_END_NAMESPACE +#endif // __cpp_lib_optional + #ifdef __cpp_lib_variant FMT_BEGIN_NAMESPACE template struct formatter { diff --git a/test/std-test.cc b/test/std-test.cc index 4411d3d1cd39b..f8011e09c5419 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -6,6 +6,7 @@ // For the license information refer to format.h. #include "fmt/std.h" +#include "fmt/xchar.h" #include #include @@ -50,6 +51,43 @@ TEST(std_test, thread_id) { EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty()); } +TEST(std_test, optional) { +#ifdef __cpp_lib_optional + EXPECT_EQ(fmt::format( + L"{}", std::optional{}), + L"Empty"); + EXPECT_EQ(fmt::format( + "{}", std::vector{std::optional{1}, std::optional{2}, std::optional{3}}), + "[Option(1), Option(2), Option(3)]"); + EXPECT_EQ(fmt::format( + "{:#x}", std::optional>{std::optional{1}}), + "Option(Option(0x1))"); + EXPECT_EQ(fmt::format( + "{::d}", std::vector{'h', 'e', 'l', 'l', 'o'}), + "[104, 101, 108, 108, 111]"); + EXPECT_EQ(fmt::format( + "{:<{}}", std::optional{std::string{"left aligned"}}, 30), + "Option(\"left aligned \")"); + EXPECT_EQ(fmt::format( + "{0:>{1}}", std::optional{'R'}, 30), + "Option(\' R\')"); + EXPECT_EQ(fmt::format( + "{:*^30}", std::optional{"centered"}), + "Option(\"***********centered***********\")"); + EXPECT_EQ(fmt::format( + "{:.{}f}", std::optional{3.14}, 1), + "Option(3.1)"); + EXPECT_EQ(fmt::format( + "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", std::optional{42}), + "int: Option(42); hex: Option(2a); oct: Option(52); bin: Option(101010)"); + + struct unformattable {}; + EXPECT_FALSE((fmt::is_formattable::value)); + EXPECT_FALSE((fmt::is_formattable>::value)); + EXPECT_TRUE((fmt::is_formattable>::value)); + #endif +} + TEST(std_test, variant) { #ifdef __cpp_lib_variant EXPECT_EQ(fmt::format("{}", std::monostate{}), "monostate");