From ff29e7a6d096495258f96ac674996499e81a9d2a Mon Sep 17 00:00:00 2001 From: "S. B. Tam" Date: Tue, 23 Aug 2022 05:13:08 +0800 Subject: [PATCH] Implement P2419R2 Clarify Handling Of Encodings In Localized Formatting Of `chrono` Types (#2977) Co-authored-by: Stephan T. Lavavej --- stl/inc/chrono | 96 ++++++++++++++++--- stl/inc/filesystem | 48 ---------- stl/inc/yvals_core.h | 1 + .../test.cpp | 1 + 4 files changed, 87 insertions(+), 59 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index c6d7e6fd1..de1955860 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -10,6 +10,12 @@ #if _STL_COMPILER_PREPROCESSOR #include <__msvc_chrono.hpp> +#if _HAS_CXX17 +#include +#include +#include +#endif // _HAS_CXX17 + #if _HAS_CXX20 #include <__msvc_tzdb.hpp> #include @@ -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 +// 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(_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 +_NODISCARD basic_string _Convert_wide_to_narrow( + const __std_code_page _Code_page, const wstring_view _Input, const _Alloc& _Al) { + basic_string _Output(_Al); + + if (!_Input.empty()) { + if (_Input.size() > static_cast(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(_Input.size()), nullptr, 0)); + + _Output.resize(static_cast(_Len)); + + const auto _Data_as_char = reinterpret_cast(_Output.data()); + + (void) _Check_convert_result(__std_fs_convert_wide_to_narrow( + _Code_page, _Input.data(), static_cast(_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 + _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 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 _Wspec{._Modifier = _Spec._Modifier, ._Type = _Spec._Type}; + _Wstream << _STD put_time(&_Time, _Fmt_string(_Wspec, _Fmt_str)); + + _Stream << _Convert_wide_to_narrow>( + __std_code_page::_Utf8, _Wstream.view(), allocator{}); + + 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{}; }; diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 6399c49fa..6dccb9a89 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -19,7 +19,6 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with #include #include #include -#include #include #include #include @@ -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 - // 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(_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 - _NODISCARD basic_string _Convert_wide_to_narrow( - const __std_code_page _Code_page, const wstring_view _Input, const _Alloc& _Al) { - basic_string _Output(_Al); - - if (!_Input.empty()) { - if (_Input.size() > static_cast(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(_Input.size()), nullptr, 0)); - - _Output.resize(static_cast(_Len)); - - const auto _Data_as_char = reinterpret_cast(_Output.data()); - - (void) _Check_convert_result(__std_fs_convert_wide_to_narrow( - _Code_page, _Input.data(), static_cast(_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 diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 0b4a5d5d1..68ad8fa7c 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -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 Should Be A Random-Access Iterator diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index b97c7c823..1b4e719d1 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -1119,6 +1119,7 @@ void test() { #if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING test_locale(); + test_locale(); assert(setlocale(LC_ALL, ".UTF-8") != nullptr); test_locale(); #endif // !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING