From 9ae5c370ad18fff4fb89f3c8fc994fd5861b1d27 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Sat, 26 Jun 2021 22:31:31 +0100 Subject: [PATCH] #2390: consteval format checks for wchar_t and char8_t --- include/fmt/xchar.h | 25 ++++++++++++++++++++++++- test/format-test.cc | 18 ++++++++++++------ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 74c690458fde2..a130770e68f8c 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -27,6 +27,9 @@ using wformat_context = buffer_context; using wformat_args = basic_format_args; using wmemory_buffer = basic_memory_buffer; +template +using xformat_string = basic_format_string...>; + #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. template using wformat_string = wstring_view; @@ -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,24 @@ 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)> + FMT_ENABLE_IF(!std::is_same::value && + !std::is_convertible< + S, xformat_string, Args...>>::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 892556f18d30f..fd18a1d44e220 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) {