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
using wstring_view = basic_string_view<wchar_t>;
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : 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<wchar_t>() 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<It, Sentinel, char> join(It begin, Sentinel end, string_view 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
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);
}
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
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));
}
/**
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>
std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
auto size = buf.size();
@ -2873,9 +2853,6 @@ operator""_a() {
constexpr detail::udl_arg<char> operator"" _a(const char* s, size_t) {
return {s};
}
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s};
}
# endif
} // namespace literals

View File

@ -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<wchar_t> 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<wchar_t> s);
};
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>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(
const std::tuple<T...>& tuple, basic_string_view<wchar_t> 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);
}
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_END_NAMESPACE

View File

@ -15,6 +15,7 @@
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
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> {
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
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) {
wmemory_buffer buffer;
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...));
}
/**
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_END_NAMESPACE

View File

@ -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<wchar_t> 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<wchar_t> s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(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<wchar_t>() const FMT_NOEXCEPT {
return basic_string_view<wchar_t>(message_, result_);
}
};

View File

@ -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<wchar_t>(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<wchar_t>(str));
}
TEST(arg_test, pointer_arg) {

View File

@ -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() {

View File

@ -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<char> 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;

View File

@ -20,6 +20,7 @@
using fmt::buffered_file;
using testing::HasSubstr;
using wstring_view = fmt::basic_string_view<wchar_t>;
#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<LPWSTR>(&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,

View File

@ -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<wchar_t> 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 <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...);
}

View File

@ -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<wcontext> warg = fmt::detail::make_arg<wcontext>(42);
auto wargs = fmt::basic_format_args<wcontext>(&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)); }