From b890509ec14df63d9ed1f6382ef4a524aac5bccd Mon Sep 17 00:00:00 2001 From: Tom Huntington <55266932+tom-huntington@users.noreply.github.com> Date: Sat, 11 Feb 2023 19:37:42 +1300 Subject: [PATCH 1/3] Add std::optional --- doc/api.rst | 1 + include/fmt/std.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ test/std-test.cc | 30 ++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index 1402f7c08758..2e7f6d6d146c 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -508,6 +508,7 @@ Standard Library Types Formatting * `std::thread::id `_ * `std::monostate `_ * `std::variant `_ +* `std::optional `_ Formatting Variants ------------------- diff --git a/include/fmt/std.h b/include/fmt/std.h index 32c3e454e293..544ec541140f 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,49 @@ 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>> { + private: + formatter underlying_; + static constexpr basic_string_view optional = + detail::string_literal{}; + static constexpr basic_string_view none = + detail::string_literal{}; + + template + FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set) + -> decltype(u.set_debug_format(set)) { + u.set_debug_format(set); + } + + template + FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} + + public: + template FMT_CONSTEXPR auto parse(ParseContext& ctx) { + maybe_set_debug_format(underlying_, true); + return underlying_.parse(ctx); + } + + template + auto format(std::optional const& opt, FormatContext& ctx) const + -> decltype(ctx.out()) { + if (not opt) return detail::write(ctx.out(), none); + + auto out = ctx.out(); + out = detail::write(out, optional); + ctx.advance_to(out); + out = underlying_.format(*opt, ctx); + return detail::write(out, ')'); + } +}; +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 4411d3d1cd39..a5a4dcc6cd16 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -12,6 +12,7 @@ #include "fmt/os.h" // fmt::system_category #include "fmt/ranges.h" +#include "fmt/xchar.h" #include "gtest-extra.h" // StartsWith using testing::StartsWith; @@ -50,6 +51,35 @@ 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"none"); + EXPECT_EQ(fmt::format("{}", std::pair{1, "second"}), "(1, \"second\")"); + EXPECT_EQ(fmt::format("{}", std::vector{std::optional{1}, std::optional{2}, + std::optional{3}}), + "[optional(1), optional(2), optional(3)]"); + EXPECT_EQ( + fmt::format("{}", std::optional>{std::optional{ + "nested"}}), + "optional(optional(\"nested\"))"); + EXPECT_EQ( + fmt::format("{:<{}}", std::optional{std::string{"left aligned"}}, 30), + "optional(\"left aligned\" )"); + EXPECT_EQ( + fmt::format("{::d}", std::optional{std::vector{'h', 'e', 'l', 'l', 'o'}}), + "optional([104, 101, 108, 108, 111])"); + EXPECT_EQ(fmt::format("{}", std::optional{std::string{"string"}}), + "optional(\"string\")"); + EXPECT_EQ(fmt::format("{}", std::optional{'C'}), "optional(\'C\')"); + EXPECT_EQ(fmt::format("{:.{}f}", std::optional{3.14}, 1), "optional(3.1)"); + + 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"); From a406d8c5beb9c7cd59be32f4e94a7d1295d59172 Mon Sep 17 00:00:00 2001 From: Tom Huntington <55266932+tom-huntington@users.noreply.github.com> Date: Fri, 24 Feb 2023 21:06:33 +1300 Subject: [PATCH 2/3] not -> ! --- include/fmt/std.h | 2 +- test/std-test.cc | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/fmt/std.h b/include/fmt/std.h index 544ec541140f..4b22eda15710 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -125,7 +125,7 @@ struct formatter, Char, template auto format(std::optional const& opt, FormatContext& ctx) const -> decltype(ctx.out()) { - if (not opt) return detail::write(ctx.out(), none); + if (!opt) return detail::write(ctx.out(), none); auto out = ctx.out(); out = detail::write(out, optional); diff --git a/test/std-test.cc b/test/std-test.cc index a5a4dcc6cd16..f30c514b443a 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -59,8 +59,7 @@ TEST(std_test, optional) { std::optional{3}}), "[optional(1), optional(2), optional(3)]"); EXPECT_EQ( - fmt::format("{}", std::optional>{std::optional{ - "nested"}}), + fmt::format("{}", std::optional>{{"nested"}}), "optional(optional(\"nested\"))"); EXPECT_EQ( fmt::format("{:<{}}", std::optional{std::string{"left aligned"}}, 30), From 0045255da1e98d310cc0ee5dd1934b8c558051de Mon Sep 17 00:00:00 2001 From: Tom Huntington <55266932+tom-huntington@users.noreply.github.com> Date: Sat, 25 Feb 2023 13:35:01 +1300 Subject: [PATCH 3/3] fix xchar support. tests moved/added to xchar-test.cc --- include/fmt/std.h | 2 +- test/std-test.cc | 3 +-- test/xchar-test.cc | 9 +++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/fmt/std.h b/include/fmt/std.h index 4b22eda15710..29667b2b1dfd 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -98,7 +98,7 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE template struct formatter, Char, - std::enable_if_t::value>> { + std::enable_if_t::value>> { private: formatter underlying_; static constexpr basic_string_view optional = diff --git a/test/std-test.cc b/test/std-test.cc index f30c514b443a..39aac5a2fc65 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -12,7 +12,6 @@ #include "fmt/os.h" // fmt::system_category #include "fmt/ranges.h" -#include "fmt/xchar.h" #include "gtest-extra.h" // StartsWith using testing::StartsWith; @@ -53,7 +52,7 @@ TEST(std_test, thread_id) { TEST(std_test, optional) { #ifdef __cpp_lib_optional - EXPECT_EQ(fmt::format(L"{}", std::optional{}), L"none"); + EXPECT_EQ(fmt::format("{}", std::optional{}), "none"); EXPECT_EQ(fmt::format("{}", std::pair{1, "second"}), "(1, \"second\")"); EXPECT_EQ(fmt::format("{}", std::vector{std::optional{1}, std::optional{2}, std::optional{3}}), diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 1deab2d02c9d..022646da67be 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -16,6 +16,7 @@ #include "fmt/color.h" #include "fmt/ostream.h" #include "fmt/ranges.h" +#include "fmt/std.h" #include "gtest-extra.h" // Contains #include "util.h" // get_locale @@ -588,4 +589,12 @@ TEST(locale_test, sign) { EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50"); } +TEST(std_test_xchar, optional) { +# ifdef __cpp_lib_optional + EXPECT_EQ(fmt::format(L"{}", std::optional{L'C'}), L"optional(\'C\')"); + EXPECT_EQ(fmt::format(L"{}", std::optional{std::wstring{L"wide string"}}), + L"optional(\"wide string\")"); +# endif +} + #endif // FMT_STATIC_THOUSANDS_SEPARATOR