Implement P2419R2 Clarify Handling Of Encodings In Localized Formatting Of `chrono` Types (#2977)

Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
S. B. Tam 2022-08-23 05:13:08 +08:00 коммит произвёл GitHub
Родитель 2ae879b73c
Коммит ff29e7a6d0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 87 добавлений и 59 удалений

Просмотреть файл

@ -10,6 +10,12 @@
#if _STL_COMPILER_PREPROCESSOR
#include <__msvc_chrono.hpp>
#if _HAS_CXX17
#include <system_error>
#include <xfilesystem_abi.h>
#include <xstring>
#endif // _HAS_CXX17
#if _HAS_CXX20
#include <__msvc_tzdb.hpp>
#include <algorithm>
@ -45,6 +51,55 @@ _STL_DISABLE_CLANG_WARNINGS
#undef new
_STD_BEGIN
#if _HAS_CXX17
// We would really love to use the proper way of building error_code by specializing
// is_error_code_enum and make_error_code for __std_win_error, but because:
// 1. We would like to keep the definition of __std_win_error in xfilesystem_abi.h
// 2. and xfilesystem_abi.h cannot include <system_error>
// 3. and specialization of is_error_code_enum and overload of make_error_code
// need to be kept together with the enum (see limerick in N4810 [temp.expl.spec]/7)
// we resort to using this _Make_ec helper.
_NODISCARD inline error_code _Make_ec(__std_win_error _Errno) noexcept { // make an error_code
return {static_cast<int>(_Errno), _STD system_category()};
}
[[noreturn]] inline void _Throw_system_error_from_std_win_error(const __std_win_error _Errno) {
_THROW(system_error{_Make_ec(_Errno)});
}
_NODISCARD inline int _Check_convert_result(const __std_fs_convert_result _Result) {
if (_Result._Err != __std_win_error::_Success) {
_Throw_system_error_from_std_win_error(_Result._Err);
}
return _Result._Len;
}
template <class _Traits, class _Alloc>
_NODISCARD basic_string<typename _Traits::char_type, _Traits, _Alloc> _Convert_wide_to_narrow(
const __std_code_page _Code_page, const wstring_view _Input, const _Alloc& _Al) {
basic_string<typename _Traits::char_type, _Traits, _Alloc> _Output(_Al);
if (!_Input.empty()) {
if (_Input.size() > static_cast<size_t>(INT_MAX)) {
_Throw_system_error(errc::invalid_argument);
}
const int _Len = _Check_convert_result(
__std_fs_convert_wide_to_narrow(_Code_page, _Input.data(), static_cast<int>(_Input.size()), nullptr, 0));
_Output.resize(static_cast<size_t>(_Len));
const auto _Data_as_char = reinterpret_cast<char*>(_Output.data());
(void) _Check_convert_result(__std_fs_convert_wide_to_narrow(
_Code_page, _Input.data(), static_cast<int>(_Input.size()), _Data_as_char, _Len));
}
return _Output;
}
#endif // _HAS_CXX17
#if _HAS_CXX20
namespace chrono {
// [time.duration.io]
@ -5189,6 +5244,18 @@ namespace chrono {
return _Os << _Local_time_format_t<_Duration>{_Val.get_local_time(), &_Info.abbrev};
}
template <class _CharT>
_NODISCARD const _CharT* _Fmt_string(const _Chrono_spec<_CharT>& _Spec, _CharT (&_Fmt_str)[4]) {
size_t _Next_idx = 0;
_Fmt_str[_Next_idx++] = _CharT{'%'};
if (_Spec._Modifier != '\0') {
_Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Modifier);
}
_Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Type);
_Fmt_str[_Next_idx] = _CharT{'\0'};
return _Fmt_str;
}
template <class _CharT>
struct _Chrono_formatter {
_Chrono_formatter() = default;
@ -5351,6 +5418,24 @@ namespace chrono {
_Validate_specifiers(_Spec, _Val);
#if defined(_MSVC_EXECUTION_CHARACTER_SET) && _MSVC_EXECUTION_CHARACTER_SET == 65001 // TRANSITION, VSO-1468747 (EDG)
if constexpr (is_same_v<_CharT, char>) {
if (_Specs._Localized) {
wostringstream _Wstream;
_Wstream.imbue(_FormatCtx.locale());
wchar_t _Fmt_str[4];
_Chrono_spec<wchar_t> _Wspec{._Modifier = _Spec._Modifier, ._Type = _Spec._Type};
_Wstream << _STD put_time<wchar_t>(&_Time, _Fmt_string(_Wspec, _Fmt_str));
_Stream << _Convert_wide_to_narrow<char_traits<char>>(
__std_code_page::_Utf8, _Wstream.view(), allocator<char>{});
continue;
}
}
#endif // defined(_MSVC_EXECUTION_CHARACTER_SET) && _MSVC_EXECUTION_CHARACTER_SET == 65001
_CharT _Fmt_str[4];
_Stream << _STD put_time<_CharT>(&_Time, _Fmt_string(_Spec, _Fmt_str));
}
@ -5675,17 +5760,6 @@ namespace chrono {
}
}
_NODISCARD static const _CharT* _Fmt_string(const _Chrono_spec<_CharT>& _Spec, _CharT (&_Fmt_str)[4]) {
size_t _Next_idx = 0;
_Fmt_str[_Next_idx++] = _CharT{'%'};
if (_Spec._Modifier != '\0') {
_Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Modifier);
}
_Fmt_str[_Next_idx++] = static_cast<_CharT>(_Spec._Type);
_Fmt_str[_Next_idx] = _CharT{'\0'};
return _Fmt_str;
}
_Chrono_format_specs<_CharT> _Specs{};
basic_string_view<_CharT> _Time_zone_abbreviation{};
};

