diff --git a/stl/inc/chrono b/stl/inc/chrono index 8405b26be5..b19aa83f7c 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -10,6 +10,7 @@ #if _STL_COMPILER_PREPROCESSOR #include #include +#include #include #include #include @@ -617,6 +618,88 @@ namespace chrono { }; using high_resolution_clock = steady_clock; + +#if _HAS_CXX20 + // [time.duration.io] + +#define _IF_PERIOD_RETURN_SUFFIX_ELSE(_TYPE, _SUFFIX) \ + if constexpr (is_same_v<_Period, _TYPE>) { \ + if constexpr (is_same_v<_CharT, char>) { \ + return _SUFFIX; \ + } else { \ + return L##_SUFFIX; \ + } \ + } else + + template + _NODISCARD constexpr const _CharT* _Get_literal_unit_suffix() { + _IF_PERIOD_RETURN_SUFFIX_ELSE(atto, "as") + _IF_PERIOD_RETURN_SUFFIX_ELSE(femto, "fs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(pico, "ps") + _IF_PERIOD_RETURN_SUFFIX_ELSE(nano, "ns") + _IF_PERIOD_RETURN_SUFFIX_ELSE(micro, "us") + _IF_PERIOD_RETURN_SUFFIX_ELSE(milli, "ms") + _IF_PERIOD_RETURN_SUFFIX_ELSE(centi, "cs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(deci, "ds") + _IF_PERIOD_RETURN_SUFFIX_ELSE(seconds::period, "s") + _IF_PERIOD_RETURN_SUFFIX_ELSE(deca, "das") + _IF_PERIOD_RETURN_SUFFIX_ELSE(hecto, "hs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(kilo, "ks") + _IF_PERIOD_RETURN_SUFFIX_ELSE(mega, "Ms") + _IF_PERIOD_RETURN_SUFFIX_ELSE(giga, "Gs") + _IF_PERIOD_RETURN_SUFFIX_ELSE(tera, "Ts") + _IF_PERIOD_RETURN_SUFFIX_ELSE(peta, "Ps") + _IF_PERIOD_RETURN_SUFFIX_ELSE(exa, "Es") + _IF_PERIOD_RETURN_SUFFIX_ELSE(minutes::period, "min") + _IF_PERIOD_RETURN_SUFFIX_ELSE(hours::period, "h") + _IF_PERIOD_RETURN_SUFFIX_ELSE(ratio<86400>, "d") + + { + return nullptr; + } + } + +#undef _IF_PERIOD_RETURN_SUFFIX_ELSE + + template + _NODISCARD _CharT* _Get_general_unit_suffix(_CharT* _Rnext, const intmax_t _Num, const intmax_t _Den) { + // Returns the head pointer of the string, built in reverse. + _STL_INTERNAL_CHECK(_Num > 0 && _Den > 0); + *--_Rnext = '\0'; + *--_Rnext = 's'; + *--_Rnext = ']'; + if (_Den != 1) { + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Den)); + *--_Rnext = '/'; + } + + _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Num)); + *--_Rnext = '['; + return _Rnext; + } + + template + basic_ostream<_CharT, _Traits>& operator<<( + basic_ostream<_CharT, _Traits>& _Os, const duration<_Rep, _Period>& _Dur) { + basic_ostringstream<_CharT, _Traits> _Sstr; + _Sstr.flags(_Os.flags()); + _Sstr.imbue(_Os.getloc()); + _Sstr.precision(_Os.precision()); + _Sstr << _Dur.count(); + + constexpr auto _Suffix = _Get_literal_unit_suffix<_CharT, _Period>(); + if constexpr (_Suffix == nullptr) { + _CharT _Buffer[2 * (numeric_limits::digits10 + 1) + 5] = {}; // 2 numbers + "[/]s\0" + const _CharT* const _Begin = + _Get_general_unit_suffix<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); + _Sstr << _Begin; + } else { + _Sstr << _Suffix; + } + + return _Os << _Sstr.str(); + } +#endif // _HAS_CXX20 } // namespace chrono // HELPERS diff --git a/stl/inc/string b/stl/inc/string index 09802c114e..72d3cebb2d 100644 --- a/stl/inc/string +++ b/stl/inc/string @@ -448,7 +448,7 @@ inline long double stold(const wstring& _Str, size_t* _Idx = nullptr) { // conve return _Ans; } -// HELPERS FOR to_string AND to_wstring +// HELPERS FOR to_string AND to_wstring AND operator<<(duration) template _Elem* _UIntegral_to_buff(_Elem* _RNext, _UTy _UVal) { // format _UVal into buffer *ending at* _RNext static_assert(is_unsigned_v<_UTy>, "_UTy must be unsigned"); diff --git a/tests/std/test.lst b/tests/std/test.lst index adf5a62423..4140f5fd7c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -222,6 +222,7 @@ tests\P0220R1_searchers tests\P0220R1_string_view tests\P0325R4_to_array tests\P0339R6_polymorphic_allocator +tests\P0355R7_calendars_and_time_zones_io tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper tests\P0414R2_shared_ptr_for_arrays diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_io/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp new file mode 100644 index 0000000000..fefbba6929 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using chrono::duration; + +template +bool test_duration_basic_out(const duration& d, const CharT* expected) { + basic_ostringstream ss; + + ss << d; + return ss.str() == expected; +} + +#define WIDEN(TYPE, STR) get(make_pair(STR, L##STR)); + +template +bool test_duration_locale_out() { + basic_stringstream ss; + const duration d{0.140625}; + ss.precision(3); + ss << d; + ss.setf(ios_base::scientific, ios_base::floatfield); + ss << ' ' << d; + + basic_string expected = WIDEN(CharT, "0.141s 1.406e-01s"); + +#ifdef _DEBUG +#define DEFAULT_IDL_SETTING 2 +#else +#define DEFAULT_IDL_SETTING 0 +#endif + +#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + // When linking dynamically, user-defined facets are incompatible with non-default _ITERATOR_DEBUG_LEVEL settings. + struct comma : numpunct { + CharT do_decimal_point() const { + return ','; + } + }; + + ss.imbue(locale(ss.getloc(), new comma)); + ss << ' ' << d; + expected += WIDEN(CharT, " 1,406e-01s"); +#endif // !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + + return ss.str() == expected; +} + +#undef WIDEN + +void test_duration_output() { + using LongRatio = ratio; + assert(test_duration_basic_out(duration{1}, "1as")); + assert(test_duration_basic_out(duration{2}, "2fs")); + assert(test_duration_basic_out(duration{3}, "3ps")); + assert(test_duration_basic_out(duration{42}, "42ns")); + assert(test_duration_basic_out(duration{42}, "42us")); + assert(test_duration_basic_out(duration{42}, "42ms")); + assert(test_duration_basic_out(duration{42}, "42cs")); + assert(test_duration_basic_out(duration{42}, "42ds")); + assert(test_duration_basic_out(duration>{42}, "42s")); + assert(test_duration_basic_out(duration{42}, "42das")); + assert(test_duration_basic_out(duration{42}, "42hs")); + assert(test_duration_basic_out(duration{42}, "42ks")); + assert(test_duration_basic_out(duration{42}, "42Ms")); + assert(test_duration_basic_out(duration{42}, "42Gs")); + assert(test_duration_basic_out(duration{42}, "42Ts")); + assert(test_duration_basic_out(duration{42}, "42Ps")); + assert(test_duration_basic_out(duration{42}, "42Es")); + assert(test_duration_basic_out(duration>{42}, "42min")); + assert(test_duration_basic_out(duration>{42}, "42h")); + assert(test_duration_basic_out(duration>{42}, "42d")); + + assert(test_duration_basic_out(duration>{24}, "24[2]s")); + assert(test_duration_basic_out(duration>{24}, "24[1/2]s")); + assert(test_duration_basic_out(duration>{24}, "24[22/7]s")); + assert(test_duration_basic_out(duration{24}, "24[9223372036854775806/9223372036854775807]s")); + + assert(test_duration_basic_out(duration{0.140625}, "0.140625s")); + assert(test_duration_locale_out()); + + assert(test_duration_basic_out(duration{1}, L"1as")); + assert(test_duration_basic_out(duration{2}, L"2fs")); + assert(test_duration_basic_out(duration{3}, L"3ps")); + assert(test_duration_basic_out(duration{42}, L"42ns")); + assert(test_duration_basic_out(duration{42}, L"42us")); + assert(test_duration_basic_out(duration{42}, "42ms")); + assert(test_duration_basic_out(duration{42}, L"42cs")); + assert(test_duration_basic_out(duration{42}, L"42ds")); + assert(test_duration_basic_out(duration>{42}, L"42s")); + assert(test_duration_basic_out(duration{42}, L"42das")); + assert(test_duration_basic_out(duration{42}, L"42hs")); + assert(test_duration_basic_out(duration{42}, L"42ks")); + assert(test_duration_basic_out(duration{42}, L"42Ms")); + assert(test_duration_basic_out(duration{42}, L"42Gs")); + assert(test_duration_basic_out(duration{42}, L"42Ts")); + assert(test_duration_basic_out(duration{42}, L"42Ps")); + assert(test_duration_basic_out(duration{42}, L"42Es")); + assert(test_duration_basic_out(duration>{42}, L"42min")); + assert(test_duration_basic_out(duration>{42}, L"42h")); + assert(test_duration_basic_out(duration>{42}, L"42d")); + + assert(test_duration_basic_out(duration>{24}, L"24[2]s")); + assert(test_duration_basic_out(duration>{24}, L"24[1/2]s")); + assert(test_duration_basic_out(duration>{24}, L"24[22/7]s")); + assert(test_duration_basic_out(duration{24}, L"24[9223372036854775806/9223372036854775807]s")); + + assert(test_duration_basic_out(duration{0.140625}, L"0.140625s")); + assert(test_duration_locale_out()); +} + +int main() { + test_duration_output(); + return 0; +}