Skip to content

Commit

Permalink
Implement P2419R2 Clarify Handling Of Encodings In Localized Formatti…
Browse files Browse the repository at this point in the history
…ng Of `chrono` Types (#2977)

Co-authored-by: Stephan T. Lavavej <[email protected]>
  • Loading branch information
cpplearner and StephanTLavavej authored Aug 22, 2022
1 parent 2ae879b commit ff29e7a
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 59 deletions.
96 changes: 85 additions & 11 deletions stl/inc/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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{};
};
Expand Down
48 changes: 0 additions & 48 deletions stl/inc/filesystem
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;

Expand All @@ -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>
Expand Down
1 change: 1 addition & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ff29e7a

Please sign in to comment.