Просмотреть файл

@ -19,7 +19,6 @@ _EMIT_STL_WARNING(STL4038, "The contents of <filesystem> are available only with
#include <list>
#include <locale>
#include <memory>
#include <string_view>
#include <system_error>
#include <utility>
#include <vector>
@ -39,29 +38,6 @@ _STL_DISABLE_CLANG_WARNINGS
_STD_BEGIN
namespace filesystem {
// We would really love to use the proper way of building error_code by specializing
// is_error_code_enum and make_error_code for __std_win_error, but because:
// 1. We would like to keep the definition of __std_win_error in xfilesystem_abi.h
// 2. and xfilesystem_abi.h cannot include <system_error>
// 3. and specialization of is_error_code_enum and overload of make_error_code
// need to be kept together with the enum (see limerick in N4810 [temp.expl.spec]/7)
// we resort to using this _Make_ec helper.
_NODISCARD inline error_code _Make_ec(__std_win_error _Errno) noexcept { // make an error_code
return {static_cast<int>(_Errno), _STD system_category()};
}
[[noreturn]] inline void _Throw_system_error_from_std_win_error(const __std_win_error _Errno) {
_THROW(system_error{_Make_ec(_Errno)});
}
_NODISCARD inline int _Check_convert_result(const __std_fs_convert_result _Result) {
if (_Result._Err != __std_win_error::_Success) {
_Throw_system_error_from_std_win_error(_Result._Err);
}
return _Result._Len;
}
_NODISCARD inline wstring _Convert_narrow_to_wide(const __std_code_page _Code_page, const string_view _Input) {
wstring _Output;
@ -82,30 +58,6 @@ namespace filesystem {
return _Output;
}
template <class _Traits, class _Alloc>
_NODISCARD basic_string<typename _Traits::char_type, _Traits, _Alloc> _Convert_wide_to_narrow(
const __std_code_page _Code_page, const wstring_view _Input, const _Alloc& _Al) {
basic_string<typename _Traits::char_type, _Traits, _Alloc> _Output(_Al);
if (!_Input.empty()) {
if (_Input.size() > static_cast<size_t>(INT_MAX)) {
_Throw_system_error(errc::invalid_argument);
}
const int _Len = _Check_convert_result(__std_fs_convert_wide_to_narrow(
_Code_page, _Input.data(), static_cast<int>(_Input.size()), nullptr, 0));
_Output.resize(static_cast<size_t>(_Len));
const auto _Data_as_char = reinterpret_cast<char*>(_Output.data());
(void) _Check_convert_result(__std_fs_convert_wide_to_narrow(
_Code_page, _Input.data(), static_cast<int>(_Input.size()), _Data_as_char, _Len));
}
return _Output;
}
// More lenient version of _Convert_wide_to_narrow: Instead of failing on non-representable characters,
// replace them with a replacement character.
template <class _Traits, class _Alloc>

Просмотреть файл

@ -280,6 +280,7 @@
// P2408R5 Ranges Iterators As Inputs To Non-Ranges Algorithms
// P2415R2 What Is A view?
// P2418R2 Add Support For std::generator-like Types To std::format
// P2419R2 Clarify Handling Of Encodings In Localized Formatting Of chrono Types
// P2432R1 Fix istream_view
// P2520R0 move_iterator<T*> Should Be A Random-Access Iterator

Просмотреть файл

@ -1119,6 +1119,7 @@ void test() {
#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING
test_locale<wchar_t>();
test_locale<char>();
assert(setlocale(LC_ALL, ".UTF-8") != nullptr);
test_locale<char>();
#endif // !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING