More wchar_t-specific API to wchar.h

This commit is contained in:
Victor Zverovich
2021-05-21 17:37:07 -07:00
parent 6326c18906
commit 34b8acaef7
11 changed files with 104 additions and 97 deletions

View File

@ -629,8 +629,6 @@ void iterator_buffer<OutputIt, T, Traits>::flush() {
FMT_MODULE_EXPORT_BEGIN FMT_MODULE_EXPORT_BEGIN
using wstring_view = basic_string_view<wchar_t>;
template <> struct is_char<wchar_t> : std::true_type {}; template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {}; template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {}; template <> struct is_char<char16_t> : std::true_type {};
@ -1129,7 +1127,7 @@ class utf8_to_utf16 {
public: public:
FMT_API explicit utf8_to_utf16(string_view s); FMT_API explicit utf8_to_utf16(string_view s);
operator wstring_view() const { return {&buffer_[0], size()}; } operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
size_t size() const { return buffer_.size() - 1; } size_t size() const { return buffer_.size() - 1; }
const wchar_t* c_str() const { return &buffer_[0]; } const wchar_t* c_str() const { return &buffer_[0]; }
std::wstring str() const { return {&buffer_[0], size()}; } std::wstring str() const { return {&buffer_[0], size()}; }
@ -2489,11 +2487,6 @@ arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
return {begin, end, sep}; return {begin, end, sep};
} }
template <typename It, typename Sentinel>
arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
return {begin, end, sep};
}
/** /**
\rst \rst
Returns an object that formats `range` with elements separated by `sep`. Returns an object that formats `range` with elements separated by `sep`.
@ -2516,12 +2509,6 @@ arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, char> join(
return join(std::begin(range), std::end(range), sep); return join(std::begin(range), std::end(range), sep);
} }
template <typename Range>
arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t> join(
Range&& range, wstring_view sep) {
return join(std::begin(range), std::end(range), sep);
}
/** /**
\rst \rst
Converts *value* to ``std::string`` using the default format for type *T*. 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<char>(begin, value)); return std::string(begin, detail::write<char>(begin, value));
} }
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename T> inline std::wstring to_wstring(const T& value) {
return format(FMT_STRING(L"{}"), value);
}
template <typename Char, size_t SIZE> template <typename Char, size_t SIZE>
std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) { std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
auto size = buf.size(); auto size = buf.size();
@ -2873,9 +2853,6 @@ operator""_a() {
constexpr detail::udl_arg<char> operator"" _a(const char* s, size_t) { constexpr detail::udl_arg<char> operator"" _a(const char* s, size_t) {
return {s}; return {s};
} }
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s};
}
# endif # endif
} // namespace literals } // namespace literals

View File

@ -148,7 +148,7 @@ class utf16_to_utf8 {
public: public:
utf16_to_utf8() {} utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s); FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); } operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; } size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; } 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 // Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw // throwing exception on conversion error. This method may still throw
// in case of memory allocation error. // in case of memory allocation error.
FMT_API int convert(wstring_view s); FMT_API int convert(basic_string_view<wchar_t> s);
}; };
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,

View File

@ -435,8 +435,8 @@ FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
} }
template <typename... T> template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(
wstring_view sep) { const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep) {
return {tuple, sep}; return {tuple, sep};
} }
@ -457,12 +457,6 @@ arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -15,6 +15,7 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_MODULE_EXPORT_BEGIN
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>; using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>; using wformat_context = buffer_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>; using wformat_args = basic_format_args<wformat_context>;
@ -38,8 +39,31 @@ constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> { -> detail::udl_formatter<wchar_t> {
return {{s, n}}; return {{s, n}};
} }
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s};
}
#endif
} // namespace literals } // namespace literals
template <typename It, typename Sentinel>
arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
return {begin, end, sep};
}
template <typename Range>
arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t> join(
Range&& range, wstring_view sep) {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> 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) { inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer; wmemory_buffer buffer;
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buffer, fmt, args);
@ -61,6 +85,13 @@ template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), make_wformat_args(args...)); return vprint(wstring_view(fmt), make_wformat_args(args...));
} }
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename T> inline std::wstring to_wstring(const T& value) {
return format(FMT_STRING(L"{}"), value);
}
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -72,14 +72,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#ifdef _WIN32 #ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) { detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
if (int error_code = convert(s)) { if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code, FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8")); "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<wchar_t> s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size()); int s_size = static_cast<int>(s.size());
if (s_size == 0) { if (s_size == 0) {
@ -129,8 +129,8 @@ class system_message {
} }
~system_message() { LocalFree(message_); } ~system_message() { LocalFree(message_); }
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; }
operator wstring_view() const FMT_NOEXCEPT { operator basic_string_view<wchar_t>() const FMT_NOEXCEPT {
return wstring_view(message_, result_); return basic_string_view<wchar_t>(message_, result_);
} }
}; };

View File

@ -22,10 +22,6 @@
#include "gmock/gmock.h" #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::string_view;
using fmt::detail::buffer; using fmt::detail::buffer;
@ -453,11 +449,11 @@ TEST(arg_test, wstring_arg) {
wchar_t* str = str_data; wchar_t* str = str_data;
const wchar_t* cstr = str; const wchar_t* cstr = str;
auto sv = fmt::wstring_view(str); auto sv = fmt::basic_string_view<wchar_t>(str);
CHECK_ARG(wchar_t, cstr, str); CHECK_ARG(wchar_t, cstr, str);
CHECK_ARG(wchar_t, cstr, cstr); CHECK_ARG(wchar_t, cstr, cstr);
CHECK_ARG(wchar_t, sv, std::wstring(str)); 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<wchar_t>(str));
} }
TEST(arg_test, pointer_arg) { TEST(arg_test, pointer_arg) {

View File

@ -14,6 +14,7 @@
#include "fmt/locale.h" #include "fmt/locale.h"
#include "fmt/ostream.h" #include "fmt/ostream.h"
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include "fmt/wchar.h"
// Exercise the API to verify that everything we expect to can compile. // Exercise the API to verify that everything we expect to can compile.
void test_format_api() { void test_format_api() {

View File

@ -1437,18 +1437,6 @@ TEST(format_test, format_explicitly_convertible_to_std_string_view) {
} }
#endif #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 { namespace fake_qt {
class QString { class QString {
public: public:
@ -1640,7 +1628,6 @@ TEST(format_test, join) {
EXPECT_EQ("(+01.20, +03.40)", EXPECT_EQ("(+01.20, +03.40)",
fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", "))); 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("1, 2, 3", fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1));
EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[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::format("{first}{second}{first}{third}", fmt::arg("first", "abra"),
fmt::arg("second", "cad"), fmt::arg("third", 99)), fmt::arg("second", "cad"), fmt::arg("third", 99)),
udl_a); 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 #endif // FMT_USE_USER_DEFINED_LITERALS
@ -1869,8 +1849,6 @@ TEST(format_test, to_string) {
EXPECT_EQ("0", fmt::to_string(test_value)); 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) { TEST(format_test, output_iterators) {
std::list<char> out; std::list<char> out;
fmt::format_to(std::back_inserter(out), "{}", 42); 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)); 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 { struct test_output_iterator {
char* data; char* data;

View File

@ -20,6 +20,7 @@
using fmt::buffered_file; using fmt::buffered_file;
using testing::HasSubstr; using testing::HasSubstr;
using wstring_view = fmt::basic_string_view<wchar_t>;
#ifdef _WIN32 #ifdef _WIN32
@ -62,9 +63,9 @@ TEST(util_test, utf16_to_utf8_error) {
TEST(util_test, utf16_to_utf8_convert) { TEST(util_test, utf16_to_utf8_convert) {
fmt::detail::utf16_to_utf8 u; 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, 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) { TEST(os_test, format_std_error_code) {
@ -98,8 +99,7 @@ TEST(os_test, format_windows_error) {
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_IGNORE_INSERTS,
0, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 0, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0); reinterpret_cast<LPWSTR>(&message), 0, 0);
fmt::detail::utf16_to_utf8 utf8_message( fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
fmt::wstring_view(message, result - 2));
LocalFree(message); LocalFree(message);
fmt::memory_buffer actual_message; fmt::memory_buffer actual_message;
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test"); fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
@ -124,8 +124,7 @@ TEST(os_test, format_long_windows_error) {
LocalFree(message); LocalFree(message);
return; return;
} }
fmt::detail::utf16_to_utf8 utf8_message( fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
fmt::wstring_view(message, result - 2));
LocalFree(message); LocalFree(message);
fmt::memory_buffer actual_message; fmt::memory_buffer actual_message;
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed, fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,

View File

@ -29,7 +29,7 @@ static std::string make_positional(fmt::string_view format) {
return s; return s;
} }
static std::wstring make_positional(fmt::wstring_view format) { static std::wstring make_positional(fmt::basic_string_view<wchar_t> format) {
std::wstring s(format.data(), format.size()); std::wstring s(format.data(), format.size());
s.replace(s.find(L'%'), 1, L"%1$"); s.replace(s.find(L'%'), 1, L"%1$");
return s; return s;
@ -42,7 +42,8 @@ std::string test_sprintf(fmt::string_view format, const Args&... args) {
return fmt::sprintf(format, args...); return fmt::sprintf(format, args...);
} }
template <typename... Args> template <typename... Args>
std::wstring test_sprintf(fmt::wstring_view format, const Args&... args) { std::wstring test_sprintf(fmt::basic_string_view<wchar_t> format,
const Args&... args) {
return fmt::sprintf(format, args...); return fmt::sprintf(format, args...);
} }

View File

@ -9,7 +9,19 @@
#include "gtest/gtest.h" #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; using wcontext = fmt::wformat_context;
fmt::basic_format_arg<wcontext> warg = fmt::detail::make_arg<wcontext>(42); fmt::basic_format_arg<wcontext> warg = fmt::detail::make_arg<wcontext>(42);
auto wargs = fmt::basic_format_args<wcontext>(&warg, 1); auto wargs = fmt::basic_format_args<wcontext>(&warg, 1);
@ -21,14 +33,52 @@ TEST(format_test, vformat_to) {
EXPECT_EQ(L"42", w); 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 #if FMT_USE_USER_DEFINED_LITERALS
TEST(format_test, format_udl) { TEST(wchar_test, format_udl) {
using namespace fmt::literals; using namespace fmt::literals;
EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1)); 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) { TEST(wchar_test, print) {
// Check that the wide print overload compiles. // Check that the wide print overload compiles.
if (fmt::detail::const_check(false)) fmt::print(L"test"); 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)); }