From cd2b1c09fbac163be8740c645269b13351d218df Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Sun, 27 Jun 2021 01:26:13 +0100 Subject: [PATCH] #2390: consteval format checks for wchar_t and char8_t --- include/fmt/xchar.h | 27 ++++++++++++++++++++++++++- test/format-test.cc | 18 ++++++++++++------ test/xchar-test.cc | 6 +++--- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 74c69045..5d402a44 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -29,8 +29,11 @@ using wmemory_buffer = basic_memory_buffer; #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. +template using xformat_string = basic_string_view; template using wformat_string = wstring_view; #else +template +using xformat_string = basic_format_string...>; template using wformat_string = basic_format_string...>; #endif @@ -40,6 +43,12 @@ template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; +template +constexpr format_arg_store, Args...> make_xformat_args( + const Args&... args) { + return {args...}; +} + template constexpr format_arg_store make_wformat_args( const Args&... args) { @@ -87,10 +96,26 @@ auto vformat(basic_string_view format_str, return to_string(buffer); } +template +auto format(wformat_string fmt, T&&... args) -> std::wstring { + return vformat(fmt, make_xformat_args(args...)); +} + +template +auto format(xformat_string fmt, T&&... args) + -> std::basic_string { + return vformat( + fmt, make_xformat_args(args...)); +} + // Pass char_t as a default template parameter instead of using // std::basic_string> to reduce the symbol size. template , - FMT_ENABLE_IF(!std::is_same::value)> + typename Str = xformat_string, + FMT_ENABLE_IF(!std::is_same::value && + (!std::is_convertible::value || + (!std::is_same::value && + !std::is_same::value)))> auto format(const S& format_str, Args&&... args) -> std::basic_string { const auto& vargs = fmt::make_args_checked(format_str, args...); return vformat(to_string_view(format_str), vargs); diff --git a/test/format-test.cc b/test/format-test.cc index 892556f1..fd18a1d4 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -11,6 +11,7 @@ #endif // clang-format off #include "fmt/format.h" +#include "fmt/xchar.h" // clang-format on #include // uint32_t @@ -33,6 +34,7 @@ using fmt::memory_buffer; using fmt::runtime; using fmt::string_view; using fmt::detail::max_value; +using fmt::char_t; using testing::Return; using testing::StrictMock; @@ -1949,7 +1951,8 @@ struct test_error_handler { } }; -FMT_CONSTEXPR size_t len(const char* s) { +template +FMT_CONSTEXPR size_t len(const Char* s) { size_t len = 0; while (*s++) ++len; return len; @@ -1964,12 +1967,12 @@ FMT_CONSTEXPR bool equal(const char* s1, const char* s2) { return *s1 == *s2; } -template -FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) { +template +FMT_CONSTEXPR bool test_error(const Char* fmt, const char* expected_error) { const char* actual_error = nullptr; - auto s = string_view(fmt, len(fmt)); + auto s = fmt::basic_string_view(fmt, len(fmt)); auto checker = - fmt::detail::format_string_checker( + fmt::detail::format_string_checker( s, test_error_handler(actual_error)); fmt::detail::parse_format_string(s, checker); return equal(actual_error, expected_error); @@ -1978,7 +1981,7 @@ FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) { # define EXPECT_ERROR_NOARGS(fmt, error) \ static_assert(test_error(fmt, error), "") # define EXPECT_ERROR(fmt, error, ...) \ - static_assert(test_error<__VA_ARGS__>(fmt, error), "") + static_assert(test_error, __VA_ARGS__>(fmt, error), "") TEST(format_test, format_string_errors) { EXPECT_ERROR_NOARGS("foo", nullptr); @@ -2041,6 +2044,9 @@ TEST(format_test, format_string_errors) { EXPECT_ERROR("{}{1}", "cannot switch from automatic to manual argument indexing", int, int); + + EXPECT_ERROR(L"{:d}", "invalid type specifier", decltype(L"")); + EXPECT_ERROR(u8"{:d}", "invalid type specifier", decltype(u8"")); } TEST(format_test, vformat_to) { diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 78ecb2c7..61ef1fa0 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -78,7 +78,7 @@ TEST(xchar_test, format) { EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2)); EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc")); EXPECT_EQ(L"z", fmt::format(L"{}", L'z')); - EXPECT_THROW(fmt::format(L"{:*\x343E}", 42), fmt::format_error); + EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error); EXPECT_EQ(L"true", fmt::format(L"{}", true)); EXPECT_EQ(L"a", fmt::format(L"{0}", 'a')); EXPECT_EQ(L"a", fmt::format(L"{0}", L'a')); @@ -130,7 +130,7 @@ TEST(xchar_test, format_utf8_precision) { str_type(reinterpret_cast(u8"{:.4}")); auto str = str_type(reinterpret_cast( u8"caf\u00e9s")); // cafés - auto result = fmt::format(format, str); + auto result = fmt::format(fmt::runtime(format), str); EXPECT_EQ(fmt::detail::compute_width(result), 4); EXPECT_EQ(result.size(), 5); EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5))); @@ -221,7 +221,7 @@ TEST(xchar_test, sign_not_truncated) { wchar_t format_str[] = { L'{', L':', '+' | static_cast(1 << fmt::detail::num_bits()), L'}', 0}; - EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error); + EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error); } namespace fake_qt {