From 34b8acaef7f3158850731d710e5a6f710e9adf99 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 21 May 2021 17:37:07 -0700 Subject: [PATCH] More wchar_t-specific API to wchar.h --- include/fmt/format.h | 25 +---------------- include/fmt/os.h | 4 +-- include/fmt/ranges.h | 10 ++----- include/fmt/wchar.h | 31 ++++++++++++++++++++ src/os.cc | 8 +++--- test/core-test.cc | 8 ++---- test/enforce-checks-test.cc | 1 + test/format-test.cc | 42 ---------------------------- test/os-test.cc | 11 ++++---- test/printf-test.cc | 5 ++-- test/wchar-test.cc | 56 +++++++++++++++++++++++++++++++++++-- 11 files changed, 104 insertions(+), 97 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 2acbd682..b59a1865 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -629,8 +629,6 @@ void iterator_buffer::flush() { FMT_MODULE_EXPORT_BEGIN -using wstring_view = basic_string_view; - template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; @@ -1129,7 +1127,7 @@ class utf8_to_utf16 { public: FMT_API explicit utf8_to_utf16(string_view s); - operator wstring_view() const { return {&buffer_[0], size()}; } + operator basic_string_view() const { return {&buffer_[0], size()}; } size_t size() const { return buffer_.size() - 1; } const wchar_t* c_str() const { return &buffer_[0]; } std::wstring str() const { return {&buffer_[0], size()}; } @@ -2489,11 +2487,6 @@ arg_join join(It begin, Sentinel end, string_view sep) { return {begin, end, sep}; } -template -arg_join join(It begin, Sentinel end, wstring_view sep) { - return {begin, end, sep}; -} - /** \rst Returns an object that formats `range` with elements separated by `sep`. @@ -2516,12 +2509,6 @@ arg_join, detail::sentinel_t, char> join( return join(std::begin(range), std::end(range), sep); } -template -arg_join, detail::sentinel_t, wchar_t> join( - Range&& range, wstring_view sep) { - return join(std::begin(range), std::end(range), sep); -} - /** \rst Converts *value* to ``std::string`` using the default format for type *T*. @@ -2550,13 +2537,6 @@ inline std::string to_string(T value) { return std::string(begin, detail::write(begin, value)); } -/** - Converts *value* to ``std::wstring`` using the default format for type *T*. - */ -template inline std::wstring to_wstring(const T& value) { - return format(FMT_STRING(L"{}"), value); -} - template std::basic_string to_string(const basic_memory_buffer& buf) { auto size = buf.size(); @@ -2873,9 +2853,6 @@ operator""_a() { constexpr detail::udl_arg operator"" _a(const char* s, size_t) { return {s}; } -constexpr detail::udl_arg operator"" _a(const wchar_t* s, size_t) { - return {s}; -} # endif } // namespace literals diff --git a/include/fmt/os.h b/include/fmt/os.h index fca43d0a..061a6cd5 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -148,7 +148,7 @@ class utf16_to_utf8 { public: utf16_to_utf8() {} - FMT_API explicit utf16_to_utf8(wstring_view s); + FMT_API explicit utf16_to_utf8(basic_string_view s); operator string_view() const { return string_view(&buffer_[0], size()); } size_t size() const { return buffer_.size() - 1; } const char* c_str() const { return &buffer_[0]; } @@ -157,7 +157,7 @@ class utf16_to_utf8 { // Performs conversion returning a system error code instead of // throwing exception on conversion error. This method may still throw // in case of memory allocation error. - FMT_API int convert(wstring_view s); + FMT_API int convert(basic_string_view s); }; FMT_API void format_windows_error(buffer& out, int error_code, diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 367d7a6b..e9f24ff8 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -435,8 +435,8 @@ FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, } template -FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, - wstring_view sep) { +FMT_CONSTEXPR tuple_arg_join join( + const std::tuple& tuple, basic_string_view sep) { return {tuple, sep}; } @@ -457,12 +457,6 @@ arg_join join(std::initializer_list list, return join(std::begin(list), std::end(list), sep); } -template -arg_join join(std::initializer_list list, - wstring_view sep) { - return join(std::begin(list), std::end(list), sep); -} - FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/include/fmt/wchar.h b/include/fmt/wchar.h index b3049b5f..53c95f37 100644 --- a/include/fmt/wchar.h +++ b/include/fmt/wchar.h @@ -15,6 +15,7 @@ FMT_BEGIN_NAMESPACE FMT_MODULE_EXPORT_BEGIN +using wstring_view = basic_string_view; using wformat_parse_context = basic_format_parse_context; using wformat_context = buffer_context; using wformat_args = basic_format_args; @@ -38,8 +39,31 @@ constexpr auto operator"" _format(const wchar_t* s, size_t n) -> detail::udl_formatter { return {{s, n}}; } + +#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +constexpr detail::udl_arg operator"" _a(const wchar_t* s, size_t) { + return {s}; +} +#endif } // namespace literals +template +arg_join join(It begin, Sentinel end, wstring_view sep) { + return {begin, end, sep}; +} + +template +arg_join, detail::sentinel_t, wchar_t> join( + Range&& range, wstring_view sep) { + return join(std::begin(range), std::end(range), sep); +} + +template +arg_join join(std::initializer_list list, + wstring_view sep) { + return join(std::begin(list), std::end(list), sep); +} + inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { wmemory_buffer buffer; detail::vformat_to(buffer, fmt, args); @@ -61,6 +85,13 @@ template void print(wformat_string fmt, T&&... args) { return vprint(wstring_view(fmt), make_wformat_args(args...)); } +/** + Converts *value* to ``std::wstring`` using the default format for type *T*. + */ +template inline std::wstring to_wstring(const T& value) { + return format(FMT_STRING(L"{}"), value); +} + FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/src/os.cc b/src/os.cc index 279a8a8d..934629d7 100644 --- a/src/os.cc +++ b/src/os.cc @@ -72,14 +72,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; } FMT_BEGIN_NAMESPACE #ifdef _WIN32 -detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) { +detail::utf16_to_utf8::utf16_to_utf8(basic_string_view s) { if (int error_code = convert(s)) { FMT_THROW(windows_error(error_code, "cannot convert string from UTF-16 to UTF-8")); } } -int detail::utf16_to_utf8::convert(wstring_view s) { +int detail::utf16_to_utf8::convert(basic_string_view s) { if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; int s_size = static_cast(s.size()); if (s_size == 0) { @@ -129,8 +129,8 @@ class system_message { } ~system_message() { LocalFree(message_); } explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } - operator wstring_view() const FMT_NOEXCEPT { - return wstring_view(message_, result_); + operator basic_string_view() const FMT_NOEXCEPT { + return basic_string_view(message_, result_); } }; diff --git a/test/core-test.cc b/test/core-test.cc index de1ac225..43fa1d11 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -22,10 +22,6 @@ #include "gmock/gmock.h" -#if defined(FMT_COMPILE_TIME_CHECKS) && FMT_COMPILE_TIME_CHECKS -# include "fmt/format.h" -#endif - using fmt::string_view; using fmt::detail::buffer; @@ -453,11 +449,11 @@ TEST(arg_test, wstring_arg) { wchar_t* str = str_data; const wchar_t* cstr = str; - auto sv = fmt::wstring_view(str); + auto sv = fmt::basic_string_view(str); CHECK_ARG(wchar_t, cstr, str); CHECK_ARG(wchar_t, cstr, cstr); CHECK_ARG(wchar_t, sv, std::wstring(str)); - CHECK_ARG(wchar_t, sv, fmt::wstring_view(str)); + CHECK_ARG(wchar_t, sv, fmt::basic_string_view(str)); } TEST(arg_test, pointer_arg) { diff --git a/test/enforce-checks-test.cc b/test/enforce-checks-test.cc index 103f827e..a2056b22 100644 --- a/test/enforce-checks-test.cc +++ b/test/enforce-checks-test.cc @@ -14,6 +14,7 @@ #include "fmt/locale.h" #include "fmt/ostream.h" #include "fmt/ranges.h" +#include "fmt/wchar.h" // Exercise the API to verify that everything we expect to can compile. void test_format_api() { diff --git a/test/format-test.cc b/test/format-test.cc index 723c87a0..cbed4984 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1437,18 +1437,6 @@ TEST(format_test, format_explicitly_convertible_to_std_string_view) { } #endif -// std::is_constructible is broken in MSVC until version 2015. -#if !FMT_MSC_VER || FMT_MSC_VER >= 1900 -struct explicitly_convertible_to_wstring_view { - explicit operator fmt::wstring_view() const { return L"foo"; } -}; - -TEST(format_test, format_explicitly_convertible_to_wstring_view) { - EXPECT_EQ(L"foo", - fmt::format(L"{}", explicitly_convertible_to_wstring_view())); -} -#endif - namespace fake_qt { class QString { public: @@ -1640,7 +1628,6 @@ TEST(format_test, join) { EXPECT_EQ("(+01.20, +03.40)", fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", "))); - EXPECT_EQ(L"(1, 2, 3)", fmt::format(L"({})", join(v1, v1 + 3, L", "))); EXPECT_EQ("1, 2, 3", fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1)); EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]), @@ -1770,13 +1757,6 @@ TEST(format_test, named_arg_udl) { fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"), fmt::arg("second", "cad"), fmt::arg("third", 99)), udl_a); - auto udl_a_w = - fmt::format(L"{first}{second}{first}{third}", L"first"_a = L"abra", - L"second"_a = L"cad", L"third"_a = 99); - EXPECT_EQ( - fmt::format(L"{first}{second}{first}{third}", fmt::arg(L"first", L"abra"), - fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)), - udl_a_w); } #endif // FMT_USE_USER_DEFINED_LITERALS @@ -1869,8 +1849,6 @@ TEST(format_test, to_string) { EXPECT_EQ("0", fmt::to_string(test_value)); } -TEST(format_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } - TEST(format_test, output_iterators) { std::list out; fmt::format_to(std::back_inserter(out), "{}", 42); @@ -1966,26 +1944,6 @@ TEST(format_test, format_to_n) { EXPECT_EQ("***x", fmt::string_view(buffer, 4)); } -TEST(format_test, wide_format_to_n) { - wchar_t buffer[4]; - buffer[3] = L'x'; - auto result = fmt::format_to_n(buffer, 3, L"{}", 12345); - EXPECT_EQ(5u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); - buffer[0] = L'x'; - buffer[1] = L'x'; - buffer[2] = L'x'; - result = fmt::format_to_n(buffer, 3, L"{}", L'A'); - EXPECT_EQ(1u, result.size); - EXPECT_EQ(buffer + 1, result.out); - EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); - result = fmt::format_to_n(buffer, 3, L"{}{} ", L'B', L'C'); - EXPECT_EQ(3u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); -} - struct test_output_iterator { char* data; diff --git a/test/os-test.cc b/test/os-test.cc index 09115b77..cc23e6b8 100644 --- a/test/os-test.cc +++ b/test/os-test.cc @@ -20,6 +20,7 @@ using fmt::buffered_file; using testing::HasSubstr; +using wstring_view = fmt::basic_string_view; #ifdef _WIN32 @@ -62,9 +63,9 @@ TEST(util_test, utf16_to_utf8_error) { TEST(util_test, utf16_to_utf8_convert) { fmt::detail::utf16_to_utf8 u; - EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1))); + EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(wstring_view(0, 1))); EXPECT_EQ(ERROR_INVALID_PARAMETER, - u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u))); + u.convert(wstring_view(L"foo", INT_MAX + 1u))); } TEST(os_test, format_std_error_code) { @@ -98,8 +99,7 @@ TEST(os_test, format_windows_error) { FORMAT_MESSAGE_IGNORE_INSERTS, 0, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&message), 0, 0); - fmt::detail::utf16_to_utf8 utf8_message( - fmt::wstring_view(message, result - 2)); + fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2)); LocalFree(message); fmt::memory_buffer actual_message; fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test"); @@ -124,8 +124,7 @@ TEST(os_test, format_long_windows_error) { LocalFree(message); return; } - fmt::detail::utf16_to_utf8 utf8_message( - fmt::wstring_view(message, result - 2)); + fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2)); LocalFree(message); fmt::memory_buffer actual_message; fmt::detail::format_windows_error(actual_message, provisioning_not_allowed, diff --git a/test/printf-test.cc b/test/printf-test.cc index f8db99ae..5aacd37f 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -29,7 +29,7 @@ static std::string make_positional(fmt::string_view format) { return s; } -static std::wstring make_positional(fmt::wstring_view format) { +static std::wstring make_positional(fmt::basic_string_view format) { std::wstring s(format.data(), format.size()); s.replace(s.find(L'%'), 1, L"%1$"); return s; @@ -42,7 +42,8 @@ std::string test_sprintf(fmt::string_view format, const Args&... args) { return fmt::sprintf(format, args...); } template -std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) { +std::wstring test_sprintf(fmt::basic_string_view format, + const Args&... args) { return fmt::sprintf(format, args...); } diff --git a/test/wchar-test.cc b/test/wchar-test.cc index 0a472420..38804723 100644 --- a/test/wchar-test.cc +++ b/test/wchar-test.cc @@ -9,7 +9,19 @@ #include "gtest/gtest.h" -TEST(format_test, vformat_to) { +// std::is_constructible is broken in MSVC until version 2015. +#if !FMT_MSC_VER || FMT_MSC_VER >= 1900 +struct explicitly_convertible_to_wstring_view { + explicit operator fmt::wstring_view() const { return L"foo"; } +}; + +TEST(wchar_test, format_explicitly_convertible_to_wstring_view) { + EXPECT_EQ(L"foo", + fmt::format(L"{}", explicitly_convertible_to_wstring_view())); +} +#endif + +TEST(wchar_test, vformat_to) { using wcontext = fmt::wformat_context; fmt::basic_format_arg warg = fmt::detail::make_arg(42); auto wargs = fmt::basic_format_args(&warg, 1); @@ -21,14 +33,52 @@ TEST(format_test, vformat_to) { EXPECT_EQ(L"42", w); } +TEST(format_test, wide_format_to_n) { + wchar_t buffer[4]; + buffer[3] = L'x'; + auto result = fmt::format_to_n(buffer, 3, L"{}", 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); + buffer[0] = L'x'; + buffer[1] = L'x'; + buffer[2] = L'x'; + result = fmt::format_to_n(buffer, 3, L"{}", L'A'); + EXPECT_EQ(1u, result.size); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); + result = fmt::format_to_n(buffer, 3, L"{}{} ", L'B', L'C'); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); +} + #if FMT_USE_USER_DEFINED_LITERALS -TEST(format_test, format_udl) { +TEST(wchar_test, format_udl) { using namespace fmt::literals; EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1)); } -#endif + +TEST(wchar_test, named_arg_udl) { + using namespace fmt::literals; + auto udl_a = + fmt::format(L"{first}{second}{first}{third}", L"first"_a = L"abra", + L"second"_a = L"cad", L"third"_a = 99); + EXPECT_EQ( + fmt::format(L"{first}{second}{first}{third}", fmt::arg(L"first", L"abra"), + fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)), + udl_a); +} +#endif // FMT_USE_USER_DEFINED_LITERALS TEST(wchar_test, print) { // Check that the wide print overload compiles. if (fmt::detail::const_check(false)) fmt::print(L"test"); } + +TEST(wchar_test, join) { + int v[3] = {1, 2, 3}; + EXPECT_EQ(fmt::format(L"({})", fmt::join(v, v + 3, L", ")), L"(1, 2, 3)"); +} + +TEST(wchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }